Smalltalk Best Practice Patterns
Part I
1
Smalltalk Best Practice Patterns Part I 1 Based on the Book by - - PowerPoint PPT Presentation
Smalltalk Best Practice Patterns Part I 1 Based on the Book by Kent Beck 2 Based on the Book by Kent Beck Very little here is Smalltalk-specific 2 Why Patterns? 3 Why Patterns? There are only so many ways of using objects
Part I
1
2
2
3
independent of the application domain
solutions
3
independent of the application domain
solutions
3
environments and style
“maintenance”
° discovering the intent of the original programmers
4
5
6
7
8
9
10
11
° Put creation methods in a protocol called instance
creation
12
13
° Put constructor parameter methods into the private
protocol
° Answer self explicitly (INTERESTING RETURN VALUE)
14
° initialization ° state-change during computation
15
° Add no more than three such shortcut constructor
methods per system!
° Examples: 20@30, key->value,
20@30 extent: 10@10
° Put shortcut constructor methods into the converting
protocol
16
° If you convert to an object with similar responsibilities,
use a CONVERTER METHOD.
° If you convert to an object with different protocol, use a
CONVERTER CONSTRUCTOR METHOD
17
18
° examples: Collection ›› asSet,
Number ›› asFloat, but not String ›› asDate
19
° Put Converter Constructor Methods in the instance
creation protocol
° Example: Date class ›› fromString:
20
21
➡ Switch ›› on
Switch ›› off
Switch ›› status
➡
22
➡ Switch ›› on
Switch ›› off
Switch ›› status
➡ Switch ›› turnOn
Switch ›› turnOff
Switch ›› isOn
Switch ›› isOff
23
° Put comparing methods into a protocol called comparing
24
25
26
File » openDuring: aBlock
27
File » openDuring: aBlock
27
File » openDuring: aBlock | s | s := self open. aBlock value: s
28
° Point››printOn: aStream
29
° Point››printOn: aStream
30
° Point››printOn: aStream
° Point››printOn: aStream
31
° Point››printOn: aStream
° Point››printOn: aStream
32
° Point››printOn: aStream
33
33
33
33
° Obligation ›› sendTask: aTask job: aJob
34
35
Object subclass: #TaskSender instanceVariableNames: 'obligation task job notProcessed processed copies executed'
36
° Obligation ›› sendTask: aTask job: aJob
| notProcessed processed copied executed | … 150 lines of heavily commented code …
TaskSender class ›› obligation: anObligation task: aTask job: aJob ^ self new setObligation: anObligation task: aTask job: aJob
37
TaskSender››compute
° Obligation ›› sendTask: aTask job: aJob
38
39
40
41
° other clients get their own message
42
Converting Objects to Strings
There are now four getters defined in trait Object for converting an Object to a String:
Show ASCII
In the trait, all of the other methods are defined in terms of asString, so asString is the principal method that you should override when you create a new trait. Frequently, programmers write a method that emits more information about the internal structure of an object to help in debugging. If you do that, make it a getter and call it asDebugString. asExprString is intended to produce a fortress expression that is equal to the object being converted.
Examples
The automatic conversion to String that takes place when an object is concatenated to a String uses asString. The assert(a, b, m ...) function uses asDebugString to print a and b when a ≠ b Here are the results of using the three getters on the same string: asString: The word "test" is overused asExprString: "The word \"test\" is overused" asDebugString: BC27/1: J15/0:The word "test" J12/0: is overused Here they are applied to the range 1:20:2 asString: [1,3,5,7,... 19] asExprString: 1:19:2 asDebugString: StridedFullParScalarRange(1,19,2)
44
45
46
METHOD
47
show
isVisible
show
48
don’t really care
50
target isGreen ifTrue: [ target doExistingThing ] ifFalse: [ target doNewThing ]
51
target isGreen ifTrue: [ target doExistingThing ] ifFalse: [ target doNewThing ]
51
target isGreen ifTrue: [ target doExistingThing ] ifFalse: [ target doNewThing ]
51
target doAppropriateThing Green » doApproriateThing self doExistingThing Blue » doApproriateThing self doNewThing
52
54
BrowserNameMorph » onClick
55
56
BrowserNameMorph » onClick
56
BrowserNameMorph » onClick
represent a class, but not if I represent a trait.
56
BrowserNameMorph » onClick
57
BrowserNameMorph » onClick
ClassMorph » showStructure
TraitMorph » showStructure
58
responsible := (anEntry isKindOf: Film)
communicative, flexible code by using a Choosing Message:
Film»responsible ^self producer Entry»responsible ^self author
responsible := anEntry responsible
VARIABLE any more.
59
be productive with small code fragments
abstractions
60
sum := 0. 1 to: collection size
collection sum
61
collection isEmpty number reciprocal color darker
62
Collection » isEmpty ^ self size = 0 Number » reciprocal ^ 1 / self Color » darker ^ self adjustBrightness: -0.08
63
° leave the “how” for the body of the method
Array»linearSearchFor: Set»hashedSearchFor: BTree»treeSearchFor:
64
° leave the “how” for the body of the method
Array»linearSearchFor: Set»hashedSearchFor: BTree»treeSearchFor:
64
° leave the “how” for the body of the method
Array»linearSearchFor: Set»hashedSearchFor: BTree»treeSearchFor:
64
Collection»includes:
now
65
67
67
67
class side
68
sortBlock: [ :a :b | a name <= b name]
69
69
69
is different from the setting of that instance variable, write two methods!
70
70
Window class » withTitle: aTextOrString ↑ Window new title: aTextOrString;
70
Window class » withTitle: aTextOrString ↑ Window new title: aTextOrString;
Window » title: aTextOrString initializing ← title isNil. title ← aTextOrString. initializing ifFalse: [self changed: #title]
71
71
Window class » withTitle: aTextOrString ↑ Window new setTitle: aTextOrString;
71
Window class » withTitle: aTextOrString ↑ Window new setTitle: aTextOrString;
Window » setTitle: aTextOrString title ← aTextOrString.
71
Window class » withTitle: aTextOrString ↑ Window new setTitle: aTextOrString;
Window » setTitle: aTextOrString title ← aTextOrString. Window » title: aTextOrString title ← aTextOrString. self changed: #title
72
° encoded as a sequence of line, curve, stroke and fill
commands
73
| interp | interp := anInterpreter writingOn: self canvass. aShape sendCommandsTo: interp.
self components do: [ :each | each sendCommandTo: anObject]
command to the interpreter?
74
case:
self fromPoint printOn: anObject. ’ ’ printOn: anObject. self toPoint printOn: anObject. ’ line’ printOn: anObject
75
to the client
between the objects. (Beck page 57)
76
aComplexObject withSomeComponentsDo: aBlock
value: anArgument
77
78
(Sharp Ch. 9)
79
79
aPort isMemberOf: DisplayPort ifTrue: ["code for displaying on DisplayPort"]. aPort isMemberOf: PrinterPort ifTrue: ["code for displaying on PrinterPort"]. aPort isMemberOf: RemotePort ifTrue: ["code for displaying on RemotePort"].
79
aPort isMemberOf: DisplayPort ifTrue: ["code for displaying on DisplayPort"]. aPort isMemberOf: PrinterPort ifTrue: ["code for displaying on PrinterPort"]. aPort isMemberOf: RemotePort ifTrue: ["code for displaying on RemotePort"].
79
aPort isMemberOf: DisplayPort ifTrue: ["code for displaying on DisplayPort"]. aPort isMemberOf: PrinterPort ifTrue: ["code for displaying on PrinterPort"]. aPort isMemberOf: RemotePort ifTrue: ["code for displaying on RemotePort"].
Ellipse?
79
aPort isMemberOf: DisplayPort ifTrue: ["code for displaying on DisplayPort"]. aPort isMemberOf: PrinterPort ifTrue: ["code for displaying on PrinterPort"]. aPort isMemberOf: RemotePort ifTrue: ["code for displaying on RemotePort"].
Ellipse?
80
Rectangle» displayOn: aPort aPort displayRectangle: self Oval» displayOn: aPort aPort displayOval: self Bitmap» displayOn: aPort aPort displayBitmap: self ... and similarly for the other graphical objects.
81
"code to display a rectangle on a displayPort" DisplayPort » displayOval: aRect "code to display an oval on a displayPort" DisplayPort » displayBitmap: aRect "code to display a bitmap on a displayPort" ... and similarly for the other graphical objects,
"code to display a rectangle on a printerPort" PrinterPort » displayOval: aRect "code to display an oval on a printerPort" PrinterPort » displayBitmap: aRect "code to display a bimmp on a printerPort" ... and similarly for the other graphical objects
82
Rectangle» displayOn: aPort aPort displayRectangle: self
selector
PrinterPort » displayRectangle: aRect "code to display a rectangle on a printerPort"
Handling Multiple Polymorphism” by Ingalls, OOPSLA ’86
83
Create a superclass to hold common code of two or more existing classes.
84
85
you will find that it conflicts with code sharing.
you don’t yet have any code
86
87
responsible for actually drawing on the display.
88
access to the target? Does the delegate send a message back to the client?
88
access to the target? Does the delegate send a message back to the client?
target
88
access to the target? Does the delegate send a message back to the client?
self target
88
access to the target? Does the delegate send a message back to the client?
delegate self target
88
access to the target? Does the delegate send a message back to the client?
delegate target
88
access to the target? Does the delegate send a message back to the client?
self delegate target
89
collectionOfPoints do: aBlock
| newPath | newPath ← self species new: self size. newPath form: self form. newPath points: (collectionOfPoints collect: aBlock). ↑newPath
90
91
Dictionary»at: keyObject put: valueObject self hashTable at: keyObject put: valueObject for: self HashTable»at: keyObject put: valueObject for: aCollection | hash | hash ← aCollection hashOf: keyObject. Dictionary»hashOf: anObject ↑anObject hash IdentityDictionary»hashOf: anObject ↑anObject basicHash
92
PluggableButtonMorph » performAction self model perform: self actionMessage
93
ActionButton ... instanceVariableNames: ' … action … ' This class represents a button that gives a user the
mouseDown event. ActionButton » action: aBlock action ← aBlock ActionButton » mouseDown: anEvent action value
94
fireButton := ActionButton withAction: [self loadAndFire] andLabel: 'Fire'