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
Moving creation of alarm instances away from the classes that use those
202
202
alarms brings up an interesting problem - if an object does not create
203
-
the objects it uses, then who does it? I have cleverly avoided
204
-
answering this question so far, so let's tackle this issue now. A
205
-
solution is to make some special place in the code that assembles a
206
-
system from loosely coupled objects. We saw this already as Johnny was
207
-
explaining composability to Benjamin. He used the following example:
203
+
the objects it uses, then who does it? A solution is to make some special places in the code that are only responsible for composing a system from context-independent objects[^moreonindependence]. We saw this already as Johnny was explaining composability to Benjamin. He used the following example:
208
204
209
205
{lang="csharp"}
210
206
~~~
@@ -221,50 +217,114 @@ new SqlRepository(
221
217
We can do the same with our alarms. Let's say that we have a secure area
222
218
that has three buildings with different alarm policies:
223
219
224
-
- Office building - the alarm should be silent during the day (to keep
225
-
office staff from panicking) and loud in the night, when guards are
226
-
on patrol.
227
-
- Storage building - as it is quite far and the workers are few, we
228
-
want to trigger loud and silent alarms at the same time
229
-
- Guards building - as the guards are there, no need to notify them
230
-
with silent alarm, but a loud alarm is desired
220
+
- Office building - the alarm should silently notify guards during the day (to keep office staff from panicking) and loud during the night, when guards are on patrol.
221
+
- Storage building - as it is quite far and the workers are few, we want to trigger loud and silent alarms at the same time
222
+
- Guards building - as the guards are there, no need to notify them. However, a silent alarm should call police for help instead, and a loud alarm is desired as well
231
223
232
-
The composition of objects fulfilling these needs could look like this:
224
+
Note that besides just triggering loud or silent alarm, we have a requirement for a combination ("loud and silent alarms at the same time") and a conditional ("silent during the day and loud during the night"). we could just hardcode some `for`s and `if-else`s in our code, but instead, let's factor out these two operations (combination and choice) into separate classes implementing the alarm interface.
225
+
226
+
Let's call the class implementing the choice between two alarms `DayNightSwitchedAlarm`. Here is the source code:
227
+
228
+
{lang="csharp"}
229
+
~~~
230
+
public class DayNightSwitchedAlarm : Alarm
231
+
{
232
+
private readonly Alarm _dayAlarm;
233
+
private readonly Alarm _nightAlarm;
234
+
235
+
public DayNightSwitchedAlarm(
236
+
Alarm dayAlarm,
237
+
Alarm nightAlarm)
238
+
{
239
+
_dayAlarm = dayAlarm;
240
+
_nightAlarm = nightAlarm;
241
+
}
242
+
243
+
public void Trigger()
244
+
{
245
+
if(/* is day */)
246
+
{
247
+
_dayAlarm.Trigger();
248
+
}
249
+
else
250
+
{
251
+
_nightAlarm.Trigger();
252
+
}
253
+
}
254
+
255
+
public void Disable()
256
+
{
257
+
_dayAlarm.Disable();
258
+
_nightAlarm.Disable();
259
+
}
260
+
}
261
+
~~~
262
+
263
+
Studying the above code, it is apparent that this is not an alarm *per se*, e.g. it does not raise any sound or notification, but rather, it contains some rules on how to use other alarms. This is the same concept as power splitters in real life, which act as electric devices but do not do anything other than redirecting the electricity to other devices.
264
+
265
+
Next, let's use the same approach and model the combination of two alarms as a class called `HybridAlarm`. Here is the source code:
266
+
267
+
{lang="csharp"}
268
+
~~~
269
+
public class HybridAlarm : Alarm
270
+
{
271
+
private readonly Alarm _alarm1;
272
+
private readonly Alarm _alarm2;
273
+
274
+
public HybridAlarm(
275
+
Alarm alarm1,
276
+
Alarm alarm2)
277
+
{
278
+
_alarm1 = alarm1;
279
+
_alarm2 = alarm2;
280
+
}
281
+
282
+
public void Trigger()
283
+
{
284
+
_alarm1.Trigger();
285
+
_alarm2.Trigger();
286
+
}
287
+
288
+
public void Disable()
289
+
{
290
+
_alarm1.Disable();
291
+
_alarm2.Disable();
292
+
}
293
+
}
294
+
~~~
295
+
296
+
Using these two classes along with already existing alarms, we can implement the requirements by composing instances of those classes like this:
233
297
234
298
{lang="csharp"}
235
299
~~~
236
300
new SecureArea(
237
301
new OfficeBuilding(
238
302
new DayNightSwitchedAlarm(
239
-
new SilentAlarm(),
303
+
new SilentAlarm("222-333-444"),
240
304
new LoudAlarm()
241
305
)
242
306
),
243
307
new StorageBuilding(
244
308
new HybridAlarm(
245
-
new SilentAlarm(),
309
+
new SilentAlarm("222-333-444"),
246
310
new LoudAlarm()
247
311
)
248
312
),
249
313
new GuardsBuilding(
250
-
new LoudAlarm()
314
+
new HybridAlarm(
315
+
new SilentAlarm("919"), //call police
316
+
new LoudAlarm()
317
+
)
251
318
)
252
319
);
253
320
~~~
254
321
255
-
The parts I would like to turn your attention to are: `HybridAlarm` and
256
-
`DayNightSwitchedAlarm`. These are both classes implementing the `Alarm`
257
-
interface, at the same time taking `Alarm` implementations as their
258
-
constructor arguments. For example, `DayNightSwitchedAlarm` uses
259
-
different alarm during the day and another during the night. Note that
260
-
this allows us to change the alarm behaviors in many interesting ways
261
-
using the parts we already have, but composing them together
262
-
differently. For example, we might have, as in the above example:
322
+
Note that the fact that we implemented combination and choice of alarms as separate objects implementing the `Alarm` interface allows us to define new, interesting alarm behaviors using the parts we already have, but composing them together differently. For example, we might have, as in the above example:
263
323
264
324
{lang="csharp"}
265
325
~~~
266
326
new DayNightSwitchAlarm(
267
-
new SilentAlarm(),
327
+
new SilentAlarm("222-333-444"),
268
328
new LoudAlarm());
269
329
~~~
270
330
@@ -274,28 +334,32 @@ during night. However, instead of this combination, we might use:
274
334
{lang="csharp"}
275
335
~~~
276
336
new DayNightSwitchAlarm(
277
-
new SilentAlarm(),
337
+
new SilentAlarm("222-333-444"),
278
338
new HybridAlarm(
279
-
new SilentAlarm(),
339
+
new SilentAlarm("919"),
280
340
new LoudAlarm()
281
341
)
282
342
)
283
343
~~~
284
344
285
-
Which would mean that we use silent alarm during the day, but a
286
-
combination of silent and loud during the night.
345
+
Which would mean that we use silent alarm to notify the guards during the day, but a combination of silent (notifying police) and loud during the night. Of course, we are not limited to combining a silent alarm with a loud one only. We can as well combine two silent ones:
287
346
288
-
Additionally, if we suddenly decided that we do not want alarm at all
289
-
during the day, we could use a special class called `NoAlarm` that would
290
-
implement `Alarm` interface, but have both `Trigger` and `Disable`
291
-
methods do nothing. The composition code would look like this:
347
+
{lang="csharp"}
348
+
~~~
349
+
new HybridAlarm(
350
+
new SilentAlarm("919"),
351
+
new SilentAlarm("222-333-444")
352
+
)
353
+
~~~
354
+
355
+
Additionally, if we suddenly decided that we do not want alarm at all during the day, we could use a special class called `NoAlarm` that would implement `Alarm` interface, but have both `Trigger` and `Disable` methods do nothing. The composition code would look like this:
292
356
293
357
{lang="csharp"}
294
358
~~~
295
359
new DayNightSwitchAlarm(
296
-
new NoAlarm(), // no alarm during day
360
+
new NoAlarm(), // no alarm during the day
297
361
new HybridAlarm(
298
-
new SilentAlarm(),
362
+
new SilentAlarm("919"),
299
363
new LoudAlarm()
300
364
)
301
365
)
@@ -311,31 +375,17 @@ new GuardsBuilding(
311
375
)
312
376
~~~
313
377
314
-
Noticed something funny about the last few examples? If not, here goes an explanation:
315
-
in the last few examples, we have twisted the behaviors of our application in wacky ways, but all
316
-
of this took place in the composition code! We did not have to modify
317
-
any other existing classes! True, we had to write a new class called
318
-
`NoAlarm`, but did not need to modify any other code than the
319
-
composition code to make objects if this new class work with objects of existing classes!
378
+
Noticed something funny about the last few examples? If not, here goes an explanation: in the last few examples, we have twisted the behaviors of our application in wacky ways, but all of this took place in the composition code! We did not have to modify any other existing classes! True, we had to write a new class called `NoAlarm`, but did not need to modify any other code than the composition code to make objects if this new class work with objects of existing classes!
320
379
321
-
This ability to change the behavior of our application just by changing
322
-
the way objects are composed together is extremely powerful (although
323
-
you will always be able to achieve it only to certain extent),
324
-
especially in evolutionary, incremental design, where we want to evolve
325
-
some pieces of code with as little as possible other pieces of code
326
-
having to realize that the evolution takes place..
380
+
This ability to change the behavior of our application just by changing the way objects are composed together is extremely powerful (although you will always be able to achieve it only to certain extent), especially in evolutionary, incremental design, where we want to evolve some pieces of code with as little as possible other pieces of code having to realize that the evolution takes place. This ability can be achieved only if our system consists of composable objects, thus the need for composability - an answer to a question raised at the beginning of this chapter.
327
381
328
382
Summary - are you still with me?
329
383
--------------------------------
330
384
331
-
Although we started with what seemed to be a repetition from basic
332
-
object oriented programming class, using a basic example. It was
333
-
necessary though to make a fluent transition to the benefits of
334
-
composability we eventually introduced at the end. I hope you did not
335
-
get overwhelmed and can understand now why I am putting so much stress
336
-
on composability.
385
+
We started with what seemed to be a repetition from basic object oriented programming course, using a basic example. It was necessary though to make a fluent transition to the benefits of composability we eventually introduced at the end. I hope you did not get overwhelmed and can understand now why I am putting so much stress on composability.
386
+
387
+
In the next chapter, we will take a closer look at composing objects itself.
337
388
338
-
In the next chapter, we will take a closer look at composing objects
339
-
itself.
389
+
[^skipfunc]: I am simplifying the discussion on purpose, leaving out e.g. functional languages and assuming that "pre-object oriented" means procedural or structural. While this is not true in general, this is how the reality looked like for many of us. If you are good at functional programming, you already understand the benefits of composability.
340
390
341
-
[^skipfunc]: I am smiplifying the discussion on purpose, leaving out e.g. functional languages and assuming that "pre-object oriented" means procedural or structural. While this is not true in general, this is how the reality looked like for many of us. If you are good at functional programming, you already understand the benefits of composability
391
+
[^moreonindependence]: More on context-independence and what these "special places" are, in the next chapters.
0 commit comments