diff --git a/README.md b/README.md index 7e0e467..dc64e39 100644 --- a/README.md +++ b/README.md @@ -32,3 +32,8 @@ This holds all of our best practices. ## Project Practices 1. [Core Project Presentations](projects/core_presentation.md) + +## Encouraged Design & Code Patterns + +1. [Three Levels of Thinking](patterns/three_levels_of_thinking.md) +1. [Complete Constructor](patterns/complete_constructor.md) diff --git a/patterns/complete_constructor.md b/patterns/complete_constructor.md new file mode 100644 index 0000000..99d8f1a --- /dev/null +++ b/patterns/complete_constructor.md @@ -0,0 +1,125 @@ +# Complete Constructor + +_NOTE: Some of the phrases used here come from "Smalltalk Best Practice Patterns" by Kent Beck, *Constructor Method*_ + +In just about every language that is Object-Oriented (or allows/encourages the use of Object-Oriented techniques), the concept of _instance creation_ is present. + +## Assumption(s) + +* You have created a class/prototype with an _Intention Revealing Name_ +* Programmers who want to use the class will ask, "What does it take to create an instance?" +* There is an interface to instances of your class with other _Intention Revealing Names_ + +## Discussion + +* Single responsibility principle says one method does one thing +* Some languages make constructors a single method, others separate constructors into a class-specific constructor names and instance-specific initializers + +One could just use the standard way of creating new instances and then send a serious of messages to put the instance into a useable state. +There is a lot of flexibility in doing this. +However, it is not obvious what state an object needs to be in to successfully use the rest of the interface. + +## Solution + +Provide obvious instance creation methods that create well-formed instances. +Pass all required parameters to them, giving each of their parameters _Intention Revealing Names_. + +Users should not be able to create an instance that they can not use without sending a bunch of other messages to set it up before you send it further instructions. +I.e. they should not expect an error to occur when sending a message to an object that they just constructed. +And, of course, the Complete Constructor method does nothing else besides construct the object. +It would not have side effects like modifying one of the objects that was sent in as a parameter, or automatically call another public non-setter method. + +## Examples + +### Ruby + +The initializer should give you an object that you can then send messages to in order to make things happen. + +```ruby +class Thing + def initialize(name) + @name = name + end + + def doYourThing + ... + end +end +``` + +So, without having to read the implementation details of any of the methods, one who would like to use the Thing class would write… + +```ruby +thing = Thing.new(name) +thing.doYourThing +``` + +vs. + +```ruby +thing = Thing.new +thing.name = name +thing.doYourThing +``` + +vs. + +```ruby +thing = Thing.new(name) #which after it creates the instance of thing automatically sends :doYourThing +``` + +The following: + +```ruby +numberArray = %w(1 2 3) +thing = AnotherThing.new(numberArray) +``` + +should not modify numberArray (e.g. doing a `pop`) on the numberArray inside the Constructor/Initializer + +### JavaScript + +There are a variety of decent articles to teach about Constructors in JavaScript, including: + +* [Some Javascript constructor patterns, and when to use them](http://www.samselikoff.com/blog/some-Javascript-constructor-patterns/) - written November 14, 2013 but still a good overview +* [JavaScript Tutorial OOP Patterns](http://javascript.info/tutorial/oop) + +```js +function Thing(name) { + this._name = name; +} + +Thing.prototype.doYourThing = function() { + ... +} +``` + +So, without having to read the implementation details of any of the methods, one who would like to use the Thing class would write… + +```js +var thing = new Thing(name); +thing.doYourThing(); +``` + +vs. + +```js +var thing = new Thing(); +thing._name = name; +thing.doYourThing(); +``` + +vs. + +```js +var thing = new Thing(); // which after it creates the instance of thing authomatically sends :doYourThing +``` + +The following: + +```js +var numberArray = [1, 2, 3]; +var thing = new AnotherThing(numberArray); +``` + +should not modify numberArray (e.g. `numberArray.pop()`) on the numberArray inside the Constructor/Initializer diff --git a/patterns/three_levels_of_thinking.md b/patterns/three_levels_of_thinking.md new file mode 100644 index 0000000..1b42341 --- /dev/null +++ b/patterns/three_levels_of_thinking.md @@ -0,0 +1,118 @@ +# Three Levels of Thinking + +_NOTE: This can be found in a different form in our RoleModel Way series of talks._ + +Whenever we think about our systems there are three levels of thinking: +* Conceptual +* Specification +* Implementation + +Each level should be distinct and reflected in our code. + +## Assumption(s) + +* Code is read more often than it is written +* You understand the importance of _Intention Revealing Names_ + +## Discussion + +All systems have a context. They cover one or more problem spaces or domains. +> "Naming classes is your biggest billboard for communicating about your system. The first thing readers will look at when they look at your code is the names of the classes. Those names will go beyond your code. Insidiously, they leak into everyday conversation... +Good class names provide insight into the purpose and design of a system. They reveal underlying metaphors. They communicate themes and variations. +They break the system into parts and show how the parts get put back together." +> +> p. 61, Classes, Smalltalk Best Practice Patterns, Kent Beck + +These are the subjects of your programming literature. + +All methods have a context. In a pure object-oriented system, they are always in the context of an instance of a class or the class itself. +The method signatures are the predicates of your programming literature. + +Inside the methods are the implementation. They draw their context from the method signatures inside the context of a class. + +This is where the sentences and paragraphs are put together. Messages are sent to objects. +If _Intention Revealing Names_ are used for classes and method signatures, +the implementation can read almost like natural language. + +## Solution + +In order to allow readers to grok your code at the Conceptual, Specification, and Implementation levels, +You must write your code with these three levels of thinking in mind at all times. + +Classes should be named to convey their purpose and answer the question, `What is this component responsible for?` +Typically these are nouns or short noun phrases that further qualify the context in which the main noun is appropriate. + +Method signatures should indicate `What command does this execute?` or `What question does this answer?` without having to read the implementation details. +Parameters should have type suggesting names. + +The internal code of a method should exploit the power of the conceptual class names and the specification level method signatures +with role suggesting variable names. +Instance variables (or properties) and the implementation of methods answer the question `What information does the component manage?` and +`How does it execute the commands and answer the questions specified in its interface?` + +## Examples + +### Ruby + +The initialize method should use either named arguments or keyword arguments +so that it is clear how one initializes an instance +without having to read the implementation or knowing the names of instance variables. + +```ruby +class PlayingCard + def initialize(rank, suit) + @rank, @suit = rank, suit + end +end +``` + +or + +```ruby +class PlayingCard + def initialize(rank:, suit:) + @rank, @suit = rank, suit + end +end +``` + +but NOT + +```ruby +class PlayingCard + def initialize(attributes) + @rank, @suit = attributes[:rank], attributes[:suit] + end +end +``` + +### JavaScript + +Constructors should use explicitly named arguments +so that it is clear how one initializes an instance of a particular type of object +without having to read the implementation or know the names of the private properties of the object. + +```js +function PlayingCard(rank, suit) { + this._rank = rank; + this._suit = suit; +} +``` + +but NOT implicit arguments + +```js +function PlayingCard() { + this._rank = arguments[0]; + this._suit = arguments[1]; +} +``` + +and NOT arguments as a generic object/hash + +```js +function PlayingCard(attributes) { + this._rank = attributes.rank; + this._suit = attributes.suit; +} +```