Sunday 23 August 2015

What Is It You Do Here?



Its important when we decide that we need a certain class that this class isn't just a collection of functionality and data but that it has a clear role within our system.
This role should come with clearly defined responsibilities where their is no question that they belong together and no ambiguity over how they should be used.
In order to help us achieve this role assignment and to properly encapsulate responsibilities we can use a set of principles that go by the name of the General Responsibility Assignment Software Principles or GRASP.
GRASP is made up of both patterns and principles that ensure we build a system made of clearly defined blocks interacting in a flexible and clear manner.
Controller
The first of these patterns is controller, the premise of the controller pattern is that a systems UI should be divorced from its logic.
In the controller pattern an applications UI detects user interaction and dispatches these events to a controller object, the controller processes these events and delegates responsibility to executing the required business logic to other classes further down chain.
Controllers are re-usable in any situation where a certain type of user interaction should be linked to a business process.
Creator
Object creation is a fundamental responsibility of any software system.
Creators are designated objects within a system who are responsible for creating the right object for the right task at the right time.
A creator may record instances of the classes it creates as well as supply dependencies along with any required initialisation.
Information Expert
An information expert acts as the single source of truth for either data within a system, functionality or both.
An information expert might provide access to a data source detailing the current state of a system or provide authoritative implementations for things such as encryption.
Indirection
A class providing indirection acts as intermediary between multiple other classes within a system.
When using this pattern classes who must co-operate in order to achieve a goal are isolated from knowledge of each by use of a class coordinating their activities.
The use of indirection increases the possibility of code re-use by weakening the ties between classes.
Protected Variations
Certain aspects of a system are subject to change and this may or may not be directly under the control of those of us that are building and maintaining it.
The use of the protected variations pattern isolates these volatile elements into their own sub-systems and protects dependent code from possible change by placing this functionality behind a well understood interface.
Protected variations should be used to isolate code that interacts with the outside world, in this case the outside world means code not written by us. This might the OS our system runs on top of or a 3rd party library we rely upon to perform a certain task.
Pure Fabrication
A large number of the classes in a system will represent some aspect of the problem domain the system is working in but quite often we are still left with functionality that needs to be implemented.
Classes that we create to fill this void are known as pure fabrication, they do not directly map to any outward facet of our system but are required to achieve the proper break down of roles and responsibilities.
A good example of pure fabrication is the service layer that exists in many system where certain repeatable, potentially re-usable functionality is implemented.
High, Low, Strong and Weak
The goal of the GRASP principles if to achieve high cohesion and low coupling, to have classes who are strong at their core but weak in their interaction.
A class with a well assigned role will exhibit high cohesion, all the inner parts will share a clearly visible common goal, their will be no fault lines where potentially smaller classes could be made.
It should be possible to work with a class with a well assigned role in such a way as to have no knowledge of this well defined internal mechanism, interfacing to such a class should be purely concerned with functionality not implementation.
A good example of this is via the effective use of polymorphism.
Polymorphism should be used to mask an area of our code base that may be subject to a large amount of variation. As a class that is dependent on this are of code I want to be protected from this potential instability and I want to delegate responsibility for navigating through it to someone else.
Polymorphism allows many classes that may be very different internally to masquerade behind the same interface leaving dependent classes in blissful ignorance of the complexity underneath, we achieve high cohesion and low coupling, creating strongly defined classes interacting in a weak manner.
So when your creating a class be sure you know its role and understand the responsibilities that should come with that role.
This isn't just about naming the class properly, its about creating an architecture that fits together just well enough to finish the puzzle but at a moments notice the same building blocks could be re-arranged to follow another pattern to produce a different outcome.          

No comments:

Post a Comment