SlideShare a Scribd company logo
1 of 96
S.Ducasse 1
QuickTime™ and aTIFF (Uncompressed) decompressorare needed to see this picture.
Stéphane Ducasse
Stephane.Ducasse@univ-savoie.fr
http://www.listic.univ-savoie.fr/~ducasse/
Elements of Design
S.Ducasse 2
License: CC-Attribution-ShareAlike 2.0
http://creativecommons.org/licenses/by-sa/2.0/
S.Ducasse 3
Elements of Design
• Instance initialization
• Enforcing the instance creation
• Instance / Class methods
• Instance variables / Class
instance variables
• Class initialization
• Law of Demeter
• Factoring Constants
• Abstract Classes
• Template Methods
• Delegation
• Bad Coding Style
S.Ducasse 4
Instance initialization
• Automatic initialize
• Lazy initialize
• Proposing the right interface
• Providing default value
S.Ducasse 5
Provider Responsibility
• This is the responsibility of the class to provide well-
formed object
• The client should not make assumptions or been
responsible to send specific sequence of messages to
get a working object
S.Ducasse 6
Instance Initialization
• How to ensure that an instance is well initialized?
• Automatic initialize
• Lazy initialize
• Proposing the right interface
• Providing default value
S.Ducasse 7
A First Implementation of Packet
Object subclass: #Packet
instanceVariableNames:‘contents addressee originator ‘
Packet>>printOn: aStream
super printOn: aStream.
aStream nextPutAll:‘ addressed to:‘; nextPutAll: self addressee.
aStream nextPutAll:‘ with contents: ’; nextPutAll: self contents
Packet>>addressee
^addressee
Packet>>addressee: aSymbol
addressee := aSymbol
S.Ducasse 8
Packet class Definition
Packet class is automatically defined
Packet class
instanceVariableNames: ''
Example of instance creation
Packet new
addressee: #mac ;
contents:‘hello mac’
S.Ducasse 9
Fragile Instance Creation
If we do not specify a contents, it breaks!
|p|
p := Packet new addressee: #mac.
p printOn: aStream -> error
Problems of this approach:
responsibility of the instance creation relies on the clients
A client can create packet without contents, without
address instance variable not initialized -> error (for
example, printOn:) -> system fragile
S.Ducasse 10
Fragile Instance Creation Solutions
• Automatic initialization of instance variables
• Proposing a solid interface for the creation
• Lazy initialization
S.Ducasse 11
Assuring InstanceVariable Initialization
• How to initialize a newly created instance ?
• Define the method initialize
Packet>>initialize
super initialize.
contents := ‘’.
addressee := #noAd
S.Ducasse 12
(VW) Assuring InstanceVariable Initialization
• Problem: By default new class method returns
instance with uninitialized instance variables.
• Moreover inVisualWorks, initialize method is not
automatically called by creation methods new/new:.
• How to initialize a newly created instance ?
S.Ducasse 13
The New/Initialize Couple
Define an instance method that initializes the instance
variables and override new to invoke it.
(1&2) Packet class>>new “Class Method”
^ super new initialize
(3) Packet>>initialize “Instance Method”
super initialize.
(4) contents := ‘default message’
Packet new (1-2) => aPacket initialize (3-4) => returning
aPacket but initialized!
Reminder:You cannot access instance variables from a class
method like new
S.Ducasse 14
The New/Initialize Couple
Object>>initialize
“do nothing. Called by new my subclasses
override me if necessary”
^ self
S.Ducasse 15
One single method application
S.Ducasse 16
Strengthen Instance Creation Interface
• Problem: A client can still create aPacket without
address.
• Solution: Force the client to use the class interface
creation.
• Providing an interface for creation and avoiding the
use of new: Packet send:‘Hello mac’ to: #Mac
• First try:
Packet class>>send: aString to: anAddress
^ self new contents: aString ; addressee: anAddress
S.Ducasse 17
Examples of Instance Initialization
step 1. SortedCollection sortBlock: [:a :b| a name < b
name]
SortedCollection class>>sortBlock: aBlock
"Answer a new instance of SortedCollection such that its
elements are sorted according to the criterion specified in
aBlock."
^self new sortBlock: aBlock
step 2. self new => aSortedCollection
step 3. aSortedCollection sortBlock: aBlock
step 4. returning the instance aSortedCollection
S.Ducasse 18
Another Example
step 1. OrderedCollection with: 1
Collection class>>with: anObject
"Answer a new instance of a Collection containing
anObject."
| newCollection |
newCollection := self new.
newCollection add: anObject.
^newCollection
S.Ducasse 19
Lazy Initialization
When some instance variables are:
- not used all the time
- consuming space, difficult to initialize because depending
on other
- need a lot of computation
Use lazy initialization based on accessors
Accessor access should be used consistently!
S.Ducasse 20
Lazy Initialization Example
A lazy initialization scheme with default value
Packet>>contents
contents isNil
ifTrue: [contents := ‘no contents’]
^ contents
aPacket contents or self contents
A lazy initialization scheme with computed value
Dummy>>ratioBetweenThermonuclearAndSolar
ratio isNil
ifTrue: [ratio := self heavyComputation]
^ ratio
S.Ducasse 21
Providing a DefaultValue
OrderedCollection variableSubclass: #SortedCollection
instanceVariableNames: 'sortBlock '
classVariableNames: 'DefaultSortBlock '
SortedCollection class>>initialize
DefaultSortBlock := [:x :y | x <= y]
SortedCollection>>initialize
"Set the initial value of the receiver's sorting algorithm
to a default.”
sortBlock := DefaultSortBlock
S.Ducasse 22
Providing a DefaultValue
SortedCollection class>>new: anInteger
"Answer a new instance of SortedCollection.The
default sorting is a <= comparison on elements. "
^ (super new: anInteger) initialize
SortedCollection class>>sortBlock: aBlock
"Answer a new instance of SortedCollection such
that its elements are sorted according to the
criterion specified in aBlock. "
^ self new sortBlock: aBlock
S.Ducasse 23
Invoking per Default the Creation Interface
OrderedCollection class>>new
"Answer a new empty instance of OrderedCollection."
^self new: 5
S.Ducasse 24
Forbidding new?
Problem: We can still use new to create fragile instances
Solution: new should raise an error!
Packet class>>new
self error: 'Packet should only be created using
send:to:'
S.Ducasse 25
Forbidding new Implications
But we still have to be able to create instance!
Packet class>>send: aString to: anAddres
^ self new contents: aString ; addressee: anAddress
=> raises an error
Packet class>>send: aString to: anAddress
^ super new contents: aString ; addressee: anAddress
=> BAD STYLE: link between class and superclass
dangerous in case of evolution
S.Ducasse 26
Forbidding new
Solution: use basicNew and basicNew:
Packet class>>send: aString to: anAddress
^ self basicNew
contents: aString ;
addressee: anAddress
Conclusion: Never override basic* methods else you will
not be able to invoke them later
S.Ducasse 27
How to Reuse Superclass Initialization?
A class>>new
^ super new doThat; andThat; end
B class>>forceClientInterface
^ self basicNew ???
Solution: Define the initialization behavior on the instance
side
A>>doThatAndThatEnd
^ self doThat; andThat; end
A class>>new
^ super new doThatAndThatEnd
B class>>forceClientInterface
^ self basicNew doThatAndThatEnd
S.Ducasse 28
Different Self/Super
Do not invoke a super with a different method selector.
It’s bad style because it links a class and a superclass.
This is dangerous in case the software evolves.
S.Ducasse 29
Example
Packet class>>new
self error: 'Packet should be created using send:to:'
Packet class>>send: aString to: anAddress
^ super new contents: aString ; addressee:
anAddress
Use basicNew and basicNew:
Packet class>>send: aString to: anAddress
^ self basicNew contents: aString ; addressee: anAddress
S.Ducasse 30
Super is static!
With the super foo:
A new bar
-> 10
B new bar
-> 10
C new bar
-> 10
Without the super foo:
A new bar
-> 10
B new bar
-> 100
C new bar
-> 50
S.Ducasse 31
Basic Design Mistakes
S.Ducasse 32
A Class should have
Class Person {
String getName();
void setName(String name);
int getAge();
void setAge(int age);
Car getCar();
void setCar(Car car);
}
What do we see ?
A class should have one main responsibility and some
behavior not just holding state
Minimal access to its data!
S.Ducasse 33
Confusing
Class City extends Place { … }
Class Jerusalem extends City implements Capital { … }
Class TelAviv extends City { … }
What is wrong here?
Confusing inheritance and instantiation
Too much inheritance?
S.Ducasse 34
Do not expose implementation
S.Ducasse 35
Do not overuse conversions
nodes asSet
removes all the duplicated nodes (if node knows how to
compare). But a systematic use of asSet to protect
yourself from duplicate is not good
nodes asSet asOrderedCollection
returns an ordered collection after removing duplicates
Look for the real source of duplication if you do not
want it!
S.Ducasse 36
Hiding missing information
Dictionary>>at: aKey
This raises an error if the key is not found
Dictionary>>at: aKey ifAbsent: aBlock
Allows one to specify action aBlock to be done when
the key does not exist.
Do not overuse it:
nodes at: nodeId ifAbsent:[ ]
This is bad because at least we should know that the
nodeId was missing
S.Ducasse 37
isNil
Avoid to return special results as nil
messages := self fetchMessages.
messages isNil
  ifFalse: [ messages dispatchFrom: self ]
