CSC/ECE 517 Fall 2007/wiki3 3 ab
Topic
Take the principle of Separation of Responsibility and catalog the information on it available on the Web. Find good descriptions and good, concise, understandable examples. Tell which you consider the best to present to a class.
Introduction
Separation of Responsibility
In Object-Oriented Design (OOD), Separation of Concern is well known as a principle or process of breaking computer program codes into different components that have little coupling with each other and have strong cohesion. Following this principle, a class should have one clearly defined responsibility. In separation of concern, there are two important concepts: Separation of Responsibility and Separation of knowledge (information and environment hiding).
Separation of responsibility states that specific functionality or specific actions are assigned to design components and are not distributed throughout a design [3], in other words, each individual object should have as few responsibilities as possible, ideally one responsibility per object.
Why we should use separation of responsibility
Before we discuss the separation of responsibility, let us have a look at the following class [4]:
class Employee { public Money calculatePay() public void save() public String reportHours() }
This class has to be changed when the following aspects are changed.
- The business rules having to do with calculating pay.
- The database schema.
- The format of the string that reports hours.
That is to say, the class Employee is impacted by three completely different responsibilities. Every time the accounts decide to change the format of the hourly report, or every time the DBAs make a change to the database schema, as well as every time the managers change the payroll calculation, we have to change the class Employee.
Separation of responsibility can avoid this trouble. When using separation of responsibility, these three functions calculatePay(), save(), reportHours() will be separated into different classes so that they can change independently without influencing others. Using separation of responsibility, programs are easy to maintain and change.
Principle of Separation of Responsibility
There are several principles of Separation of Responsibility [1]:
- Single Responsibility Principle (SRP). Different responsibilities should be divided among different objects. In other words, one object should have only one responsibility in ideal situation. We can only say A class should have only one reason to change. We want to focus classes, functions, etc. so that there is only one reason for them to change. This is why many people separate their application into layers. For example, Ruby on Rails has three layers, View, Model and Controller. Different layers handle different rules. When we change database, we only need to change models. And when we change business logics, we only need to change Controllers.
- Encapsulation. One class should be responsible for knowing and maintaining a set of data, even if that data is used by many other classes. In other word, Data should be kept in only one place.
- Expert pattern. The object that contains the necessary data to perform a task should be the object that manipulates the data.
- The Dry principle. Don't Repeat Yourself. Code should not be duplicated. A given functionality should be implemented only in one place in the system.
Examples
We consider the Java example for Principle of Separation of Responsibility is the best example to describe Separation of Responsibility.
Java Example for Principle of Separation of Responsibility
There is an Java example of principle of Separation of responsibility[5].
public void createCustomer(Map requestParameters) { Customer customer = new Customer(); customer.setName = requestParameters.get("name"); //Check if a customer was already registered with that name if (customerService.getCustomerByName(customer.getName()) != null) { System.out.println("Customer already exists"); return; } customer.setShoppingCart(new ShoppingCart()); customerService.save(customer); }
The method name is create. While others viewing the the code will expect to have a create function. But instead it has three functionalities, creates a customer, checks if it's already exist and then save it. It should divide these functionalities into 4 methods.
- bindValidateAndSave is the application method. It tells what to do rather than how's done.
- bindCustomer binds and adds new shoppingCart.
- validateCustomer checks whether the customer exists.
- saveCustomer saves the customer.
The last method saveCustomer has only 1 line of code, why were we let it become a individual method? It is show how it works although it might not improve the readability of the code.
Each methods only doing one thing and have only one responsibility. The blind, validate and save separately in different methods.
Example for Single Responsibility Principle(SRP)
Single responsibility principle is to say that an object should only has one reason to change. If there are more than one reason to change the object then we should split the object into smaller object which has one responsibility.[10]
We have an object to keep an email message as below.
// single responsability principle - bad example interface IEmail { public void setSender(String sender); public void setReceiver(String receiver); public void setContent(String content); } class Email implements IEmail { public void setSender(String sender) {// set sender; } public void setReceiver(String receiver) {// set receiver; } public void setContent(String content) {// set content; } }
The IEmail interface has two responsibilities.
- One would be the use of the class in some email protocols such as pop3 or imap. If other protocols must be supported the objects should be serialized in another manner and code should be added to support new protocols.
- Another one would be for the Content field. Even if content is a string maybe we want in the future to support HTML or other formats.
We can create a new interface and class called IContent and Content to split the responsibilities, having only one responsibility for each class give us a more flexible design.
- adding a new protocol causes changes only in the Email class.
- adding a new type of content supported causes changes only in Content class.
// single responsability principle - good example interface IEmail { public void setSender(String sender); public void setReceiver(String receiver); public void setContent(IContent content); } interface Content { public String getAsString(); // used for serialization } class Email implements IEmail { public void setSender(String sender) {// set sender; } public void setReceiver(String receiver) {// set receiver; } public void setContent(IContent content) {// set content; } }
The Single Responsibility Principle represents a good way of identifying classes during the design phase of an application and it reminds you to think of all the ways a class can evolve. A good separation of Responsibility is done only when the full picture of how the application should work is well understood.
Example for Encapsulation principle and Expert pattern
One class should be responsible for knowing and maintaining a set of data, even if that data is used by many other classes. In other words, Data should be kept in only one place.
For example:
public class Person { private String firstName; private String lastName; private String suffixName; public String getFirstName() { return firstName; } public void setFirstName(String _x) { firstName = _x; } public String getLastName() { return lastName; } public void setLastName(String _x) { lastName = _x; } public String getSuffixName() { return suffixName; } public void setSuffixName(String _x) { suffixName = _x; } ... }
In this example, class Person is responsible for the data firstName, lastName, suffixName. Person uses methods getFirstName and setFirstName to get or set data. Other classes that need to use these data have to call these methods of objects declared as Person, they can not use or modify these data directly.
The object of class Person contains the necessary data firstName to do the task of getting the first name. The object of class Person is also the only object that manipulates the data.
References
- Elegance and classes
- Separation of concerns
- A Logical Theory of Interfaces and Objects
- Curly's Law: Do One Thing
- Java by Experience
- OO Programming By Example
- TDD Design Starter Kit – Responsibilities, Cohesion, and Coupling
- Benefits of the Three-Tiered Architecture
- Single-Responsibility Principle
- OOD - Single Responsibility Principle