Topic 7: Design Patterns: Coupling, Cohesion and Separation of Concerns
Note: this topic is a lecture-only topic. There is no accompanying practical. This is to allow you all to catch up with Week 7, which only a few of you have completed.
Credits for this week's notes
- Many of today's notes are based on the originals by
a former member of staff John Flackett, but reworded
What is a design pattern?
A software pattern is:
- A documented solution to a commonly-encountered software design problem
- Helps allocates responsibilities for different tasks amongst your classes
- Following design patterns leads to reusable, maintainable code
General good design principles
Following patterns is a way to ensure that your code follows
general good design principles:
- Reusability: ability to reuse classes in other applications
- Maintainability: patterns enhance clarity of code, and therefore make it
easier to maintain
- Overall system robustness - patterns help avoid
situations where changes in one class lead to changes in the whole system
Low coupling
- Perhaps one of the most fundamental concepts in object-orientated design
- Low coupling means to minimise the dependency a class has on other classes
- This maximises its reusability
- It also makes the code easier to read, and, therefore, maintain
What is wrong with dependency on other classes?
- Harder to reuse
- If class A depends on class B, we cannot reuse it in a scenario where
we don't need class B
- Harder to understand in isolation
- It is harder to figure out the inner workings of a class which depends
on other classes, compared to a class which does not
- Changes in other classes force changes in the class
- If class A depends on class B, and the public methods in class B
change, we have to change class A
Minimise, do not necessarily eliminate, dependencies
- Obviously sometimes a class has to depend on another class
- notably if a class has attributes which are themselves classes
- e.g. a
Cinema
class which contains an array of three Screen
objects
- Here the
Cinema
depends on the Screens
, which is sensible
- However, if a
Cinema
depended on a CarPark
class, that would minimise the reusability of the Cinema
- What if you wanted to have a
Cinema
with no CarPark
?
- Think of real-world analogies, would the real world object the class
is modelling exhibit such a dependency?
Coupling example - College, Course and Student
- Scenario: software to manage enrolments onto courses offered by a college
- We need to write code to add a student to a course
High coupling design (bad)
Low coupling alternative
Student
no longer depends on College
in this example
class Student
{
ArrayList<Course> courses;
public Student()
{
courses = new ArrayList<Course>();
}
public void enrolOn(Course course)
{
courses.add(course);
}
}
- The
Student no longer needs the College
- Rather than passing in a course ID (which needs a
College
to look up the Course
with that ID) we pass in the Course
object directly to enrolOn()
Coupling and user interfaces - summary
- You should design your data classes (e.g.
University
or Student
in the week 3 exercise) so that they are independent of your GUI
- Allows the data classes to be reused with a different UI (console, web, desktop GUI, Android app)
- Avoid all references to the GUI in data classes
- Similarly, avoid console output (
System.out.println()
) as this couples your data classes to the console
- Use return values to indicate success or otherwise, e.g. boolean return values, or
null
to indicate an error if a method returns an object, instead
- By keeping the data classes free of UI code, we are following the software engineering principle of separation of concerns, which states that you should separate out different functions of the system (UI, data) into different classes
High Cohesion pattern
- A highly cohesive class is one where the responsibilities are
all closely related, and focused on one thing
- It has a small number of methods, with highly related functionality
- It does the job it's supposed to do, and nothing else!
Example of a high cohesion pattern
- Imagine a student records system application
- A low cohesion system might have one big
StudentRecordsSystem
class which does all the work
- A high cohesion system, by contrast, would have a series of
smaller classes, all focused on one particular job, e.g.:
-
Student
to represent a student
-
Course
to represent a course
-
University
to manage the system as a whole
- This means that the individual classes could be re-used in other applications, and by separating out code into individual classes, makes the application easier to read, and thus maintain
Another example of a high cohesion pattern: web communication
- Imagine a ticket-office booking application at a railway station which allows the member of staff to look up train times for a passenger
- The train times might be stored on a database on a web-server machine
- There are two parts to this:
- Communicate across the web to the web-server and obtain the train times data
- Interpret the train times (which will be delivered in a particular text-based format, such as JSON or XML) and integrate them in the GUI
- A low-cohesion design would have one big class for both these
tasks (web communication, interpreting the data)
- A high-cohesion design would have one class for the web communication and
one class for interpreting the data
- Why is this beneficial? Main point is we could use one without the other
- Imagine the train times were stored in a text file on the local machine, rather than on a
web-server. We could then use
the data-interpreting class without the web communication class
- Imagine that the format of the data changed (e.g. XML to JSON). We could then use the web communication class and substitute the original data-interpreting class with another
- Imagine that bookings were stored on the web-server. We could then use the web-communication class to make bookings as well as look up train times
Exercise
Look at these two github repositories:
https://github.com/nwcourses/coupling.git
https://github.com/nwcourses/cohesion.git
The former shows a high coupling design, the latter a low cohesion design. Redesign and change the code so they have low coupling and high cohesion, respectively. Also look for other problems with their design and correct them!