-
Notifications
You must be signed in to change notification settings - Fork 0
/
withStyle.pillar
2737 lines (1947 loc) · 78 KB
/
withStyle.pillar
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
Programming is a lot more than just writing algorithms or programs. Programming is all about communication. Communication with others: not only with the other programmers who will be involved in your development effort, but also with yourself. Indeed, finding good names is a really important task because using the right name often opens the door to new spaces where your design can bloom and expand.
The purpose of a programming style guide such as this book is to provide a simple vehicle for addressing the needs of good communication. The goal is to make the source code clear, easy to read, and easy to understand and extend.
These conventions are not cast in stone, but they set the foundation of a common culture. Culture is important when programming.
We were influenced by the excellent little book called ''Smalltalk with Style''. We hope that you will enjoy this one and that it will help you become a better communicative designer.
Feedback and suggestions are welcome at stephane.ducasse@inria.fr.
Pull requests on *https://github.com/SquareBracketAssociates/Booklet-PharoWithStyle* are also welcome.
Special thank to Réné-Paul Mages, Christopher Furhman, Benoit St Jean and Masashi Fujita, Nathan Reilly, Esteban Maringolo, Hernán Morales for their feedback.
S. Ducasse - 12 February 2022.
!! General naming conventions
In this chapter, we will start with guidelines about names.
Indeed, names are important. And using ''good'' names is the cornerstone of writing good software. This is true in any language. We will never repeat it enough.
Now, finding a good name is sometimes difficult. To help, good names are often driven by a domain or an ecosystem. Finally, the object-oriented programming paradigm gives some context that is useful: For example, the "Don't ask, tell" motto favors active and direct orders for method names.
!!! Guideline: Favor simple direct meaning
Some native English speakers use more precise, but less common terms. Consider that your software may be read by people from different cultures. So use simple, mainstream, and common terms. Note that this does not mean that you should not use precise terms. Avoid hidden or implied meanings that can only be understood by a limited group of people. Make information explicit and clear.
!!! Guideline: Use descriptive names
Choose descriptive names that capture domain entities unambiguously. Avoid cryptic names. Characters are cheap, so do not count them. If you are slow at typing then use the completion engine.
Prefer
[[[type=prefer
dayOfWeek
]]]
over
[[[type=not
Not dow
]]]
Prefer
[[[type=prefer
seconds
]]]
over
[[[type=not
Not sec
]]]
Prefer
[[[type=prefer
copyMenu
]]]
over
[[[type=not
Not cMenu
]]]
!!! Guideline: Pay attention to meaning
Pharo uses English as main language. It looks strange to mention it, but this has some implications. We should pay attention to two mistakes that non native english speakers tend to do: first, adjective inversion, and second, keeping the plural form during inversion of composition (using of).
!!!!! Adjective inversion
Non-English native speakers often misplace word qualifiers (adjective). In English, the qualifier is often before the word it qualifies. In Pharo, we follow such a convention.
Prefer
[[[type=prefer
DateParser
]]]
over
[[[type=not
Not ParserForDate
]]]
or
[[[type=not
Not ParserDate
]]]
Prefer
[[[type=prefer
userAssociation
]]]
(an association of users)
over
[[[type=not
Not associationUser
]]]
!!!!! No plural inside
In English, the names of methods can also be written method names. Notice that in such a situation there is no s in method.
Prefer
[[[type=prefer
userAssociation
]]]
over
[[[type=not
Not usersAssociation
]]]
!!!!! Avoid homographs
Compare the three following variables:
[[[
sizeToRead
sizeJustRead
readSize
]]]
In this situation, avoid homographs (*https://en.wiktionary.org/wiki/homograph*).
That is, words that are written the same way but can have different meanings or pronunciations. For example, ''Did you read that book? ... Yes, I read it yesterday.''
About ==readSize==: does this mean that the size was just read (red) or that it is the size to read (reed)?
Favor words with a unique pronunciation.
!!! Guideline: Follow domain
Follow the domain concepts and culture of the project. Do not invent your own terms because you think they are better. Favor regularity (consistency) over preciseness.
Prefer
[[[type=prefer
GnatXmlNode
]]]
over
[[[type=not
not GNAT_XML_Object
]]]
When representing the XML Ada abstract syntax tree in Pharo, we should not follow Ada naming conventions. The name should convey that the class is an abstract syntax tree node. Hence, ==GnatXmlNode== is much better than ==GnatXmlObject==.
Another example is the following: In Moose, an importer is an object creating FAMIX entities (classes, methods, etc.) from the data structure representing a language element, usually an Abstract Syntax Tree (AST). Therefore ==GNATInstaller==, which creates entities from an AST, should be renamed ==GnatImporter== and ==GNATImporter==, which loads an AST in memory should be renamed ==GnatASTLoader==.
!!! Guideline: Favor unique meaning and pronunciation
Choose names that have a unique meaning. Avoid homographs.
Prefer
[[[type=prefer
sizeToRead
sizeJustRead
]]]
over
[[[type=not
Not readSize
]]]
Does this mean that the size was just read (red) or is it the size to read (reed)?
!!! Guideline: Limit or use abbreviations consistently
Abbreviations or acronisms are often obscure to newcomers. Now they are often handy so when you use them use consistently.
Prefer
[[[type=prefer
ASTNode
]]]
over
[[[type=prefer
TreeNode
AbstractSyntaxTreeNode
]]]
!!! Guideline: Avoid numbers
It can be handy when trying something new to reuse an identifier and add a number to it. Now limit this practice to your development session. Indeed, a number does not help the reader understand the difference. As a general principle, keeping multiple implementations of the same classes is a way to propose rotten code. So if you want to keep several versions then make sure that why several versions are kept is made clear.
Prefer
[[[type=prefer
StackBasedInlineParser
NewInlineParser
CleanerInlineParser
]]]
over
[[[type=not
InlineParser2
]]]
!!! Conclusion
The guidelines presented so far are general. We will see that we also have guidelines for classes and variables.
!! About class names
In object-oriented programming, classes play an important role. They are factories of objects, and as such, they are important for conveying the main abstractions within an application.
!!! Guideline: Use direct and natural style
Often, classes represent objects, even abstract ones. Use the names that fit the best without inventing some clunky new terms.
Prefer
[[[type=prefer
Terminal
]]]
over
[[[type=not
PlaceToDisplayInformation
]]]
Some classes represent an action (such as Visitor) or process.
[[[type=prefer
RBReadBeforeWrittenTester
MicHTMLWriter
]]]
Finally, some may represent a particular state. In Pharo, announcements follow the convention of finishing with the verb in the past tense. It contradicts English conventions, but it is applied uniformly.
[[[type=prefer
IcePackageLoaded
MCVersionSaved
RubBoundsChanged
]]]
Avoid class name using gerund
[[[type=not
RBParsing
OpeningFile
]]]
!!! Guideline: Use natural language
When defining class names, avoid abbreviations that are not obvious, avoid shortening names, and favor natural language.
Prefer
[[[type=prefer
RandomNumberGenerator
]]]
over
[[[type=not
RandNumbGen
]]]
Prefer
[[[type=prefer
CSVImporter
]]]
over
[[[type=not
CSVImport
]]]
Prefer
[[[type=prefer
RemoteControl
]]]
over
[[[type=not
RemControl
]]]
Prefer
[[[type=prefer
Player
]]]
over
[[[type=not
BoardMan
]]]
!!! Guideline: Do not expose implementation
A class name should not expose the implementation the class uses. This is important because implementation may change in the future.
Prefer
[[[type=prefer
PropertyName
ContactBook
]]]
over
[[[type=not
PropertyNameString
ContactDictionary
]]]
!!! Guideline: Avoid to use plural for class names
A class often has many instances. TestCase classes are special classes where their methods are individual tests. Still, it is really awkward to have a class ending with S.
Prefer
[[[type=prefer
AthensTextRenderManualTest
]]]
over
[[[type=not
AthensTextRenderManualTests
]]]
Of course some domain names ends with S, in such as case just follow the convention.
Prefer
[[[type=prefer
NetworkSystemSettings
]]]
!!! Guideline: Avoid name collisions
To avoid name space collisions, add a prefix indicative of the project to the name of the class.
Prefer
[[[type=prefer
PRDocument
CmdMessage
]]]
@@note You may find that Pharo is lacking a namespace. If you have a couple hundred thousand euros, we can fix that!
Note, however, that even with a namespace you will have to pay attention that your namespace name does not collide with another one.
!!! Guideline: Class names should indicate the class' parent
Suffix class names with the root class to convey the kind of object we are talking about.
For example, without the ==Morph== suffix, the reader is forced to check the superclass to understand if the class is about a graphical object or not.
Prefer
[[[type=prefer
ClyBrowserButtonMorph
]]]
over
[[[type=not
ClyBrowserButton
]]]
Prefer
[[[type=prefer
ClyQueryViewMorph
]]]
over
[[[type=not
ClyQueryView
]]]
Prefer
[[[type=prefer
SpLayoutHelpTopics
]]]
over
[[[type=not
SpLayouts
]]]
In the following, not mentioning the ==Presenter== suffix makes it unclear to the reader that it is a Presenter object as opposed to a Model object.
Prefer
[[[type=prefer
ApplicationWithToolBarPresenter
]]]
over
[[[type=not
ApplicationWithToolbar
]]]
In the next example, ==DynamicWidgetChange== does not convey that this is not a ''domain'' object representing a change, but a ==Presenter== object in the Model-View-Presenter triad:
[[[type=prefer
DynamicWidgetChangePresenter
]]]
over
[[[type=not
DynamicWidgetChange
]]]
!!! Conclusion
Finding a good name is often difficult. However, nobody forces you to find it right upfront. And you can always change a name once you find a good one.
A good way to find good names is to write unit tests.
When you write unit tests, you are the first client of your code. You can then see if the names you chose let you write coherent and comprehensible little stories. You can explore the names you picked up and rename them.
!! About identifier format
While in the previous chapters we focused on identifiers or class variables, this chapter focuses on the form or format of such names. Indeed, writing software is about writing sentences that seamlessly integrate with existing ones. You do not want your program to be easily identifiable as an existing one. This is particularly true for multiple developer projects with common code ownership. Note that even if you are working on a single-person project, you will use existing class libraries and you do not want to have your 'ugly' code stepping out.
!!! Guideline: Avoid underscores and favor camel case
The form or format of identifiers is also important. Following the form promoted by a complete ecosystem will make your software more readable and acceptable by others.
This is true in any programming language.
Because Pharo and its ecosystem use camel case for class, variable, and method names.
Avoid using underscores.
Prefer
[[[type=prefer
timeOfDay
]]]
over
[[[type=not
timeofday
time_of_day
]]]
Prefer
[[[type=prefer
GnatXmlNode
]]]
over
[[[type=not
GNAT_XML_Object
]]]
Prefer
[[[type=prefer
releasedX
]]]
over
[[[type=not
released_X
]]]
Prefer
[[[type=prefer
| scaledX reducedX |
]]]
over
[[[
| scaled_X reduced_X |
]]]
When creating private low-level methods that bind to external C-libraries, you may want to use underscores to follow C conventions to ease tracing back the communication between libraries. In such a case, limit your use to carefully thought-out cases.
!!! Guideline: CamelCase fully identifiers
Use consistently camel case on the full length of an identifier.
Prefer
[[[type=prefer
readyForNextItem
]]]
over
[[[type=not
readyFornextitem
]]]
!!! Guideline: Shared variables start with uppercase
Begin class names, global variables, pool variables, and class variables with an uppercase letter. If the word is compound, then use camel case for the rest.
[[[
Point "Class"
Transcript "global variable"
PackageGlobalOrganizer "class variables"
]]]
!!! Guideline: Private variables start with lowercase
Begin instance variables, temporary variables, method parameters, and method selectors with lowercase. If the word is compound, then use camel case for the rest.
[[[
address
classExtensionSelectors
classTags
]]]
Prefer
[[[type=prefer
| dataset f xMatrix scale x |
]]]
over
[[[type=not
| dataset f Xmatrix scale X |
]]]
!!! Guideline: Convey language semantics
Remember ==MaxLimit==, ==maxLimit==, ==maxlimit==, and ==MAXLIMIT== are all different identifiers in Pharo.
[[[
| MaxLimit maxLimit |
MaxLimit := 10.
maxLimit := 20.
MaxLimit
>>> 10
]]]
Still, Pharo favors the camel case, so use it systematically for words. Wikipedia defines camel case as: Camel case (stylized as camelCase) is the practice of writing phrases such that each word or abbreviation in the middle of the phrase begins with a capital letter, with no intervening spaces or punctuation.
!!!!! For local variables.
Method parameters and instance variables, use
[[[type=prefer
maxLimit
]]]
instead of
[[[type=not
maxlimit
MAXLIMIT
]]]
!!!!! For classes, or shared variables
Use
[[[type=prefer
OrderedCollection
MaxLimit
]]]
instead of
[[[type=not
ORDEREDCOLLECTION
MAXLIMIT
]]]
@@note In a compound word, do not confuse a prefix or suffix with a word when trying to determine which words should begin with an uppercase letter. For example, some readers may think that the "c" in ==subclass== should be uppercase, but ==sub== is a prefix, not a word. When in doubt about prefixes and suffixes, check a dictionary.
Prefer
[[[type=prefer
superclass
]]]
over
[[[type=not
superClass
]]]
!!! Guideline: Method selectors start with lowercase
Prefer
[[[type=prefer
getMethodsNamesFromAClass: aClass
| methodsNames |
methodsNames := aClass selectors.
methodsNames do: [ :each | names add: each ]
]]]
over
[[[type=not
GetMethodsNamesFromAClass: aClass
| methodsNames |
methodsNames := aClass selectors.
methodsNames do: [ :each | names add: each ]
]]]
Also, in this example, the method selector is not good because method names are called selectors in Pharo. In addition in English ==methodsNames== should be written ==methodNames==. It should be ==gatherSelectorsFrom:== or something similar.
!!! Conclusion
This is a bit obvious, but as a developer you should follow language conventions.
This is not because Pharo is permissive that you should bend the rules. Doing so will only confuse you and other developers. So use camel case. Remember, private or local variables start with a lowercase letter, while class variables start with an uppercase letter.
!! About variable names
When choosing an appropriate name for a variable, the developer is faced with the decision: ''Should I choose a name that conveys semantic meaning to tell the user how to use the variable, or should I choose a name that indicates the type of object the variable is storing?''
There are good arguments for both styles. Let us see what the guidelines are that can help us find the right balance.
!!! Guideline: Favor semantic variables
A semantic name is less restrictive than a type name. When modifying code, it is possible that a variable may change type. But unless one redefines the method, the semantics of it will not change. We recommend using semantically meaningful names wherever possible.
In the example below, the typed variable does not indicate how it will be used, whereas the semantic variable does.
Prefer
[[[type=prefer
"Semantic variable"
newSizeOfArray := numberOfAdults size max: numberOfChildren size
]]]
over
[[[type=not
"Typed variable"
anInteger := numberOfAdults size max: numberOfChildren size
]]]
Note that semantic names can convey variable roles. Having more information is definitely useful for clients of the code.
Prefer
[[[type=prefer
"Semantic variable"
selectFrom: aBeginningDate to: anEndDate
]]]
over
[[[type=not
"Type variable"
selectFrom: aDate to: anotherDate
]]]
Finding a semantic name is not always as obvious as demonstrated above. There are cases in which choosing a descriptive semantic name is difficult.
!!! Guideline: Use typed variables to indicate API
Using a type variable is an interesting way to convey the API (set of messages) that the object held in the variable responds to.
Below ==aDictionary== conveys that the argument should have the same API as a Dictionary (==at:==, ==at:put:==)
Prefer
[[[type=prefer
properties: aDictionary
]]]
over
[[[type=not
properties: map
]]]
You may also want to stress specific types in an API reference to the interface that the object implements.
Prefer
[[[type=prefer
properties: aPuttable
]]]
over
[[[type=not
properties: aCollection
]]]
Suppose a String, a Symbol, and nil are valid for a parameter. A developer may be tempted to use the name ==aStringOrSymbolOrNil==.
You may be tempted to ==aString== or ==anObject==. ==anObject== is not really helping the developer who will have to use such variables. At the minimum, such a use should be accompanied by a comment that says, "anObject can be a String or aSymbol"
Some developers may argue that type variables should not refer to classes that do not exist. We disagree. As shown in the following guidelines, it is a lot better to indicate that an argument is a block expecting two arguments (hence a binary block) than to just mention a block. And this works even if there is no binary block class in the system.
Prefer
[[[type=prefer
inject: anObject into: aBinaryBlock
]]]
over
[[[type=not
inject: anObject into: aBlock
]]]
Note that for ==inject:into:== the best naming is to mix semantic and type naming as in
[[[type=prefer
inject: initialValue into: aBinaryBlock
]]]
!!! Guideline: Get the best from semantic and type variable
A good practice is to use a mixture of both semantic and typed variable names. Method parameter names are usually named after their type. Instances, classes, and temporary variables usually have a semantic name. In some cases, a combination of both can be given in the names.
Prefer
[[[type=prefer
ifTrue: trueBlock ifFalse: falseBlock
]]]
over
[[[type=not
ifTrue: block1 ifFalse: block2
ifTrue: action1 ifFalse: action2
]]]
The following are other examples of good names.
[[[
inject: initialValue into: aBinaryBlock
copyFrom: start to: stop
findFirst: aBlock ifNone: errorBlock
paddedTo: newLength with: anObject
]]]
!!! Guideline: Use semantics for state variable
State variable names (instance variables, class variables, or class instance variables) are usually semantic-based. A combination of semantic and type information can be really powerful, too.
Prefer
[[[type=prefer
"In class PhoneBook"
phoneNumber
name
]]]
over
[[[type=not
number
labelForPerson
]]]
!!! Guideline: Use predicate for Boolean
Use predicate clauses or adjectives for Boolean objects or states. Do not use predicate clauses for non-Boolean states.
Prefer
[[[type=prefer
alarmEnabled
isAlarmEnabled
]]]
over
[[[
alarm
]]]
!!! Guideline: Use common nouns and phrases
Use common nouns and phrases for objects that are not Boolean.
[[[type=prefer
"In class Vehicle..."
numberOfTires
numberOfDoors
"In class AlarmClock..."
time
alarmTime
"In class TypeSetter..."
page
font
outputDevice
]]]
Note that you can also use ==count== instead of ==numberOf== as in the following example:
[[[type=prefer
numberOfTires
tireCount
]]]
!! About method names
Method names in Pharo are called selectors. They are used in messages and are the main vehicle to convey adequate meaning to computation. From that perspective, it is really important to use them to convey the exact meaning of the computation they perform. The correct use of words and the design of selectors are then important.
!!! Guideline: Choose selectors to form short sentences
Choose method names so that someone reading the message can read the expression as if it were a sentence.
Prefer
[[[type=prefer
FileDescriptor seekTo: word from: self position
]]]
over
[[[type=not
FileDescriptor lseek: word at: self position
]]]
Write the test first, and make sure that your test scenario reads well.
!!! Guideline: Use imperative verbs for actions
Use imperative verbs for message which perform an action.
[[[type=prefer
transform
selectors do: [:each | self pushDown: each].
selectors do: [:each | class removeMethod: each]
]]]
Prefer
[[[type=prefer
aReadStream peek
]]]
over
[[[type=not
aReadStream word
]]]
Prefer
[[[
aFace lookSurprised
aFace beSurprised
]]]
over
[[[type=not
aFace surprised
]]]
[[[type=prefer
skipSeparators
]]]
Pay attention to the fact that some words can be interpreted as interrogatives, whereas you want to give them an imperative meaning.
For example, compare:
[[[
optimized
]]]
and
[[[
triggerOptimization
]]]
This is why using ==beOptimized== would be better than a simple ==optimized== and why ==isOptimized== is better for the interrogative form.
!!! Guideline: Prefix selector with as for conversion
When converting an object to another one, the convention is to prefix the class name of the target with ==as==.
[[[
anArray asOrderedCollection
]]]
Favor the use of existing classes.
!!! Guideline: Indicate flow with preposition
When a process state is going from one object to another, indicate the direction using meaningful names.
For example, ==flattenProperties:== is not a good name because it does not convey where the properties will be flattened.
[[[type=not
aConfiguration flattenProperties: aDictionary
]]]
Better names such as ==flattenPropertiesFrom:== and ==flattenPropertiesInto:== are much better because there are no ambiguities.
[[[type=prefer
aConfiguration flattenPropertiesFrom: aDictionary
aConfiguration flattenPropertiesInto: aDictionary
]]]
Here are more examples
[[[type=prefer
changeField: anInteger to: anObject
]]]
Prefer
[[[type=prefer
ReadWriteStream on: aCollection.
]]]
over
[[[type=not
ReadWriteStream for: aCollection.
]]]
Prefer
[[[
File openOn: stream
]]]
over
[[[type=not
File with: stream
]]]
Prefer
[[[type=prefer
display: anObject on: aMedium
]]]
over
[[[type=not
display: anObject using: aMedium
]]]
!!! Guideline: Indicate returned type
When a method returns an object (different from the receiver) and this object is not polymorphic with the receiver, it is important to mention it. Since Pharo is not statically typed, we can use the selector name to give such information to the sender of the message.
For example, the method ==characterSeparatorMethodSignatureFor:== of the pretty printer did not return a character but a block, as shown below:
[[[type=not
characterSeparatorMethodSignatureFor: aMethodNode
^ [
(self needsMethodSignatureOnMultipleLinesFor: aMethodNode)
ifTrue: [ self newLine ]
ifFalse: [ self space ] ]
]]]
Favor ==characterSeparatorMethodSignatureBlockFor:== over ==characterSeparatorMethodSignatureFor:== when the method returns block and not a character as ==characterSeparatorMethodSignatureFor:== indicates.
A much better design is to rewrite this method and its users to use a character.
Returning a block in such a situation is overkill.
The following method is corresponding to its name.
[[[type=prefer
characterSeparatorMethodSignatureFor: aMethodNode
^ (self needsMethodSignatureOnMultipleLinesFor: aMethodNode)
ifTrue: [ self newLine ]
ifFalse: [ self space ]
]]]
A good example is the API of the ==FileReference== class. The message ==pathString== indicates clearly that it returns the path as a string while to access the path object the message ==path== should be used.
!!! Guideline: Use interrogative form for testing
When interrogating the state of an object, use a selector beginning with a verb such as ==has==, ==is==, ==does==,...
Prefer
[[[type=prefer
isAtLineEnd
]]]
over
[[[type=not
atLineEnd
]]]
[[[type=prefer
aVehicle hasFourWheels
]]]
over
[[[type=not
aVehicle fourWheels
]]]
!!! Guideline: Avoid using parameter and variable name type
Avoid the parameter type or name in the method name if you are using typed parameter names.
Prefer
[[[type=prefer
fileSystem at: aKey put: aFile
]]]
over
[[[type=not
fileSystem atKey: aKey putFiIe: aFile
]]]
[[[
"for semantic-based parameter names"
fileSystem atKey: index putFile: pathName
]]]
[[[
"useful when your class has several #at:put: methods"
fileSystem definitionAt: aKey put: definition
]]]
Prefer
[[[type=prefer
aFace changeTo: expression
]]]
over
[[[type=not
aFace changeExpressionTo: expression
]]]
!!! Guideline: Use accessors or not
There are different schools about whether to use accessors. In his seminal book, Kent Beck discusses it in depth. Here we give a list of arguments for and against, and you should decide and follow the conventions of the project you work on. In any case, whether you use accessors or not, be consistent.
Arguments in favor of accessors:
- Accessors abstract from the exact state internal representation.
- Accessors may hide that values are derived or not.
- Subclasses may freely redefine the way accessors are implemented.
Arguments against accessor use:
- Accessors expose the internal state of an object.
- When the class is small, using accessors may increase the number of methods.
- Using refactorings, we can always easily introduce accessors.
!!! Guideline: Name accessors following variable name
When you use accessors, name them consistently:
- The getter is name as the variable it refers to.
- The setter is the same but with an extra terminating colon ==:==.
For getter, prefer
[[[type=prefer
tiles
^ tiles
]]]
over
[[[type=not
getTiles
^ tiles
]]]
Do not use ==get== or ==set== in accessor selectors!
!!!! Watch out