Skip to content

Separation of Concerns

Estimated time to read: 4 minutes

Overview

A concern is a very general notion, it is anything that matters in providing a solution to a problem. Separation of Concerns applies through Object-Oriented Modelling and Programming.

Some concerns may involve:

  • What information the implementation represents
  • What it manipulates
  • What gets presented at the end

Example

Let's look at the behaviour of a dog. Some basic behaviours that a dog can do are walking, running, speaking and eating. While these behaviours are easy to identify and abstract, we need to ask ourselves which behaviours can the dog do all on its own? And which ones need help from something or someone else? If we examine the eating behaviour more closely, we might come up with something like this.

Screenshot 2022-07-14 at 14.37.22.png

UML tells us that the dog has food, which it knows how to eat. We can tell the dog to eat food by giving it food, but is this the correct way of modelling the situation? Who is actually giving the dog the food? Does the dog always have food to eat, or is the dog given food to eat by an owner?

Concerns

In reality, a dog would need an owner to feed it. The dog knows how to eat food, but it doesn't know anything about the foods it's eating until its owner feeds it. We need to separate two concerns, the action of eating and the action of providing food. This can be done by introducing a dog owner class.

Screenshot 2022-07-14 at 14.38.21.png

In the new design, the dog class only knows how to eat food. The dog owner class is the one that knows how to get the dog food and how to give it to the dog. We have removed the concern of how to get food away from the dog and let the dog owner handle that issue. In using separation of concerns here, we should only be encapsulating behaviours and attributes within classes that are concerned with the said behaviours and attributes. This helps us to create a modular system where individual classes can easily be swapped in and out without having to rewrite a large portion of our code.

Code Example

public class SmartPhone {
 private byte camera;
 private byte phone;

 publicSmartPhone(){..}

 public void takePhoto(){..}
 public void savePhoto(){..}
 public void cameraFlash(){..}

 public void makePhoneCall(){..}
 public void encryptOutgoingSound(){..}
 public void decipherIncomingSound(){..}
}

In this snippet of code, the SmartPhone class, has attributes called camera and phone along with all of the associated behaviours. However, there is low cohesion in the SmartPhone class, because we have behaviours that are not related to each other. The camera behaviours do not need to be encapsulated with the behaviours of the phone in order for the camera to do its job.

Furthermore the smartphone components do not offer us any modularity. We cannot access the camera or the phone separately if we were to build another system that required only one or the other. We cannot replace our current camera with a different camera, or replace it with a completely different object, without removing the code for the camera completely in this class.

Code Example Changes

The SmartPhone class has two concerns:

  • Act as a phone
  • Be able to use the built-in camera to take pictures

The concerns can be encapsulated and made into their own functional classes.

The SmartPhone class will reference the new classes, so that it can act as a co-ordinator of both the Camera and the Phone. The SmartPhone provides access to these new classes, but does not need to know how they work.

UML

Screenshot 2022-07-14 at 14.44.48.png

Interfaces

public interface ICamera{
 void takePhoto(){..}
 void savePhoto(){..}
 void cameraFlash(){..}
}

public interface IPhone{
 void makePhoneCall(){..}
 void encryptOutgoingSound(){..}
 void decipherIncomingSound(){..}
}

public class FirstGenCamera implements ICamera{
 ...
}

public class TraditionalPhone implements IPhone{
 ...
}

The code above has interfaces for both the Camera and the Phone have been created, then two new classes have been defined which implement the applicable interface.

SmartPhone Class

public class SmartPhone {
 private ICamera myCamera;
 private Iphone myPhone;

 public SmartPhone(ICamera aCamera, IPhone aPhone){
  this.mycamera = aCamera;
  this.myphone = aPhone;
 }

 public void userCamera(){
  return this.mycamera.takePhoto();
 }

 public void usePhone(){
  return this.myphone.makePhoneCall();
 }
}

This allows our smartphone to provide the functions of both the camera and the phone, while keeping the functionalities of either one separate and hidden from each other. The camera and phone know nothing about the other but are still composed by this SmartPhone class.

Also, we can have a smartphone constructor with camera and phone as parameters. Then we can create a new instance of the SmartPhone class by passing in instances of classes that implemented the camera and phone interfaces.

Note that we leave it as a separate responsibility of who will instantiate the appropriate phone and camera objects. The smartphone class does not actually need to know. Finally, the smartphone class has methods that forward the responsibilities of using the camera and phone to these objects.