The Context

The inspiration for this post came to me mainly because of the discussion in comments which I had on Sebastian’s Malaca blog. We had a little debate there about the famous „getters and setters” duo. In my opinion, you can peacefully write software, completely avoiding them (with the obvious exception for DTO’s).

My interlocutors had a little different point of view – they stated, that although you should avoid them, there are some cases when G&S are a must. I came up with classic example of a Human, which we can image as:

As we see in this code, you don’t have any getters or setters, all possible actions of a Human are expressed by the methods attached to its class.

So fine, so good, but what happens if we want to model the following situation – a Human walking down the street gets hit by a car and suffers from an injury ? If the injury is serious, he will be taken to the hospital. In the hospital he will be medically examined by some (let us hope) skilled medical personnel. If the injury was real worse, the situation may require to perform an operation on an open heart.

Unfortunately our properly encapsulated Human doesn’t expose its internal state of which the heart is part of. So, what can (and does) a surgeon ? He just simply breaks the encapsulation by cutting the chest and gets his job done.

If we wanted to translate (model) the actions of a surgeon in Java code, we would write:

In the above example, the Java Reflection API was used to break the encapsulation of the instance of a Human class. If we dive into the body of the method „operate”, we could see that an exception could be thrown – the IllegalAccessException. This is because the JVM could be configured to ban such usages of the reflection API. In the real world, the family could also disagree to operate the patient.

So much for drawing parallels between the real world and Java code. As we see, we cannot rely on this particular feature of Java Reflection API. If we want to save our innocent Human being, we have two options:

  1. write getters and setters for the organs
  2. add a new method to the Human class, to let him be operated

Let write some code to see, how could it be implemented. We will begin with the getter/setter case:

The code from the original example was altered and some getters/setters were added. The added G&S are actually some kind of extension points.

The positive of this approach is that anyone is free to do anything with our code, so when another scenario emerges – for example our Human gets started in an Olympics game, we could support this scenario without the need of altering the class itself.

The negative side of this approach is directly connected with the positive side – if someone is free to do anything with our code, than he could do something that he isn’t supposed to. An example of such situation would be the code, which sets the heart property to null – all other code depending of this property will simply fail.

After examining the G&S case, lets focus on the the second one. The code:

In this approach we added another method named „letBeOperated” which is responsible for passing in a controlled manner some of the organs to an instance of a Treatment class which effectively performs the treatment of the patient.

We have avoided getters and setters, so our Human is once again perfectly encapsulated and nobody messes with the inner state of our object without our control.

We could think, that we have found a silver bullet, a technique which solves all of our problems. But one must be very cautious, because this one also has its drawbacks.

The first thing, you may notice is that, our Human class violates the Single Responsibility Principle – what has working and walking to do with being operated? The second thing is a consequence of the first one – if a class handles more than one responsibility it will have multiple dependencies and also it’s code base will grow very fast. How can we fix this obvious flaw?

If we closely look at the use cases that we try to implement, we may notice,  that the Human class appears in different contexts. One context is for walking down the street and the other is for being hospitalized. This is a very fundamental thing, that many developers seem simply to ignore, although it is very important for proper problem modeling.

Let us organize our problem domain knowledge:

  • we have two contexts: Walking Context and Hospitalization Context
  • in the Walking Context, a Human plays a role of a Walker and in the Hospitalization Context he plays a role of a Patient

How can we refactor our code then ? I see mainly two patterns applicable:

  • Data Context Interaction (DCI)
  • Bound Context (from DDD)

The Bound Context pattern refers mainly to general systems design, so I will just describe it without examples:

Applying the Bound Context pattern to an application means, that we divide the application into highly cohesive modules. Each module is a bound context – a set of classes, functions etc that are specialized over one „domain”. Because these software building blocks are very specialized, there may be a situation that we will have two classes sharing the same name in different bound contexts. These two classes will have different behavior that is custom to their bound context, neither will they be interchangeable, nor even share the same inheritance tree.

As I said, the Bound Context pattern is more a system design pattern and it mostly applies to some bigger systems. In most cases, the Data Context Interaction pattern should be sufficient.

So can we implement the DCI pattern using our Human-Walker-Patient example ?

We can do it in two ways, depending of what language we are using, lets start with the hard way – in Java 😉

The code:

How to use it:

Explanation:

We have created two interfaces – Walkable and Treatable, they represent the actions that we want an object to be capable of. Then we created two classes – Walker and Patient, that implement the mentioned interfaces. Both classes extend the base Human class to provide support for both interfaces.  The Walker and Patient classes are just simple wrappers but they share the same inheritance hierarchy to indicate, that they are specializations of a Human class (and also avoid the use of getters/setters ;)).

As I said before, the Java implementation is the hard way. Now let’s have some joy from programming, the next example will be in Scala.

The whole code:

Explanation:

The Scala example has the same architecture as Java one, but the devil lies in the details :). The first thing that you may notice is the absence of the Walker and Patient classes – they are not needed, because traits (Scala’s aproach to interfaces) already have an implementation of the needed methods.

The second thing that will surely confuse you (if you are not a Scala hacker ;)) is the main method. You may ask yourself – where have all the conversions go and how can you call methods on a class that doesn’t physically have them ?

The answer is very simple – we are using a very clever technique called views. Thanks to these two tiny bits of code above (starting with implicit def) we tell the compiler to implicitly mixin both traits into our sample Human class.

Leave a Reply

Informuj mnie o odpowiedziach poprzez e-mail. Możesz również subskrybować wpis bez zostawiania komentarza.