What if we would simply return an empty collection in
fetchMessages instead of nil?
Less conditional and ugly tests!!
S.Ducasse 38
Say once and only once
• No Magic Number Duplicated
• Extract method
• Remove duplicated code
S.Ducasse 39
Factorize Magic Numbers
Ideally you should be able to change your constants
without having any impact on the code!
For that
define a constant only once via accessor
provide testing method (hasNextNode)
default value using the constant accessor
S.Ducasse 40
Factoring Out Constants
We want to encapsulate the way “no next node” is
coded. Instead of writing:
Node>>nextNode
^ nextNode
NodeClient>>transmitTo: aNode
aNode nextNode = ‘no next node’
...
S.Ducasse 41
Factoring Out Constants
Write:
NodeClient>>transmitTo: aNode
aNode hasNextNode
....
Node>>hasNextNode
^ (self nextNode = self class noNextNode) not
Node class>>noNextNode
^ ‘no next node’
S.Ducasse 42
Default value between class and instance
If we want to encapsulate the way “no next node” is
coded and shared this knowledge between class and
instances.
Instead of writing:
aNode nextNode isNil not
Write:
Node>>hasNextNode
^ self nextNode = self noNextNode
Node>>noNextNode
^self class noNextNode
Node class>>noNextNode
^ #noNode
S.Ducasse 43
Initializing without Duplicating
Node>>initialize
accessType := ‘local’
...
Node>>isLocal
^ accessType = ‘local’
It’s better to write
Node>>initialize
accessType := self localAccessType
Node>>isLocal
^ accessType = self localAccessType
Node>>localAccessType
^ ‘local’
S.Ducasse 44
Say something only once
Ideally you could be able to change the constant without
having any problems.
You may have to have mapping tables from model
constants to UI constants or database constants.
S.Ducasse 45
Constants Needed at Creation Time
Node class>>localNodeNamed: aString
|inst|
inst := self new.
inst name: aString.
inst type: inst localAccessType
If you want to have the following creation interface
Node class>>name: aString accessType: aType
^self new name: aString ; accessType: aType
Node class>>name: aString
^self name: aString accessType: self localAccessType
S.Ducasse 46
Constants Needed at Creation Time
You need:
Node class>>localAccessType
^ ‘local’
=> Factor the constant between class and instance level
Node>>localAccessType
^ self class localAccessType
=>You could also use a ClassVariable that is shared
between a class and its instances.
S.Ducasse 47
Elements of Design
• Class initialization
S.Ducasse 48
Class Methods - Class InstanceVariables
• Classes (Packet class) represents class (Packet).
• Class instance variables are instance variables of class
• They should represent the state of class: number of
created instances, number of messages sent,
superclasses, subclasses....
• Class methods represent class behavior: instance
creation, class initialization, counting the number of
instances....
• If you weaken the second point: class state and
behavior can be used to define common properties
shared by all the instances
S.Ducasse 49
Class Initialization
• How do we know that all the class behavior has been
loaded?
• At the end !
• Automatically called by the system at load time or
explicitly by the programmer.
• Used to initialize a classVariable, a pool dictionary or
class instance variables.
• ‘Classname initialize’ at the end of the saved files in
Squeak
• In postLoadAction: inVW
S.Ducasse 50
Example of class initialization
Magnitude subclass: #Date
instanceVariableNames: 'day year'
classVariableNames:
'DaysInMonth FirstDayOfMonth MonthNames
SecondsInDay WeekDayNames’
S.Ducasse 51
Date class>>initialize
Date class>>initialize
"Initialize class variables representing the names of the months and
days and the number of seconds, days in each month, and first day of
each month. "
MonthNames := #(January February March April May
June July August September October November December ).
SecondsInDay := 24 * 60 * 60.
DaysInMonth := #(31 28 31 30 31 30 31 31 30 31 30 31 ).
FirstDayOfMonth := #(1 32 60 91 121 152 182 213 244 274 305 335 ).
WeekDayNames := #(Monday TuesdayWednesday Thursday Friday
Saturday Sunday )
S.Ducasse 52
Sharing or not
• How can I share state and prepare
• for instance specific state?
S.Ducasse 53
Case Study: Scanner
Scanner new
scanTokens: 'identifier keyword: 8r31 ''string''
embedded.period key:word: . '
>
#(#identifier #keyword: 25 'string' 'embedded.period'
#key:word: #'.')
S.Ducasse 54
A Case Study:The Scanner class
Class Definition
Object subclass: #Scanner
instanceVariableNames: 'source mark prevEnd hereChar
token tokenType saveComments currentComment buffer
typeTable '
classVariableNames: 'TypeTable '
poolDictionaries: ''
category: 'System-Compiler-Public Access'
S.Ducasse 55
Scanner enigma
Why having an instance variable and a classVariable
denoting the same object (the scanner table)?
TypeTable is used to initialize once the table
typeTable is used by every instance and each instance
can customize the table (copying).
S.Ducasse 56
Clever Sharing
S.Ducasse 57
A Case Study: Scanner (II)
Scanner class>>initialize
"Scanner initialize"
| newTable |
newTable := ScannerTable new: 255 withAll: #xDefault. "default"
newTable atAllSeparatorsPut: #xDelimiter.
newTable atAllDigitsPut: #xDigit.
newTable atAllLettersPut: #xLetter.
'!%&*+,-/<=>?@~' do: [:bin | newTable at: bin asInteger put: #xBinary].
"Other multi-character tokens"
newTable at: $" asInteger put: #xDoubleQuote.
...
"Single-character tokens"
newTable at: $( asInteger put: #leftParenthesis.
...
newTable at: $^ asInteger put: #upArrow. "spacing circumflex, formerly up
arrow"
newTable at: $| asInteger put: #verticalBar.
TypeTable := newTable
S.Ducasse 58
A Case Study: Scanner (III)
Instances only access the type table via the instance variable that points to
the table that has been initialized once.
Scanner class>> new
^super new initScanner
Scanner>>initScanner
buffer := WriteStream on: (String new: 40).
saveComments := true.
typeTable :=TypeTable
A subclass just has to specialize initScanner without copying the
initialization of the table
MyScanner>>initScanner
super initScanner
typeTable := typeTable copy.
typeTable at: $) asInteger put: #xDefault.
S.Ducasse 59
A Simple Case...
• Introducing parametrization
S.Ducasse 60
Parametrization Advantages
DialectStream>>initializeST80ColorTable
"Initialize the colors that characterize the ST80 dialect"
ST80ColorTable _ IdentityDictionary new.
#((temporaryVariable blue italic)
(methodArgument blue normal)
…
(setOrReturn black bold)) do:
[:aTriplet |
ST80ColorTable at: aTriplet first put: aTriplet allButFirst]
•Problems:
•Color tables hardcoded in method
•Changes Require compilation
•Client responsible of initialize invocation
•No run-time changes
S.Ducasse 61
One Step
DialectStream>>initializeST80ColorTable
ST80ColorTable := IdentityDictionary new.
self defaultDescription do:
[:aTriplet |
ST80ColorTable at: aTriplet first put: aTriplet allButFirst]
DialectStream>>defaultDescription
^ #((temporaryVariable blue italic)
(methodArgument blue normal)
…
(setOrReturn black bold))
Still requires subclassing and recompilation
S.Ducasse 62
Composition-based Solution
DialectStream>>initializeST80ColorTableWith: anArray
ST80ColorTable := IdentityDictionary new.
anArray
do: [:aTriplet | ST80ColorTable at: aTriplet first
put: aTriplet allButFirst].
self initialize
•In a Client
DialectStream initializeST80ColorTableWith:
#(#(#temporaryVariable #blue #normal) …
#(#prefixKeyword #veryDarkGray #bold)
#(#setOrReturn #red #bold) )
S.Ducasse 63
Methods are Units of Reuse
• Dynamic binding and methods
• = reuse in subclasses
S.Ducasse 64
Methods are Unit of Reuse
S.Ducasse 65
Example: Forced to Duplicate!
Node>>computeRatioForDisplay
|averageRatio defaultNodeSize|
averageRatio := 55.
defaultNodeSize := self mainWindowCoordinate /maximiseViewRatio.
self window add:
(UINode new with:
(self bandWidth * averageRatio / defaultWindowSize)
…
•We are forced to copy the complete method!
SpecialNode>>computeRatioForDisplay
|averageRatio defaultNodeSize|
averageRatio := 55.
defaultNodeSize := self mainWindowCoordinate + minimalRatio /
maximiseViewRatio.
self window add:
(UINode new with: (self bandWidth * averageRatio / defaultWindowSize)
…
S.Ducasse 66
Self sends: Plan for Reuse
Node>>computeRatioForDisplay
|averageRatio defaultNodeSize|
averageRatio := 55.
defaultNodeSize := self defaultNodeSize.
self window add:
(UINode new with:
(self bandWidth * averageRatio / defaultWindowSize)
...
Node>>defaultNodeSize
^self mainWindowCoordinate / maxiViewRatio
SpecialNode>>defaultNodeSize
^ self mainWindowCoordinate+minimalRatio/maxiViewRatio
S.Ducasse 67
Do not Hardcode Constants
Node>>computeRatioForDisplay
|averageRatio defaultNodeSize|
averageRatio := 55.
defaultNodeSize := self mainWindowCoordinate / maximiseViewRatio.
self window add:
(UINode new with:
(self bandWidth * averageRatio / defaultWindowSize).
...
•We are forced to copy the method!
SpecialNode>>computeRatioForDisplay
|averageRatio defaultNodeSize|
averageRatio := 55.
defaultNodeSize := self mainWindowCoordinate / maximiseViewRatio.
self window add:
(ExtendedUINode new with:
(self bandWidth * averageRatio / defaultWindowSize).
S.Ducasse 68
Class Factories
Node>>computeRatioForDisplay
|averageRatio |
averageRatio := 55.
self window add:
self UIClass new with:
(self bandWidth * averageRatio / self
defaultWindowSize)
...
Node>>UIClass
^ UINode
SpecialNode>>UIClass
^ ExtendedUINode
S.Ducasse 69
Hook and Template
S.Ducasse 70
Hook and Template Methods
• Hooks: place for reuse
• Template: context for reuse
S.Ducasse 71
Hook and Template Methods
• Templates: Context reused by subclasses
• Hook methods: holes that can be specialized
• Hook methods do not have to be abstract, they may define
default behavior or no behavior at all.
• This has an influence on the instantiability of the superclass.
S.Ducasse 72
Hook / Template Example: Printing
Object>>printString
"Answer a String whose characters are a description of the
receiver."
| aStream |
aStream := WriteStream on: (String new: 16).
self printOn: aStream.
^aStream contents
S.Ducasse 73
Hook
Object>>printOn: aStream
"Append to the argument aStream a sequence of
characters that describes the receiver."
| title |
title := self class name.
aStream nextPutAll:
((title at: 1) isVowel ifTrue: ['an '] ifFalse: ['a ']).
aStream print: self class
S.Ducasse 74
Overriding the Hook
Array>>printOn: aStream
"Append to the argument, aStream, the elements of the Array
enclosed by parentheses."
| tooMany |
tooMany := aStream position + self maxPrint.
aStream nextPutAll: '#('.
self do: [:element |
aStream position > tooMany
ifTrue: [ aStream nextPutAll: '...(more)...)'.
^self ].
element printOn: aStream]
separatedBy: [aStream space].
aStream nextPut: $)
S.Ducasse 75
Overriding
False>>printOn: aStream
"Print false."
aStream nextPutAll: 'false'
S.Ducasse 76
Specialization of the Hook
The class Behavior that represents a class extends the
default hook but still invokes the default one.
Behavior>>printOn: aStream
"Append to the argument aStream a statement of which
superclass the receiver descends from."
aStream nextPutAll: 'a descendent of '.
superclass printOn: aStream
S.Ducasse 77
Another Example: Copying
Complex (deepCopy, veryDeepCopy...)
Recursive objects
Graph of connected objects
Each object wants a different copy of itself
No up-front solution
S.Ducasse 78
Hook Example: Copying
Object>>copy
" Answer another instance just like the receiver. Subclasses
normally override the postCopy message, but some objects
that should not be copied override copy. "
^self shallowCopy postCopy
Object>>shallowCopy
"Answer a copy of the receiver which shares the
receiver's instance variables."
<primitive: 532>
S.Ducasse 79
postCopy
Object>>postCopy
"Finish doing whatever is required, beyond a
shallowCopy, to implement 'copy'.Answer the receiver.This
message is only intended to be sent to the newly created
instance. Subclasses may add functionality, but they should
always do super postCopy first. "
^self
S.Ducasse 80
Sounds Trivial?
S.Ducasse 81
Hook Specialisation
Bag>>postCopy
"Make sure to copy the contents fully."
| new |
super postCopy.
new := contents class new: contents capacity.
contents keysAndValuesDo:
[:obj :count | new at: obj put: count].
contents := new.
S.Ducasse 82
Guidelines for Creating Template Methods
Simple implementation.
Implement all the code in one method.
Break into steps.
Comment logical subparts
Make step methods.
Extract subparts as methods
Call the step methods
Make constant methods, i.e., methods doing nothing else
than returning.
Repeat steps 1-5 if necessary on the methods created
S.Ducasse 83
Inheritance vs. Composition
S.Ducasse 84
Delegation of Responsibilities
New requirement:A document can be printed on
different printers for example lw100s or lw200s
depending on which printer is first encountered.
S.Ducasse 85
Ad-hoc Solution
LanPrinter>>accept: aPacket
(thePacket addressee = #*lw*)
ifTrue: [ self print: thePacket]
ifFalse: [ (thePacket isAddressedTo: self)
ifTrue: [self print: thePacket]
ifFalse: [super accept: thePacket]]
Limits:
not general
brittle because based on a convention
adding a new kind of address behavior requires editing the
class Printer
S.Ducasse 86
Create Object and Delegate
• An alternative solution: isAddressedTo: could be sent directly to the address
• With the current solution, the packet can still control the process if needed
accept: aPacket
send: aPacket
nodePrinter aPacket
node1
isAddressedTo: nodePrinter
accept: aPacket
print: aPacket
[true]
[false]
anAddress
S.Ducasse 87
NodeAddress
NodeAddress is responsible for identifying the packet
receivers
Packet>>isAddressedTo: aNode
^ self address isAddressedTo: aNode address “was
name”
Object subclass: #NodeAddress
instanceVariableNames:‘id‘
NodeAddress>>isAddressedTo: aNodeAddress
^ self id = aNodeAddress id
S.Ducasse 88
Matching Address
For packets with matchable addresses
Packet send:‘lulu’ to: (MatchingAddress with: #*lw*)
Address subclass: #MatchingAddress
instanceVariableNames:‘’
MatchingAddress>>isAddressedTo: aNodeAddress
^ self id match: aNodeAddress id
S.Ducasse 89
Addresses
Object subclass: #Address
instanceVariableNames:‘id‘
Address>>isAddressedTo: anAddress
^self subclassResponsibility
Address subclass: #NodeAddress
instanceVariableNames:‘‘
Address subclass: #MatchingAddress
instanceVariableNames:‘‘
S.Ducasse 90
Trade-Off
Delegation Pros
No blob class: one class one responsibility
Variation possibility
Pluggable behavior without inheritance extension
Runtime pluggability
Delegation Cons
Difficult to follow responsibilities and message flow
Adding new classes = adding complexities (more names)
New object
S.Ducasse 91
Inheritance vs. Composition
Inheritance is not a panacea
Require class definition
Require method definition
Extension should be prepared in advance
No run-time changes
Ex: editor with spell-checkerS, colorizerS, mail-
readerS….
No clear responsibility
Code bloated
Cannot load a new colorizers
S.Ducasse 92
Delegating to other Objects
myEditor setColorizer: FastColorizer new.
myEditor setColorizer:AdvancedColorizer new.
Strategy design pattern
S.Ducasse 93
Composition Analysis
Pros
Possibility to change at run-time
Clear responsibility
No blob
Clear interaction protocol
Cons
New class
Delegation
New classes
S.Ducasse 94
Designing Classes...
S.Ducasse 95
Designing Classes for Reuse
Encapsulation principle: minimize data representation
dependencies
Complete interface
No overuse of accessors
Responsibility of the instance creation
Loose coupling between classes
Methods are units of reuse (self send)
Use polymorphism as much as possible to avoid type
checking
Behavior up and state down
Use correct names for class
Use correct names for methods
S.Ducasse 96
Nothing magic
Think about it
Find your own heuristics
Taste, try and be critic
Be the force with you...
Summary

More Related Content

What's hot (9)

04 idioms
04 idioms04 idioms
04 idioms
 
4 - OOP - Taste of Smalltalk (VisualWorks)
4 - OOP - Taste of Smalltalk (VisualWorks)4 - OOP - Taste of Smalltalk (VisualWorks)
4 - OOP - Taste of Smalltalk (VisualWorks)
 
07 bestpractice
07 bestpractice07 bestpractice
07 bestpractice
 
5 - OOP - Smalltalk in a Nutshell (a)
5 - OOP - Smalltalk in a Nutshell (a)5 - OOP - Smalltalk in a Nutshell (a)
5 - OOP - Smalltalk in a Nutshell (a)
 
12 virtualmachine
12 virtualmachine12 virtualmachine
12 virtualmachine
 
02 basics
02 basics02 basics
02 basics
 
Chapter ii(oop)
Chapter ii(oop)Chapter ii(oop)
Chapter ii(oop)
 
8 - OOP - Syntax & Messages
8 - OOP - Syntax & Messages8 - OOP - Syntax & Messages
8 - OOP - Syntax & Messages
 
11 bytecode
11 bytecode11 bytecode
11 bytecode
 

Viewers also liked (20)

Stoop 303-advanced blocks
Stoop 303-advanced blocksStoop 303-advanced blocks
Stoop 303-advanced blocks
 
Stoop 415-design points
Stoop 415-design pointsStoop 415-design points
Stoop 415-design points
 
09 metaclasses
09 metaclasses09 metaclasses
09 metaclasses
 
Stoop 423-some designpatterns
Stoop 423-some designpatternsStoop 423-some designpatterns
Stoop 423-some designpatterns
 
Stoop 439-decorator
Stoop 439-decoratorStoop 439-decorator
Stoop 439-decorator
 
03 standardclasses
03 standardclasses03 standardclasses
03 standardclasses
 
Stoop 434-composite
Stoop 434-compositeStoop 434-composite
Stoop 434-composite
 
8 - OOP - Smalltalk Model
8 - OOP - Smalltalk Model8 - OOP - Smalltalk Model
8 - OOP - Smalltalk Model
 
Stoop 390-instruction stream
Stoop 390-instruction streamStoop 390-instruction stream
Stoop 390-instruction stream
 
Stoop 436-strategy
Stoop 436-strategyStoop 436-strategy
Stoop 436-strategy
 
06 debugging
06 debugging06 debugging
06 debugging
 
Stoop 437-proxy
Stoop 437-proxyStoop 437-proxy
Stoop 437-proxy
 
Stoop sed-class initialization
Stoop sed-class initializationStoop sed-class initialization
Stoop sed-class initialization
 
Stoop 413-abstract classes
Stoop 413-abstract classesStoop 413-abstract classes
Stoop 413-abstract classes
 
Stoop 301-internal objectstructureinvw
Stoop 301-internal objectstructureinvwStoop 301-internal objectstructureinvw
Stoop 301-internal objectstructureinvw
 
Stoop ed-lod
Stoop ed-lodStoop ed-lod
Stoop ed-lod
 
10 - OOP - Inheritance (a)
10 - OOP - Inheritance (a)10 - OOP - Inheritance (a)
10 - OOP - Inheritance (a)
 
15 - Streams
15 - Streams15 - Streams
15 - Streams
 
Stoop 305-reflective programming5
Stoop 305-reflective programming5Stoop 305-reflective programming5
Stoop 305-reflective programming5
 
01 intro
01 intro01 intro
01 intro
 

Similar to Elements of Design Principles

Java-Module 3-PPT-TPS.pdf.Java-Module 3-PPT-TPS.pdf
Java-Module 3-PPT-TPS.pdf.Java-Module 3-PPT-TPS.pdfJava-Module 3-PPT-TPS.pdf.Java-Module 3-PPT-TPS.pdf
Java-Module 3-PPT-TPS.pdf.Java-Module 3-PPT-TPS.pdfkakarthik685
 
Still Comparing "this" Pointer to Null?
Still Comparing "this" Pointer to Null?Still Comparing "this" Pointer to Null?
Still Comparing "this" Pointer to Null?Andrey Karpov
 
Object Trampoline: Why having not the object you want is what you need.
Object Trampoline: Why having not the object you want is what you need.Object Trampoline: Why having not the object you want is what you need.
Object Trampoline: Why having not the object you want is what you need.Workhorse Computing
 
TDD step patterns
TDD step patternsTDD step patterns
TDD step patternseduardomg23
 
Advanced programming topics asma
Advanced programming topics asmaAdvanced programming topics asma
Advanced programming topics asmaAbdullahJana
 
Observer & singleton pattern
Observer  & singleton patternObserver  & singleton pattern
Observer & singleton patternbabak danyal
 

Similar to Elements of Design Principles (20)

9 - OOP - Smalltalk Classes (b)
9 - OOP - Smalltalk Classes (b)9 - OOP - Smalltalk Classes (b)
9 - OOP - Smalltalk Classes (b)
 
9 - OOP - Smalltalk Classes (a)
9 - OOP - Smalltalk Classes (a)9 - OOP - Smalltalk Classes (a)
9 - OOP - Smalltalk Classes (a)
 
9 - OOP - Smalltalk Classes (c)
9 - OOP - Smalltalk Classes (c)9 - OOP - Smalltalk Classes (c)
9 - OOP - Smalltalk Classes (c)
 
Stoop 432-singleton
Stoop 432-singletonStoop 432-singleton
Stoop 432-singleton
 
Stoop ed-subtyping subclassing
Stoop ed-subtyping subclassingStoop ed-subtyping subclassing
Stoop ed-subtyping subclassing
 
Stoop 450-s unit
Stoop 450-s unitStoop 450-s unit
Stoop 450-s unit
 
Stoop sed-smells
Stoop sed-smellsStoop sed-smells
Stoop sed-smells
 
Debugging VisualWorks
Debugging VisualWorksDebugging VisualWorks
Debugging VisualWorks
 
Stoop ed-unit ofreuse
Stoop ed-unit ofreuseStoop ed-unit ofreuse
Stoop ed-unit ofreuse
 
04 threads
04 threads04 threads
04 threads
 
Stoop 304-metaclasses
Stoop 304-metaclassesStoop 304-metaclasses
Stoop 304-metaclasses
 
Stoop 300-block optimizationinvw
Stoop 300-block optimizationinvwStoop 300-block optimizationinvw
Stoop 300-block optimizationinvw
 
Java-Module 3-PPT-TPS.pdf.Java-Module 3-PPT-TPS.pdf
Java-Module 3-PPT-TPS.pdf.Java-Module 3-PPT-TPS.pdfJava-Module 3-PPT-TPS.pdf.Java-Module 3-PPT-TPS.pdf
Java-Module 3-PPT-TPS.pdf.Java-Module 3-PPT-TPS.pdf
 
Still Comparing "this" Pointer to Null?
Still Comparing "this" Pointer to Null?Still Comparing "this" Pointer to Null?
Still Comparing "this" Pointer to Null?
 
Object Trampoline: Why having not the object you want is what you need.
Object Trampoline: Why having not the object you want is what you need.Object Trampoline: Why having not the object you want is what you need.
Object Trampoline: Why having not the object you want is what you need.
 
TDD step patterns
TDD step patternsTDD step patterns
TDD step patterns
 
Java 102
Java 102Java 102
Java 102
 
Advanced programming topics asma
Advanced programming topics asmaAdvanced programming topics asma
Advanced programming topics asma
 
Observer & singleton pattern
Observer  & singleton patternObserver  & singleton pattern
Observer & singleton pattern
 
10 - OOP - Inheritance (b)
10 - OOP - Inheritance (b)10 - OOP - Inheritance (b)
10 - OOP - Inheritance (b)
 

More from The World of Smalltalk (12)

05 seaside canvas
05 seaside canvas05 seaside canvas
05 seaside canvas
 
99 questions
99 questions99 questions
99 questions
 
13 traits
13 traits13 traits
13 traits
 
10 reflection
10 reflection10 reflection
10 reflection
 
08 refactoring
08 refactoring08 refactoring
08 refactoring
 
05 seaside
05 seaside05 seaside
05 seaside
 
Stoop sed-class initialization
Stoop sed-class initializationStoop sed-class initialization
Stoop sed-class initialization
 
Stoop metaclasses
Stoop metaclassesStoop metaclasses
Stoop metaclasses
 
Stoop ed-inheritance composition
Stoop ed-inheritance compositionStoop ed-inheritance composition
Stoop ed-inheritance composition
 
Stoop ed-frameworks
Stoop ed-frameworksStoop ed-frameworks
Stoop ed-frameworks
 
Stoop ed-dual interface
Stoop ed-dual interfaceStoop ed-dual interface
Stoop ed-dual interface
 
Stoop 440-adaptor
Stoop 440-adaptorStoop 440-adaptor
Stoop 440-adaptor
 

Elements of Design Principles

  • 1. S.Ducasse 1 QuickTime™ and aTIFF (Uncompressed) decompressorare needed to see this picture. Stéphane Ducasse Stephane.Ducasse@univ-savoie.fr http://www.listic.univ-savoie.fr/~ducasse/ Elements of Design
  • 2. S.Ducasse 2 License: CC-Attribution-ShareAlike 2.0 http://creativecommons.org/licenses/by-sa/2.0/
  • 3. S.Ducasse 3 Elements of Design • Instance initialization • Enforcing the instance creation • Instance / Class methods • Instance variables / Class instance variables • Class initialization • Law of Demeter • Factoring Constants • Abstract Classes • Template Methods • Delegation • Bad Coding Style
  • 4. S.Ducasse 4 Instance initialization • Automatic initialize • Lazy initialize • Proposing the right interface • Providing default value
  • 5. S.Ducasse 5 Provider Responsibility • This is the responsibility of the class to provide well- formed object • The client should not make assumptions or been responsible to send specific sequence of messages to get a working object
  • 6. S.Ducasse 6 Instance Initialization • How to ensure that an instance is well initialized? • Automatic initialize • Lazy initialize • Proposing the right interface • Providing default value
  • 7. S.Ducasse 7 A First Implementation of Packet Object subclass: #Packet instanceVariableNames:‘contents addressee originator ‘ Packet>>printOn: aStream super printOn: aStream. aStream nextPutAll:‘ addressed to:‘; nextPutAll: self addressee. aStream nextPutAll:‘ with contents: ’; nextPutAll: self contents Packet>>addressee ^addressee Packet>>addressee: aSymbol addressee := aSymbol
  • 8. S.Ducasse 8 Packet class Definition Packet class is automatically defined Packet class instanceVariableNames: '' Example of instance creation Packet new addressee: #mac ; contents:‘hello mac’
  • 9. S.Ducasse 9 Fragile Instance Creation If we do not specify a contents, it breaks! |p| p := Packet new addressee: #mac. p printOn: aStream -> error Problems of this approach: responsibility of the instance creation relies on the clients A client can create packet without contents, without address instance variable not initialized -> error (for example, printOn:) -> system fragile
  • 10. S.Ducasse 10 Fragile Instance Creation Solutions • Automatic initialization of instance variables • Proposing a solid interface for the creation • Lazy initialization
  • 11. S.Ducasse 11 Assuring InstanceVariable Initialization • How to initialize a newly created instance ? • Define the method initialize Packet>>initialize super initialize. contents := ‘’. addressee := #noAd
  • 12. S.Ducasse 12 (VW) Assuring InstanceVariable Initialization • Problem: By default new class method returns instance with uninitialized instance variables. • Moreover inVisualWorks, initialize method is not automatically called by creation methods new/new:. • How to initialize a newly created instance ?
  • 13. S.Ducasse 13 The New/Initialize Couple Define an instance method that initializes the instance variables and override new to invoke it. (1&2) Packet class>>new “Class Method” ^ super new initialize (3) Packet>>initialize “Instance Method” super initialize. (4) contents := ‘default message’ Packet new (1-2) => aPacket initialize (3-4) => returning aPacket but initialized! Reminder:You cannot access instance variables from a class method like new
  • 14. S.Ducasse 14 The New/Initialize Couple Object>>initialize “do nothing. Called by new my subclasses override me if necessary” ^ self
  • 15. S.Ducasse 15 One single method application
  • 16. S.Ducasse 16 Strengthen Instance Creation Interface • Problem: A client can still create aPacket without address. • Solution: Force the client to use the class interface creation. • Providing an interface for creation and avoiding the use of new: Packet send:‘Hello mac’ to: #Mac • First try: Packet class>>send: aString to: anAddress ^ self new contents: aString ; addressee: anAddress
  • 17. S.Ducasse 17 Examples of Instance Initialization step 1. SortedCollection sortBlock: [:a :b| a name < b name] SortedCollection class>>sortBlock: aBlock "Answer a new instance of SortedCollection such that its elements are sorted according to the criterion specified in aBlock." ^self new sortBlock: aBlock step 2. self new => aSortedCollection step 3. aSortedCollection sortBlock: aBlock step 4. returning the instance aSortedCollection
  • 18. S.Ducasse 18 Another Example step 1. OrderedCollection with: 1 Collection class>>with: anObject "Answer a new instance of a Collection containing anObject." | newCollection | newCollection := self new. newCollection add: anObject. ^newCollection
  • 19. S.Ducasse 19 Lazy Initialization When some instance variables are: - not used all the time - consuming space, difficult to initialize because depending on other - need a lot of computation Use lazy initialization based on accessors Accessor access should be used consistently!
  • 20. S.Ducasse 20 Lazy Initialization Example A lazy initialization scheme with default value Packet>>contents contents isNil ifTrue: [contents := ‘no contents’] ^ contents aPacket contents or self contents A lazy initialization scheme with computed value Dummy>>ratioBetweenThermonuclearAndSolar ratio isNil ifTrue: [ratio := self heavyComputation] ^ ratio
  • 21. S.Ducasse 21 Providing a DefaultValue OrderedCollection variableSubclass: #SortedCollection instanceVariableNames: 'sortBlock ' classVariableNames: 'DefaultSortBlock ' SortedCollection class>>initialize DefaultSortBlock := [:x :y | x <= y] SortedCollection>>initialize "Set the initial value of the receiver's sorting algorithm to a default.” sortBlock := DefaultSortBlock
  • 22. S.Ducasse 22 Providing a DefaultValue SortedCollection class>>new: anInteger "Answer a new instance of SortedCollection.The default sorting is a <= comparison on elements. " ^ (super new: anInteger) initialize SortedCollection class>>sortBlock: aBlock "Answer a new instance of SortedCollection such that its elements are sorted according to the criterion specified in aBlock. " ^ self new sortBlock: aBlock
  • 23. S.Ducasse 23 Invoking per Default the Creation Interface OrderedCollection class>>new "Answer a new empty instance of OrderedCollection." ^self new: 5
  • 24. S.Ducasse 24 Forbidding new? Problem: We can still use new to create fragile instances Solution: new should raise an error! Packet class>>new self error: 'Packet should only be created using send:to:'
  • 25. S.Ducasse 25 Forbidding new Implications But we still have to be able to create instance! Packet class>>send: aString to: anAddres ^ self new contents: aString ; addressee: anAddress => raises an error Packet class>>send: aString to: anAddress ^ super new contents: aString ; addressee: anAddress => BAD STYLE: link between class and superclass dangerous in case of evolution
  • 26. S.Ducasse 26 Forbidding new Solution: use basicNew and basicNew: Packet class>>send: aString to: anAddress ^ self basicNew contents: aString ; addressee: anAddress Conclusion: Never override basic* methods else you will not be able to invoke them later
  • 27. S.Ducasse 27 How to Reuse Superclass Initialization? A class>>new ^ super new doThat; andThat; end B class>>forceClientInterface ^ self basicNew ??? Solution: Define the initialization behavior on the instance side A>>doThatAndThatEnd ^ self doThat; andThat; end A class>>new ^ super new doThatAndThatEnd B class>>forceClientInterface ^ self basicNew doThatAndThatEnd
  • 28. S.Ducasse 28 Different Self/Super Do not invoke a super with a different method selector. It’s bad style because it links a class and a superclass. This is dangerous in case the software evolves.
  • 29. S.Ducasse 29 Example Packet class>>new self error: 'Packet should be created using send:to:' Packet class>>send: aString to: anAddress ^ super new contents: aString ; addressee: anAddress Use basicNew and basicNew: Packet class>>send: aString to: anAddress ^ self basicNew contents: aString ; addressee: anAddress
  • 30. S.Ducasse 30 Super is static! With the super foo: A new bar -> 10 B new bar -> 10 C new bar -> 10 Without the super foo: A new bar -> 10 B new bar -> 100 C new bar -> 50
  • 32. S.Ducasse 32 A Class should have Class Person { String getName(); void setName(String name); int getAge(); void setAge(int age); Car getCar(); void setCar(Car car); } What do we see ? A class should have one main responsibility and some behavior not just holding state Minimal access to its data!
  • 33. S.Ducasse 33 Confusing Class City extends Place { … } Class Jerusalem extends City implements Capital { … } Class TelAviv extends City { … } What is wrong here? Confusing inheritance and instantiation Too much inheritance?
  • 34. S.Ducasse 34 Do not expose implementation
  • 35. S.Ducasse 35 Do not overuse conversions nodes asSet removes all the duplicated nodes (if node knows how to compare). But a systematic use of asSet to protect yourself from duplicate is not good nodes asSet asOrderedCollection returns an ordered collection after removing duplicates Look for the real source of duplication if you do not want it!
  • 36. S.Ducasse 36 Hiding missing information Dictionary>>at: aKey This raises an error if the key is not found Dictionary>>at: aKey ifAbsent: aBlock Allows one to specify action aBlock to be done when the key does not exist. Do not overuse it: nodes at: nodeId ifAbsent:[ ] This is bad because at least we should know that the nodeId was missing
  • 37. S.Ducasse 37 isNil Avoid to return special results as nil messages := self fetchMessages. messages isNil   ifFalse: [ messages dispatchFrom: self ] What if we would simply return an empty collection in fetchMessages instead of nil? Less conditional and ugly tests!!
  • 38. S.Ducasse 38 Say once and only once • No Magic Number Duplicated • Extract method • Remove duplicated code
  • 39. S.Ducasse 39 Factorize Magic Numbers Ideally you should be able to change your constants without having any impact on the code! For that define a constant only once via accessor provide testing method (hasNextNode) default value using the constant accessor
  • 40. S.Ducasse 40 Factoring Out Constants We want to encapsulate the way “no next node” is coded. Instead of writing: Node>>nextNode ^ nextNode NodeClient>>transmitTo: aNode aNode nextNode = ‘no next node’ ...
  • 41. S.Ducasse 41 Factoring Out Constants Write: NodeClient>>transmitTo: aNode aNode hasNextNode .... Node>>hasNextNode ^ (self nextNode = self class noNextNode) not Node class>>noNextNode ^ ‘no next node’
  • 42. S.Ducasse 42 Default value between class and instance If we want to encapsulate the way “no next node” is coded and shared this knowledge between class and instances. Instead of writing: aNode nextNode isNil not Write: Node>>hasNextNode ^ self nextNode = self noNextNode Node>>noNextNode ^self class noNextNode Node class>>noNextNode ^ #noNode
  • 43. S.Ducasse 43 Initializing without Duplicating Node>>initialize accessType := ‘local’ ... Node>>isLocal ^ accessType = ‘local’ It’s better to write Node>>initialize accessType := self localAccessType Node>>isLocal ^ accessType = self localAccessType Node>>localAccessType ^ ‘local’
  • 44. S.Ducasse 44 Say something only once Ideally you could be able to change the constant without having any problems. You may have to have mapping tables from model constants to UI constants or database constants.
  • 45. S.Ducasse 45 Constants Needed at Creation Time Node class>>localNodeNamed: aString |inst| inst := self new. inst name: aString. inst type: inst localAccessType If you want to have the following creation interface Node class>>name: aString accessType: aType ^self new name: aString ; accessType: aType Node class>>name: aString ^self name: aString accessType: self localAccessType
  • 46. S.Ducasse 46 Constants Needed at Creation Time You need: Node class>>localAccessType ^ ‘local’ => Factor the constant between class and instance level Node>>localAccessType ^ self class localAccessType =>You could also use a ClassVariable that is shared between a class and its instances.
  • 47. S.Ducasse 47 Elements of Design • Class initialization
  • 48. S.Ducasse 48 Class Methods - Class InstanceVariables • Classes (Packet class) represents class (Packet). • Class instance variables are instance variables of class • They should represent the state of class: number of created instances, number of messages sent, superclasses, subclasses.... • Class methods represent class behavior: instance creation, class initialization, counting the number of instances.... • If you weaken the second point: class state and behavior can be used to define common properties shared by all the instances
  • 49. S.Ducasse 49 Class Initialization • How do we know that all the class behavior has been loaded? • At the end ! • Automatically called by the system at load time or explicitly by the programmer. • Used to initialize a classVariable, a pool dictionary or class instance variables. • ‘Classname initialize’ at the end of the saved files in Squeak • In postLoadAction: inVW
  • 50. S.Ducasse 50 Example of class initialization Magnitude subclass: #Date instanceVariableNames: 'day year' classVariableNames: 'DaysInMonth FirstDayOfMonth MonthNames SecondsInDay WeekDayNames’
  • 51. S.Ducasse 51 Date class>>initialize Date class>>initialize "Initialize class variables representing the names of the months and days and the number of seconds, days in each month, and first day of each month. " MonthNames := #(January February March April May June July August September October November December ). SecondsInDay := 24 * 60 * 60. DaysInMonth := #(31 28 31 30 31 30 31 31 30 31 30 31 ). FirstDayOfMonth := #(1 32 60 91 121 152 182 213 244 274 305 335 ). WeekDayNames := #(Monday TuesdayWednesday Thursday Friday Saturday Sunday )
  • 52. S.Ducasse 52 Sharing or not • How can I share state and prepare • for instance specific state?
  • 53. S.Ducasse 53 Case Study: Scanner Scanner new scanTokens: 'identifier keyword: 8r31 ''string'' embedded.period key:word: . ' > #(#identifier #keyword: 25 'string' 'embedded.period' #key:word: #'.')
  • 54. S.Ducasse 54 A Case Study:The Scanner class Class Definition Object subclass: #Scanner instanceVariableNames: 'source mark prevEnd hereChar token tokenType saveComments currentComment buffer typeTable ' classVariableNames: 'TypeTable ' poolDictionaries: '' category: 'System-Compiler-Public Access'
  • 55. S.Ducasse 55 Scanner enigma Why having an instance variable and a classVariable denoting the same object (the scanner table)? TypeTable is used to initialize once the table typeTable is used by every instance and each instance can customize the table (copying).
  • 57. S.Ducasse 57 A Case Study: Scanner (II) Scanner class>>initialize "Scanner initialize" | newTable | newTable := ScannerTable new: 255 withAll: #xDefault. "default" newTable atAllSeparatorsPut: #xDelimiter. newTable atAllDigitsPut: #xDigit. newTable atAllLettersPut: #xLetter. '!%&*+,-/<=>?@~' do: [:bin | newTable at: bin asInteger put: #xBinary]. "Other multi-character tokens" newTable at: $" asInteger put: #xDoubleQuote. ... "Single-character tokens" newTable at: $( asInteger put: #leftParenthesis. ... newTable at: $^ asInteger put: #upArrow. "spacing circumflex, formerly up arrow" newTable at: $| asInteger put: #verticalBar. TypeTable := newTable
  • 58. S.Ducasse 58 A Case Study: Scanner (III) Instances only access the type table via the instance variable that points to the table that has been initialized once. Scanner class>> new ^super new initScanner Scanner>>initScanner buffer := WriteStream on: (String new: 40). saveComments := true. typeTable :=TypeTable A subclass just has to specialize initScanner without copying the initialization of the table MyScanner>>initScanner super initScanner typeTable := typeTable copy. typeTable at: $) asInteger put: #xDefault.
  • 59. S.Ducasse 59 A Simple Case... • Introducing parametrization
  • 60. S.Ducasse 60 Parametrization Advantages DialectStream>>initializeST80ColorTable "Initialize the colors that characterize the ST80 dialect" ST80ColorTable _ IdentityDictionary new. #((temporaryVariable blue italic) (methodArgument blue normal) … (setOrReturn black bold)) do: [:aTriplet | ST80ColorTable at: aTriplet first put: aTriplet allButFirst] •Problems: •Color tables hardcoded in method •Changes Require compilation •Client responsible of initialize invocation •No run-time changes
  • 61. S.Ducasse 61 One Step DialectStream>>initializeST80ColorTable ST80ColorTable := IdentityDictionary new. self defaultDescription do: [:aTriplet | ST80ColorTable at: aTriplet first put: aTriplet allButFirst] DialectStream>>defaultDescription ^ #((temporaryVariable blue italic) (methodArgument blue normal) … (setOrReturn black bold)) Still requires subclassing and recompilation
  • 62. S.Ducasse 62 Composition-based Solution DialectStream>>initializeST80ColorTableWith: anArray ST80ColorTable := IdentityDictionary new. anArray do: [:aTriplet | ST80ColorTable at: aTriplet first put: aTriplet allButFirst]. self initialize •In a Client DialectStream initializeST80ColorTableWith: #(#(#temporaryVariable #blue #normal) … #(#prefixKeyword #veryDarkGray #bold) #(#setOrReturn #red #bold) )
  • 63. S.Ducasse 63 Methods are Units of Reuse • Dynamic binding and methods • = reuse in subclasses
  • 64. S.Ducasse 64 Methods are Unit of Reuse
  • 65. S.Ducasse 65 Example: Forced to Duplicate! Node>>computeRatioForDisplay |averageRatio defaultNodeSize| averageRatio := 55. defaultNodeSize := self mainWindowCoordinate /maximiseViewRatio. self window add: (UINode new with: (self bandWidth * averageRatio / defaultWindowSize) … •We are forced to copy the complete method! SpecialNode>>computeRatioForDisplay |averageRatio defaultNodeSize| averageRatio := 55. defaultNodeSize := self mainWindowCoordinate + minimalRatio / maximiseViewRatio. self window add: (UINode new with: (self bandWidth * averageRatio / defaultWindowSize) …
  • 66. S.Ducasse 66 Self sends: Plan for Reuse Node>>computeRatioForDisplay |averageRatio defaultNodeSize| averageRatio := 55. defaultNodeSize := self defaultNodeSize. self window add: (UINode new with: (self bandWidth * averageRatio / defaultWindowSize) ... Node>>defaultNodeSize ^self mainWindowCoordinate / maxiViewRatio SpecialNode>>defaultNodeSize ^ self mainWindowCoordinate+minimalRatio/maxiViewRatio
  • 67. S.Ducasse 67 Do not Hardcode Constants Node>>computeRatioForDisplay |averageRatio defaultNodeSize| averageRatio := 55. defaultNodeSize := self mainWindowCoordinate / maximiseViewRatio. self window add: (UINode new with: (self bandWidth * averageRatio / defaultWindowSize). ... •We are forced to copy the method! SpecialNode>>computeRatioForDisplay |averageRatio defaultNodeSize| averageRatio := 55. defaultNodeSize := self mainWindowCoordinate / maximiseViewRatio. self window add: (ExtendedUINode new with: (self bandWidth * averageRatio / defaultWindowSize).
  • 68. S.Ducasse 68 Class Factories Node>>computeRatioForDisplay |averageRatio | averageRatio := 55. self window add: self UIClass new with: (self bandWidth * averageRatio / self defaultWindowSize) ... Node>>UIClass ^ UINode SpecialNode>>UIClass ^ ExtendedUINode
  • 70. S.Ducasse 70 Hook and Template Methods • Hooks: place for reuse • Template: context for reuse
  • 71. S.Ducasse 71 Hook and Template Methods • Templates: Context reused by subclasses • Hook methods: holes that can be specialized • Hook methods do not have to be abstract, they may define default behavior or no behavior at all. • This has an influence on the instantiability of the superclass.
  • 72. S.Ducasse 72 Hook / Template Example: Printing Object>>printString "Answer a String whose characters are a description of the receiver." | aStream | aStream := WriteStream on: (String new: 16). self printOn: aStream. ^aStream contents
  • 73. S.Ducasse 73 Hook Object>>printOn: aStream "Append to the argument aStream a sequence of characters that describes the receiver." | title | title := self class name. aStream nextPutAll: ((title at: 1) isVowel ifTrue: ['an '] ifFalse: ['a ']). aStream print: self class
  • 74. S.Ducasse 74 Overriding the Hook Array>>printOn: aStream "Append to the argument, aStream, the elements of the Array enclosed by parentheses." | tooMany | tooMany := aStream position + self maxPrint. aStream nextPutAll: '#('. self do: [:element | aStream position > tooMany ifTrue: [ aStream nextPutAll: '...(more)...)'. ^self ]. element printOn: aStream] separatedBy: [aStream space]. aStream nextPut: $)
  • 75. S.Ducasse 75 Overriding False>>printOn: aStream "Print false." aStream nextPutAll: 'false'
  • 76. S.Ducasse 76 Specialization of the Hook The class Behavior that represents a class extends the default hook but still invokes the default one. Behavior>>printOn: aStream "Append to the argument aStream a statement of which superclass the receiver descends from." aStream nextPutAll: 'a descendent of '. superclass printOn: aStream
  • 77. S.Ducasse 77 Another Example: Copying Complex (deepCopy, veryDeepCopy...) Recursive objects Graph of connected objects Each object wants a different copy of itself No up-front solution
  • 78. S.Ducasse 78 Hook Example: Copying Object>>copy " Answer another instance just like the receiver. Subclasses normally override the postCopy message, but some objects that should not be copied override copy. " ^self shallowCopy postCopy Object>>shallowCopy "Answer a copy of the receiver which shares the receiver's instance variables." <primitive: 532>
  • 79. S.Ducasse 79 postCopy Object>>postCopy "Finish doing whatever is required, beyond a shallowCopy, to implement 'copy'.Answer the receiver.This message is only intended to be sent to the newly created instance. Subclasses may add functionality, but they should always do super postCopy first. " ^self
  • 81. S.Ducasse 81 Hook Specialisation Bag>>postCopy "Make sure to copy the contents fully." | new | super postCopy. new := contents class new: contents capacity. contents keysAndValuesDo: [:obj :count | new at: obj put: count]. contents := new.
  • 82. S.Ducasse 82 Guidelines for Creating Template Methods Simple implementation. Implement all the code in one method. Break into steps. Comment logical subparts Make step methods. Extract subparts as methods Call the step methods Make constant methods, i.e., methods doing nothing else than returning. Repeat steps 1-5 if necessary on the methods created
  • 84. S.Ducasse 84 Delegation of Responsibilities New requirement:A document can be printed on different printers for example lw100s or lw200s depending on which printer is first encountered.
  • 85. S.Ducasse 85 Ad-hoc Solution LanPrinter>>accept: aPacket (thePacket addressee = #*lw*) ifTrue: [ self print: thePacket] ifFalse: [ (thePacket isAddressedTo: self) ifTrue: [self print: thePacket] ifFalse: [super accept: thePacket]] Limits: not general brittle because based on a convention adding a new kind of address behavior requires editing the class Printer
  • 86. S.Ducasse 86 Create Object and Delegate • An alternative solution: isAddressedTo: could be sent directly to the address • With the current solution, the packet can still control the process if needed accept: aPacket send: aPacket nodePrinter aPacket node1 isAddressedTo: nodePrinter accept: aPacket print: aPacket [true] [false] anAddress
  • 87. S.Ducasse 87 NodeAddress NodeAddress is responsible for identifying the packet receivers Packet>>isAddressedTo: aNode ^ self address isAddressedTo: aNode address “was name” Object subclass: #NodeAddress instanceVariableNames:‘id‘ NodeAddress>>isAddressedTo: aNodeAddress ^ self id = aNodeAddress id
  • 88. S.Ducasse 88 Matching Address For packets with matchable addresses Packet send:‘lulu’ to: (MatchingAddress with: #*lw*) Address subclass: #MatchingAddress instanceVariableNames:‘’ MatchingAddress>>isAddressedTo: aNodeAddress ^ self id match: aNodeAddress id
  • 89. S.Ducasse 89 Addresses Object subclass: #Address instanceVariableNames:‘id‘ Address>>isAddressedTo: anAddress ^self subclassResponsibility Address subclass: #NodeAddress instanceVariableNames:‘‘ Address subclass: #MatchingAddress instanceVariableNames:‘‘
  • 90. S.Ducasse 90 Trade-Off Delegation Pros No blob class: one class one responsibility Variation possibility Pluggable behavior without inheritance extension Runtime pluggability Delegation Cons Difficult to follow responsibilities and message flow Adding new classes = adding complexities (more names) New object
  • 91. S.Ducasse 91 Inheritance vs. Composition Inheritance is not a panacea Require class definition Require method definition Extension should be prepared in advance No run-time changes Ex: editor with spell-checkerS, colorizerS, mail- readerS…. No clear responsibility Code bloated Cannot load a new colorizers
  • 92. S.Ducasse 92 Delegating to other Objects myEditor setColorizer: FastColorizer new. myEditor setColorizer:AdvancedColorizer new. Strategy design pattern
  • 93. S.Ducasse 93 Composition Analysis Pros Possibility to change at run-time Clear responsibility No blob Clear interaction protocol Cons New class Delegation New classes
  • 95. S.Ducasse 95 Designing Classes for Reuse Encapsulation principle: minimize data representation dependencies Complete interface No overuse of accessors Responsibility of the instance creation Loose coupling between classes Methods are units of reuse (self send) Use polymorphism as much as possible to avoid type checking Behavior up and state down Use correct names for class Use correct names for methods
  • 96. S.Ducasse 96 Nothing magic Think about it Find your own heuristics Taste, try and be critic Be the force with you... Summary