From ddd9e2521192837fe9439dc931db6f59652a1c0b Mon Sep 17 00:00:00 2001 From: Ken Auer Date: Mon, 2 May 2016 21:32:29 -0400 Subject: [PATCH 1/6] First attempt at writing up Complete Constructor --- README.md | 4 ++ patterns/complete_constructor.md | 78 ++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+) create mode 100644 patterns/complete_constructor.md diff --git a/README.md b/README.md index 7e0e467..9f7a434 100644 --- a/README.md +++ b/README.md @@ -32,3 +32,7 @@ This holds all of our best practices. ## Project Practices 1. [Core Project Presentations](projects/core_presentation.md) + +## Encouraged Design & Code Patterns + +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..c5cb952 --- /dev/null +++ b/patterns/complete_constructor.md @@ -0,0 +1,78 @@ +# 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. + +``` +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… +``` +thing = Thing.new(name) +thing.doYourThing +``` +vs. +``` +thing = Thing.new +thing.name = name +thing.doYourThing +``` +vs. +``` +thing = Thing.new(name) #which after it creates the instance of thing authomatically sends :doYourThing +``` + +The following: +``` +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) From 5374de4f7969c9511c2c7ce8461666d23c26e36c Mon Sep 17 00:00:00 2001 From: Ken Auer Date: Mon, 2 May 2016 21:45:47 -0400 Subject: [PATCH 2/6] Added JavaScript example and tweaked --- patterns/complete_constructor.md | 35 +++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/patterns/complete_constructor.md b/patterns/complete_constructor.md index c5cb952..4a3be92 100644 --- a/patterns/complete_constructor.md +++ b/patterns/complete_constructor.md @@ -70,9 +70,42 @@ thing = AnotherThing.new(numberArray) ``` should not modify numberArray (e.g. doing a `pop`) on the numberArray inside the Constructor/Initializer -## JavaScript +### 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) + +``` +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… +``` +var thing = new Thing(name); +thing.doYourThing(); +``` +vs. +``` +var thing = new Thing(); +thing._name = name; +thing.doYourThing(); +``` +vs. +``` +var thing = new Thing(); // which after it creates the instance of thing authomatically sends :doYourThing +``` + +The following: +``` +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 From b2cb6b21d923c275ecec4c9a11c0477cf6489773 Mon Sep 17 00:00:00 2001 From: Ken Auer Date: Mon, 2 May 2016 23:41:01 -0400 Subject: [PATCH 3/6] Added Three Levels of Thinking --- README.md | 1 + patterns/complete_constructor.md | 2 +- patterns/three_levels_of_thinking.md | 93 ++++++++++++++++++++++++++++ 3 files changed, 95 insertions(+), 1 deletion(-) create mode 100644 patterns/three_levels_of_thinking.md diff --git a/README.md b/README.md index 9f7a434..dc64e39 100644 --- a/README.md +++ b/README.md @@ -35,4 +35,5 @@ This holds all of our best practices. ## 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 index 4a3be92..738e077 100644 --- a/patterns/complete_constructor.md +++ b/patterns/complete_constructor.md @@ -1,6 +1,6 @@ # Complete Constructor -_NOTE: Some of the phrases used here come from "Smalltalk Best Practice Patterns" by Kent Beck, *Constructor Method* +_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. diff --git a/patterns/three_levels_of_thinking.md b/patterns/three_levels_of_thinking.md new file mode 100644 index 0000000..f0de256 --- /dev/null +++ b/patterns/three_levels_of_thinking.md @@ -0,0 +1,93 @@ +# 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. + +## Examples + +### Ruby + +The initialize method should use either named arguments or keyword arguments + +``` +class PlayingCard + def initialize(rank, suit) + @rank, @suit = rank, suit + end +end +``` +or +``` +class PlayingCard + def initialize(rank:, suit:) + @rank, @suit = rank, suit + end +end +``` +but NOT +``` +class PlayingCard + def initialize(attributes) + @rank, @suit = attributes[:rank], attributes[:suit] + end +end +``` + +### JavaScript + +Constructors should use explicitly named arguments + +``` +function PlayingCard(rank, suit) { + this._rank = rank; + this._suit = suit; +} +``` +but NOT implicit arguments +``` +function PlayingCard() { + this._rank = arguments[0]; + this._suit = arguments[1]; +} +``` From 8feedb5b9ddb0bdcb1e20de89c24c07d200e4355 Mon Sep 17 00:00:00 2001 From: Ken Auer Date: Mon, 2 May 2016 23:51:48 -0400 Subject: [PATCH 4/6] Clarified a bit more about implementation --- patterns/three_levels_of_thinking.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/patterns/three_levels_of_thinking.md b/patterns/three_levels_of_thinking.md index f0de256..81ee982 100644 --- a/patterns/three_levels_of_thinking.md +++ b/patterns/three_levels_of_thinking.md @@ -43,12 +43,16 @@ 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. ``` class PlayingCard @@ -77,6 +81,8 @@ 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. ``` function PlayingCard(rank, suit) { From 1823136a60f1825ecef4722ee6ea0b2c1169da82 Mon Sep 17 00:00:00 2001 From: Ken Auer Date: Mon, 2 May 2016 23:57:04 -0400 Subject: [PATCH 5/6] Elaborated on JavaScript example --- patterns/three_levels_of_thinking.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/patterns/three_levels_of_thinking.md b/patterns/three_levels_of_thinking.md index 81ee982..53e7d3a 100644 --- a/patterns/three_levels_of_thinking.md +++ b/patterns/three_levels_of_thinking.md @@ -97,3 +97,10 @@ function PlayingCard() { this._suit = arguments[1]; } ``` +and NOT arguments as a generic object/hash +``` +function PlayingCard(attributes) { + this._rank = attributes.rank; + this._suit = attributes.suit; +} +``` From d99ee92ec4f54cd12dbc477e82b060cb39fa4584 Mon Sep 17 00:00:00 2001 From: Caleb Woods Date: Wed, 4 May 2016 11:44:12 -0400 Subject: [PATCH 6/6] Add Github Markdown formating niceties --- patterns/complete_constructor.md | 36 +++++++++++++++++++--------- patterns/three_levels_of_thinking.md | 28 +++++++++++++++------- 2 files changed, 45 insertions(+), 19 deletions(-) diff --git a/patterns/complete_constructor.md b/patterns/complete_constructor.md index 738e077..99d8f1a 100644 --- a/patterns/complete_constructor.md +++ b/patterns/complete_constructor.md @@ -35,7 +35,7 @@ It would not have side effects like modifying one of the objects that was sent i 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 @@ -48,26 +48,33 @@ 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. -``` -thing = Thing.new(name) #which after it creates the instance of thing authomatically sends :doYourThing + +```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 @@ -77,7 +84,7 @@ There are a variety of decent articles to teach about Constructors in JavaScript * [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; } @@ -88,24 +95,31 @@ 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 index 53e7d3a..1b42341 100644 --- a/patterns/three_levels_of_thinking.md +++ b/patterns/three_levels_of_thinking.md @@ -17,15 +17,19 @@ Each level should be distinct and reflected in our code. ## 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... +> "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) +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. @@ -54,23 +58,27 @@ 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] @@ -84,21 +92,25 @@ 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;