You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: Chapters/Counter/Counter.md
+5-8Lines changed: 5 additions & 8 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -62,9 +62,8 @@ Creating a class requires four steps. They consist basically in editing the clas
62
62
You should get the following class definition.
63
63
64
64
```
65
-
Object subclass: #Counter
66
-
instanceVariableNames: 'count'
67
-
classVariableNames: ''
65
+
Object << #Counter
66
+
slots: { #count };
68
67
package: 'MyCounter'
69
68
```
70
69
@@ -157,17 +156,15 @@ Writing tests is an important activity that will support the evolution of your a
157
156
To define a test case we will define a class that inherits from `TestCase`. Therefore define a class named `CounterTest` as follows:
158
157
159
158
```
160
-
TestCase subclass: #CounterTest
161
-
instanceVariableNames: ''
162
-
classVariableNames: ''
159
+
TestCase << #CounterTest
163
160
package: 'MyCounter'
164
161
```
165
162
166
163
167
164
Now we can write a first test by defining one method. Test methods should start with _test_ to be automatically executed by the TestRunner or when you press on the icon of the method. Now to make sure that you understand in which class we define the method we prefix the method body with the class name and `>>`.
168
165
`CounterTest>>` means that the method is defined in the class `CounterTest`.
169
166
170
-
Define the following method. It first creates an instance, sets its value and verifies that the value is correct. The message `assert:equals:` is a special message verifying if the test passed or not.
167
+
Define the following method. It first creates an instance, sets its value, and verifies that the value is correct. The message `assert:equals:` is a special message verifying if the test passed or not.
171
168
172
169
```
173
170
CounterTest >> testCountIsSetAndRead
@@ -310,7 +307,7 @@ If you run it, it will turn yellow indicating a failure \(a situation that you a
310
307
#### Define an initialize method
311
308
312
309
313
-
Now we have to write an initialization method that sets a default value of the `count` instance variable. However, as we mentioned the `initialize` message is sent to the newly created instance. This means that the `initialize` method should be defined at the instance side as any method that is sent to an instance of `Counter`\(like `increment`\) and `decrement`. The `initialize` method is responsible to set up the default value of instance variables.
310
+
Now we have to write an initialization method that sets a default value of the `count` instance variable. However, as we mentioned the `initialize` message is sent to the newly created instance. This means that the `initialize` method should be defined at the instance side as any method that is sent to an instance of `Counter` (like `increment`) and `decrement`. The `initialize` method is responsible to set up the default value of instance variables.
314
311
315
312
Therefore at the instance side, you should create a protocol `initialization`, and create the following method \(the body of this method is left blank. Fill it in!\).
Copy file name to clipboardExpand all lines: Chapters/NewCounterMaterial/Exo-Counter.md
+23-9Lines changed: 23 additions & 9 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -40,9 +40,8 @@ The lower pane of the Browser should now be open with a tab showing the template
40
40
You should get the following class definition:
41
41
42
42
```
43
-
Object subclass: #Counter
44
-
instanceVariableNames: 'count'
45
-
classVariableNames: ''
43
+
Object << #Counter
44
+
slots: { #count };
46
45
package: 'MyCounter'
47
46
```
48
47
@@ -73,6 +72,8 @@ The class we have defined has one instance variable named `count`, and we're goi
73
72
74
73
And so there is no other mechanism to access our instance variable from outside of our counter than by sending a message to the object. What must do is define a method that returns the value of the instance variable. Such methods are called _getter_ methods. So, let's define an accessor method for our instance variable `count`.
75
74
A method is usually placed into a _protocol_. These protocols are just a group of methods - they have no meaning in Pharo, but they do convey important information to the readers of your class. Although protocols can have any name, Pharo programmers follow certain conventions when naming protocols. If you define a method and are not sure what protocol it should be in, first take a look through existing code and see if you can find an appropriate protocol that already exists.
75
+
76
+
76
77
### Create a method
77
78
78
79
Now let us create the getter method for the instance variable `count`. Start by selecting the class `Counter` in a Browser, and make sure you are editing the instance side of the class (i.e., we define methods on _instances_ of our class) by selecting the instance side tab. Then define your method.
@@ -110,21 +111,23 @@ c count
110
111
```
111
112
112
113
The setter method does not currently exist, so as an exercise create the method `count:` such that, when invoked on an instance of `Counter`, the instance variable is set to the argument of the message. Test your method by evaluating the example above in a Playground.
114
+
113
115
### Define a Test Class
114
116
115
117
Writing tests - whether you do it before or after you write your code - isn't really optional these days. A collection of well-written tests will support the evolution of your application, and give you confidence that your program does the things you expect it to do. Writing tests for your code is a good investment; test code is written once and executed a million times. For example, if we turned the example above into a test we could have checked automatically that our new setter method is working as expected.
118
+
116
119
Our test cases, written as methods, need to live inside a test class that inherits from `TestCase`. So we define a class named `CounterTest` as follows:
117
120
118
121
```
119
-
TestCase subclass: #CounterTest
120
-
instanceVariableNames: ''
121
-
classVariableNames: ''
122
+
TestCase << #CounterTest
122
123
package: 'MyCounter'
123
124
```
124
125
125
126
Now we can write our first test by defining a method. Test methods should start with _test_ to be automatically executed by the Test Runner or to get the little clickable circle next to the method name that lets you run the test.
126
127
Figure *@FirstGreenTest@* shows the definition of the method `testCountIsSetAndRead` in the class `CounterTest`.
127
-

128
+
129
+

130
+
128
131
Define the following method for our test case. It first creates an instance of a `Counter`, sets its value and then verifies that the value has been set. The message `assert:equals:` is a message implemented in our test class. It verifies a fact (in this case that two objects are equal), and will fail the test if the fact isn't true.
Pharoers frequently use the notation `ClassName >> methodName` to identify the class to which a method belongs. For example, the `count` method we wrote above in our class `Counter` would be referred to as `Counter >> count`. Just keep in mind that this is not _exactly_ Pharo syntax, but more like a convenient notation we use to indicate "the instance method `count` which belongs to the class `Counter`".
144
+
141
145
From now on, when we show a method in this book, we will write the name of the method in this form. Of course, when you actually type the code into the browser, you don't have to type the class name or the `>>`; instead, you just make sure that the appropriate class is selected in the class pane.
142
146
Verify that the test passes by executing either pressing the circle icon in front of the method (as shown by Figure *@FirstGreenTest@*) or using the Test Runner.
143
147
As you now have your first green test, it's a good time to save your work.
148
+
149
+
144
150
### Saving your code as a git repository with Iceberg
145
151
146
152
Saving your work in the Pharo image is good, but it's not ideal for sharing your work or collaborating with others. Much of modern software development is mediated through git, an open-source version control system. Services such as GitHub are built on top of git, providing places where developers can work together building open source projects - like Pharo!
147
153
Pharo works with git through the tool **Iceberg**. This section will show you how to create a local git repository for your code, commit your changes to it, and also push those changes to a remote repository such as GitHub.
154
+
148
155
#### Open Iceberg
149
156
150
157
Open Iceberg through the **Sources** menu, or by hitting `Cmd-O,I`.
151
-

158
+
159
+

160
+
152
161
You should now see something similar to Figure *@EmptyIceberg@* which shows the top-level Iceberg pane. It shows the Pharo project, and a few other projects that also come with your image, and indicates that it could not find a local repository for them by showing 'Local repository missing'. You do not have to worry about the Pharo project or having a local repository if you do not want to contribute to Pharo.
153
162
We're going to create a new project of our own.
163
+
154
164
#### Add and configure a project
155
165
156
166
Press the button `Add` to create a new project. Select 'New Repository' from the left and you should see a configuration pane similar to the one in Figure *@CreateProject@*. Here we name our project, declare a directory on our local disk where the project's source should be saved, and also a subdirectory in the project itself which will be used to keep the Pharo code in - conventionally this is the `src` directory.
This time the test will turn _yellow_, indicating a test failure - the test ran fine, but the assertion did not pass. This is different to the _red_ tests we've seen so far, where the tests have failed because an error occurred (when a method has not been implemented, for instance).
240
+
230
241
### Define an initialize method
231
242
232
243
Now we have to write an initialization method that sets a default value of the `count` instance variable.
233
244
In Pharo, when creating a new object sending the message `new` to a class, the newly created instance is sent a message `initialize`. This gives the opportunity to the instance to initialize itself.
234
-
Therefore we will define a `initialize` method that will correctly initialize the default value of a counter.
245
+
246
+
Therefore we will define an `initialize` method that will correctly initialize the default value of a counter.
235
247
236
248
Since the `initialize` message is sent to a new instance, it means that the `initialize` method should be defined on the _instance side_, just like any method that is sent to an instance of `Counter` (`increment` and `decrement`). The `initialize` method is responsible for setting up the default values of instance variables.
237
249
And so, on the instance side of `Counter`, and in the `initialization` protocol, write the following method (the body of this method is left blank. Fill it in!).
@@ -250,6 +262,7 @@ As always, save your work before moving on to the next step.
250
262
251
263
We just discussed how the `initialize` method is defined on the _instance side_ of our class, as it is responsible for altering an instance of `Counter`. Now let's take a look at defining a method on the _class side_ of a class. Class methods will be executed as a result of sending messages to the class itself, rather than to instances of the class. To define the method on the class, we need to toggle the Code Browser over to the class side by selecting **Class side**.
252
264
Define a new instance creation method called `startingAt:`. This method receives an integer as an argument and returns a new instance of `Counter` with the count set to the specified value.
265
+
253
266
What do we do first? Why, we define a test of course:
254
267
255
268
```
@@ -266,6 +279,7 @@ Counter class >> startingAt: anInteger
266
279
```
267
280
268
281
Here we see the notation for identifying a _class side_ method in our text: `ClassName class >> methodName` just means "the class side method `startingAt:` on the class `Counter`".
282
+
269
283
What does `self` refer to here? As always, `self` refers to the object that the method is defined in, and so here it refers to the `Counter` class itself.
270
284
Let's write another test just to make sure that everything is working:
Copy file name to clipboardExpand all lines: Chapters/OOPNutshell/OOPNutshell.md
+5-4Lines changed: 5 additions & 4 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -360,7 +360,7 @@ What the diagram shows is that we have:
360
360
- directories that have a name and can contain other files or directories. Here we get the `manga`, `comics`, `oldcomics`, and `belgiumSchool` directories. Directories can be nested: `comics` contains three repositories. The `belgiumSchool` directory contains `tintinEtLesPicaros`.
361
361
362
362
363
-

363
+

364
364
365
365
366
366
### Studying a first scenario
@@ -370,14 +370,15 @@ In the rest of this book, we will code such examples as tests that can automatic
370
370
For now, it would make the discourse too complex, so we just use little code examples.
371
371
372
372
We create two directories.
373
+
373
374
```
374
375
| dComics dOldComics dManga |
375
376
dComics := MFDirectory new name: 'comics'.
376
377
dOldComics := MFDirectory new name: 'oldcomics'.
377
378
```
378
379
379
380
380
-
We add the oldcomics folder to comics and we check that the parent children relationship is well set.
381
+
We add the oldcomics folder to comics and we check that the parent-child relationship is well set.
381
382
382
383
```
383
384
...
@@ -407,7 +408,7 @@ dComics parent
407
408
```
408
409
409
410
410
-
Here we verify that `dOldComics` is comprised in the children of `dComics`.
411
+
Here we verify that `dOldComics` is comprised of the children of `dComics`.
What do we see with this definition: it is a kind of recursive definition. The name of a directory is in fact the concatenation (here we just add in the stream but this is the same. ) of the name of its parents (as shown in Figure *@InstancesRecursion@*).
580
581
581
-
Similar to a recursive function navigating a structure composed of similar elements (like a linked-list or any structure defined by induction), each parent receives and executes another time the `printOn:` method and returns the name for its part.
582
+
Similar to a recursive function navigating a structure composed of similar elements (like a linkedlist or any structure defined by induction), each parent receives and executes another time the `printOn:` method and returns the name for its part.
Copy file name to clipboardExpand all lines: Chapters/SimpleLan/Simple-LAN-Definition.md
+3-3Lines changed: 3 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -9,7 +9,7 @@ From an object-oriented point of view, it is really interesting because it shows
9
9
10
10
You will define step by step an application that simulates a simple Local Area Network (LAN). You will create several classes: `LNPacket`, `LNNode`, `LNWorkstation`, and `LNPrintServer`. We start with the simplest version of a LAN. In subsequent exercises, we will add new requirements and modify the proposed implementation to take them into account.
11
11
12
-

12
+

13
13
14
14
15
15
### Creating the class `LNNode`
@@ -39,7 +39,6 @@ To help you write tests, we will define a test class.
39
39
40
40
```
41
41
TestCase << #LNNodeTest
42
-
slots: {};
43
42
package: 'SimpleLAN'
44
43
```
45
44
@@ -51,6 +50,7 @@ Create a subclass of `Object` called `LNNode`, with two instance variables: `nam
51
50
52
51
53
52
#### Exercise: Accessors
53
+
54
54
Create accessors and mutators for the two instance variables. Document the mutators to inform users that the argument passed to `name:` should be a `Symbol`, and the arguments passed to `nextNode:` should be a `LNNode`.
55
55
Define the following test to validate such a simple behavior.
56
56
@@ -108,7 +108,7 @@ Note that:
108
108
109
109
##### A little example.
110
110
111
-
The following snippet shows basic behavior of an open LAN composed of two nodes, Mac and PC1.
111
+
The following snippet shows the basic behavior of an open LAN composed of two nodes, Mac and PC1.
Copy file name to clipboardExpand all lines: Chapters/SnakesAndLadders/SnakesAndLadders.md
+4-4Lines changed: 4 additions & 4 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -39,7 +39,7 @@ game play
39
39
40
40
Since we want to focus on the game logic, you will develop a textual version of the game and avoid any lengthy user interface descriptions.
41
41
42
-
The following is an example of game execution: Two players are on the first tile. The board contains two ladders, \[2->6] and [7->9], and one snake [5<-11].
42
+
The following is an example of game execution: Two players are on the first tile. The board contains two ladders, [2->6] and [7->9], and one snake [5<-11].
43
43
44
44
Jill rolls a die, throws a 3, and moves to the corresponding tile.
45
45
Jack rolls a die, throws a 6, moves to the corresponding tile, and follows its effect, climbing the ladder at tile 7 up to tile 9.
Now propose an implementation of the method `movePlayer: aPlayer distance: anInteger`.
678
-
You should get the destination tile for the player, remove the player from its current tile, add it to the destination tile and change the position of the player to reflect its new position.
678
+
You should get the destination tile for the player, remove the player from its current tile, add it to the destination tile, and change the position of the player to reflect its new position.
679
679
680
680
```
681
681
SLGame >> movePlayer: aPlayer distance: anInteger
@@ -688,7 +688,7 @@ We suspect that when we will introduce ladder and snake tiles, we will have to r
688
688
#### About our implementation
689
689
690
690
691
-
The implementation that we propose below for the method `movePlayer: aPlayer distance: anInteger` is not as nice as we would like it to be. Why? Because it does not give a chance to the tiles to extend this behavior and our experience tells us that we will need it when we will introduce the snake and ladder. We will discuss that when we will arrive there.
691
+
The implementation that we propose below for the method `movePlayer: aPlayer distance: anInteger` is not as nice as we would like it to be. Why? Because it does not give a chance to the tiles to extend this behavior and our experience tells us that we will need it when we will introduce the snake and ladder. We will discuss that when we will arrive there.
#### super does not have to be the first expression
951
951
952
952
953
-
Now we show you our definition of `printInsideOn:` for the class `SLSnakeTile`. Why do we show it? Because it shows you that an expression invoking an overriden method can be placed anywhere. It does not have to be the first expression of a method. Here it is the last one.
953
+
Now we show you our definition of `printInsideOn:` for the class `SLSnakeTile`. Why do we show it? Because it shows you that an expression invoking an overridden method can be placed anywhere. It does not have to be the first expression of a method. Here it is the last one.
0 commit comments