Skip to content

Commit 7e962ec

Browse files
few more fixes to why composability chapter
1 parent 90e06fa commit 7e962ec

File tree

2 files changed

+106
-56
lines changed

2 files changed

+106
-56
lines changed
-34 Bytes
Binary file not shown.

manuscript/190_Why_do_we_need_composability.md

Lines changed: 106 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -200,11 +200,7 @@ The power of composition
200200
201201
Moving creation of alarm instances away from the classes that use those
202202
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:
208204
209205
{lang="csharp"}
210206
~~~
@@ -221,50 +217,114 @@ new SqlRepository(
221217
We can do the same with our alarms. Let's say that we have a secure area
222218
that has three buildings with different alarm policies:
223219
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
231223
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:
233297
234298
{lang="csharp"}
235299
~~~
236300
new SecureArea(
237301
new OfficeBuilding(
238302
new DayNightSwitchedAlarm(
239-
new SilentAlarm(),
303+
new SilentAlarm("222-333-444"),
240304
new LoudAlarm()
241305
)
242306
),
243307
new StorageBuilding(
244308
new HybridAlarm(
245-
new SilentAlarm(),
309+
new SilentAlarm("222-333-444"),
246310
new LoudAlarm()
247311
)
248312
),
249313
new GuardsBuilding(
250-
new LoudAlarm()
314+
new HybridAlarm(
315+
new SilentAlarm("919"), //call police
316+
new LoudAlarm()
317+
)
251318
)
252319
);
253320
~~~
254321
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:
263323
264324
{lang="csharp"}
265325
~~~
266326
new DayNightSwitchAlarm(
267-
new SilentAlarm(),
327+
new SilentAlarm("222-333-444"),
268328
new LoudAlarm());
269329
~~~
270330
@@ -274,28 +334,32 @@ during night. However, instead of this combination, we might use:
274334
{lang="csharp"}
275335
~~~
276336
new DayNightSwitchAlarm(
277-
new SilentAlarm(),
337+
new SilentAlarm("222-333-444"),
278338
new HybridAlarm(
279-
new SilentAlarm(),
339+
new SilentAlarm("919"),
280340
new LoudAlarm()
281341
)
282342
)
283343
~~~
284344
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:
287346
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:
292356
293357
{lang="csharp"}
294358
~~~
295359
new DayNightSwitchAlarm(
296-
new NoAlarm(), // no alarm during day
360+
new NoAlarm(), // no alarm during the day
297361
new HybridAlarm(
298-
new SilentAlarm(),
362+
new SilentAlarm("919"),
299363
new LoudAlarm()
300364
)
301365
)
@@ -311,31 +375,17 @@ new GuardsBuilding(
311375
)
312376
~~~
313377
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!
320379
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.
327381
328382
Summary - are you still with me?
329383
--------------------------------
330384
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.
337388
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.
340390
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

Comments
 (0)