-
Notifications
You must be signed in to change notification settings - Fork 2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Patterns #18
Open
kauerrolemodel
wants to merge
6
commits into
master
Choose a base branch
from
patterns
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Patterns #18
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
ddd9e25
First attempt at writing up Complete Constructor
kauerrolemodel 5374de4
Added JavaScript example and tweaked
kauerrolemodel b2cb6b2
Added Three Levels of Thinking
kauerrolemodel 8feedb5
Clarified a bit more about implementation
kauerrolemodel 1823136
Elaborated on JavaScript example
kauerrolemodel d99ee92
Add Github Markdown formating niceties
calebwoods File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} | ||
``` |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do intent that to mean things like using
format_string
overformat
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is a great question... does
format
implystring type
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
format
implies eitherString
orRegex
types to me.