Showing posts with label solid. Show all posts
Showing posts with label solid. Show all posts

Monday, October 12, 2009

Object-oriented myths

I think object-oriented programming techniques should be explained with a context and not shouted out loud as philosophical principles. Sometimes these techniques are misinterpreted, or exaggerated and used as a weapon against oop proponents.
Some popular myths about oop have risen in the programming world, particularly in the php continent where object orientation is still in its infancy. I want to debunk these legends to show that good oop is actually easier than you think. For instance, Dependency Injection is one of the best thing that can happen to your code.


"You should not expose public fields. Write getters and setters instead."
The point of encapsulation is to protect client classes from changes in the collaborators. In what is using a set*() method different from a public field, for the cause of encapsulation?
There are some classes which only responsibility is to maintain state. We encounter them in every project, being their names User, Post, CreditCard, String, Regex and so on. It is correct for this type of classes to have getters and setters to change their internal state and fulfill their requirements.
The other type of classes are services: stateless objects which do complex work, are not serializable and may have internal and external dependencies, towards other services or processes. These classes should not have any get*() or set*() method, since they are by definition stateless and the list of getters will break encapsulation. If I were a client class I would require a BookRepository object to search books, because I use it to... search books and not to change the underlying database calling BookRepository::setDb(Zend_Db $db). It's not my job to pass the collaborators in: I use the object only to search books.
If you find a complex service class which also has a state, the most useful thing to do is to break up the class in little ones because the responsibility is too high for a single unit. Testability and cohesion will improve.
So without setters, how I get collaborators references in, for example, a ServiceClass object private fields? You can create them directly in ServiceClass methods (bad) or realize Dependency Injection, passing them in the constructor of ServiceClass. A Factory will then call new, passing in the collaborators and encapsulating the creation. This practice leads us to the next myth.

"Why using a factory? There's no point in creating an object only to create another one."
The process of an object's creation can be intensive and complicated. If you use Dependency Injection (and you probably should as it is a standard practice for producing solid object-oriented code), every object needs its collaborators passed in the constructor, and the collaborators need other collaborators and so on. It's useful to abstract away this process in a Factory class whose methods perform the construction in one centralized place. Again, entity objects like Strings, Users and Posts are a bit special and using a Factory for them is not required, provided that they don't even had collaborators.
Though, if you find yourself creating a factory and subsequently, in the next line of code, using its methods to create a business object, chances are the design can be improved. The principle is that objects should ask for things, and not look for them.
Let's make the following assumptions:
  • You write object-oriented applications, so all your code is kept in classes. This is not necessarily true for hybrid languages like php.
  • You strive for minimizing the coupling of your components, respecting the Law of Demeter. You want to reuse classes and you want to be able to test them in isolation.
So start with examining where you are creating a BusinessObject in a class Client. The possible cases are three (actually two):
  • (not possible) BusinessObject has a longer lifecycle than Client, so you cannot create it in Client. Ask for it in the constructor.
  • BusinessObject has the same lifecycle of Client: it's a collaborator. So stop using a factory and simply ask for BusinessObject in the constructor of Client.
  • BusinessObject has a shorter lifecycle than Client: it must be created at a specific time, when some method on Client is called.
The latter case is the most interesting one. If BusinessObject is an entity, you can create it directly: there are no external dependencies to inject and there is little behavior to mock out in tests. If BusinessObject is a service class, with one or more external dependencies and more than a bit of behavior, you should indeed use a Factory to encapsulate its creation. But since you strive to reduce coupling, Client must not know if BusinessObject calls methods on an hundred different objects or returns prepackaged results. So you should pass in a Factory object in the constructor of Client: this solution resembles an Abstract Factory pattern, with the difference that we are focusing on lifetimes and not on extensibility and introduction of new subtypes.
In sum, all direct service object creation (use of the new operator) should be encapsulated in Factories, which can be used in the application bootstrap or passed in the constructor of the Client class if you need object creation as a business requirement (and the object is more complex than a String). Obviously if you are using setter injection it does not make a great difference, but the Api will be more cluttered. If your setters does not accept multiple calls to change collaborators, they have my blessings.
Warning: all the class names used are examples. For instance Client and BusinessObject are two classes that could also be called A and B. I often use Client to denote a class which is attempting to create an object, according to the GoF terminology. BusinessObject is a generic name for a class, more readable than Foo and Bar.

This post is becoming too long to not lose focus, so I'll continue tomorrow with the rest of object-oriented myths.
You may want to subscribe to the feed to discover the other myths as soon as they are published.

Tuesday, September 15, 2009

SOLID part 6 (bonus): how much solid Standard Php Library is?

This is a bonus part for the SOLID principles series. You can check it out to view the articles which explain all the principles or subscribe to the feed to be informed of new posts.

How that we have talked a lot about the five SOLID principles, it's time to apply them to a real project and see the good design ideas they introduce and what trade-offs are accepted in everyday coding. Since I have mainly a php audience, I have chosen the unique object-oriented library that the php core features natively: the Standard Php Library (SPL).

The Iterator interface and its implementations were the first Spl components to be included in php. This interface is very cohesive and it does not assume much about the source where items are coming from: for instance, in every implementation there's no need to write a method that counts the elements, since it is segregated in a Countable standard interface.
Iterator is extended by RecursiveIterator and OuterIterator, which manages respectively an iterator which items have children and a iterator who decorates another. These components adds only one or two methods to the parent interface and are also very cohesive: they are the hook for opening Spl to extensions.
The responsibilities of these classes are evenly distributed: the basic Iterator implementations have proven to be very useful (such as DirectoryIterator which produces the list of files contained in a folder), while other functionalities are kept in their own OuterIterator implementation, like LimitIterator and CachingIterator.
The primary use of Iterator (and of its parent interface Traversable) is in the foreach construct, where it can be passed as it were an array. The standard implementations can be swapped without problems in a foreach, and this means that Liskov Substitution Principle is more or less respected. What comes to mind is that different Iterators will return a different type of items, but php is a dynamic language and this precisation does not strictly affect the application of principle as long as the methods retain the same meaning and intent. To solve it completely the interface should use generics like in Java: Iterator.

Spl provides object-oriented way to access filesystem (SplFileInfo and similar ones), which is not a point of interest in this discussion since these classes are a wrapper to the basic functions such as opendir() and filesize(). The same goes for Exception and its tree of subclasses. The Serializable interface is also a good hook for extension of behavior, but we are not going to overanalyze the library.

What indeed raise attention is the SplObserver and SplSubject interfaces, which suggest a standard way to implement the observer pattern. These components are not useful in my opinion, and a comparison with static typed languages will show the difference.
In a static language like Java, when a parameter is passed to a method which have a determinate signature, only the contract defined by this signature could be used in the body of the method. This is the definition of SplObserver:
public function update(SplSubject $subject);
where SplSubject contains the methods attach(), detach() and notify(). Since the contract is defined from SplSubject interface, if php were a static language we could only call these three methods, which are totally unuseful to get the state of the subject (which can be thinked as some getXXX() methods to call on the subject); only the fact that php is a dynamic language, and does not complain if you call methods which can be unexistent,allows such a method call. Moreover, these interfaces force the developer to adopt a pull-style of Observer pattern even where a push-style would be simpler.
In sum, a design pattern, like the Observer one, is a common solution to a problem and should be implemented every time with different flavours, requiring different classes. SplObserver and SplSubject fail to satisfy the Dependency Inversion Principle since they won't decouple their implementors, that needs to know a large abstraction they leave out.

With the release of php 5.3, it has become clear that the goal of Spl is to provide fast components, implemented in C for particular purposes, and not a well designed object-oriented library. SplQueue is an example of this, along with its companion SplStack:
class SplQueue extends SplDoublyLinkedList implements Iterator, ArrayAccess, Countable { ...
SplDoublyLinkedList is a classic data structure, a general purpose list that is subclassed by SplQueue and SplStack. Since it implements Iterator and other Spl interfaces, their children inherits these features, but in my opinion it's not a good idea.
A queue can be accessed only one element at time; a stack has the same limitations, which are voluntarily imposed to incapsulate the internal data structure and limit the operations that can be done on it by a client class: I would rather have a SplQueue as an interface and not a concrete implementation, with just the enqueue() and dequeue() methods; SplStack as another interface with pop() and push(). SplDoublyLinkedList would be a generic implementation, which satisfies both interfaces.
Why I would prefer this design? Because it places functionalities in two compact interfaces; otherwise Interface Segregation Principle is not applied and no interfaces are even defined. SplQueue can't be a child of a SplDoublyLinkedList as it would be a violation of Liskov Substition Principle: a implementation of a queue can use a double linked list internally, but not inherit methods from it or it is not a queue anymore.
A benefit of a Queue interface would be real decoupling and its consequences: for instance, a mock of Queue can be used in testing to make sure only enqueue() and dequeue() are used, while mocking a SplQueue as it is will expose also all the other Iterator methods as empty ones, leading to strange results (and tying my high level modules to details instead of to a queue abstraction, infringing Dependency Inversion Principle).

I would say that Spl is a good product for the goals which it had been thinked for: a reliable and fast object-oriented layer, which can overload common constructs like count() and foreach; it has been a very successful feature of php. But its design could be vastly improved, also with application of SOLID principles.

The image at the top is an upside down library.
Did you like this series? Leave a comment or subscribe to the feed to stay in touch.

Friday, September 11, 2009

SOLID part 5: Dependency Inversion Principle

This is the fifth post in the SOLID principles series. You might want to checkout the previous entries.

High level classes should not depend on low level classes. Both should depend upon abstractions. Details should depend upon abstractions. Abstractions should not depend upon details.
The purpose of this principle is to enable decoupling of software modules. Managing dependencies is the key for isolate components to reuse later; decoupling is also important for maintenance and evolution since it stops changes in a cohesive piece of software from spreading all over an application.

The problem with dependency is the transitivity: a class depends not only on the other classes which uses directly, but also on these classes dependencies, and so on. It needs them to compile (in languages which require this process) and to work correctly; some kind of dependency between components is present in every software since object needs to work together and to know something about what methods they are going to invoke.
Let's see an example of what transitive dependency means. An object of the class Car depends on an GasEngine to move its passengers where they want to go. The GasEngine itself depends on a Gas class which models the fuel used.
The problem is that a Car should not have to use a forced fuel, like an object of the Gas class. In reality, we have electric cars or LPG ones. A Car depends upon a detail (GasEngine), while it should depend on abstraction. GasEngine is also on a lower level, so this dependency infringes both parts of the DIP.
A feasible refactoring is to introduce an interface (an abstraction) Engine which GasEngine implements and Car declares as a property. This is a powerful step since it break the dependency chain between the high level component (Car) and the low level one (GasEngine).

The Dependency Inversion in this example improves the software design since now Car and GasEngine, which are the details the principle is speaking of, depend on the abstraction Engine. Though, other issues arise when defining an abstraction: how should a Car have a reference to a GasEngine, which needs to work, while it only declares a field to contain an Engine?
There's more than one way to solve this construction problem:
  • Service Locator approach: classes like Car needs a singleton or a static registry where they pull the object they need. For instance, a method Registry::getEngine() returns an Engine whose concrete class needs to be chosen by configuration. This approach is
  • Dependency Injection: this more sophisticated, but simple at the same time, approach let Car declare its dependencies and have them injected by constructor or setters. This is the most widely used technique nowadays to achieve Inversion of Control.
Note that in the Service Locator case, Car would be totally insulated from the concrete GasEngine at compile time since the method invoked at construction includes an Engine in the signature and not a GasEngine. Though, nearly every class has a reference to a global object or static class, lying about its dependencies. Moreover, the Service Locator needs to be transported where the class are being reused.
Constructor Dependency Injection or Setter Dependency Injection is a cleaner choice since business classes have no idea of the framework or Factory which will construct the objects. Car simply declares its constructor:
public function __construct(Engine $engine);
In turn Engine will declars its needed collaborators in the constructor and the developer (or the DI framework) will learn from this signature what he must provide to build a complete object.

As always, let's do an analysis of testatiblity, confronting classes which respect or do not respect the DIP.
The initial Car class is not testable in isolation at all: it builds a GasEngine in its __construct() method and there's no way to replace it for testing purposes, expect reflection. The only thing we can test is a whole Car object, but imagine if tests were done this way in the real world... No one would know why a Car does not work when a problem arises.
The Car which uses a Service Locator is unit testable, since before running a test method we can configure the Service Locator to return a fake/mock Engine instance which follows a canned behavior. By the way, we have the hassle to configure it which can be a tedious work.
The Car which uses Dependency Injection is naturally testable: simply build a fake Engine and pass it in Car when the system under test is created. There is no need to configure other systems, which in this case get in the way instead of helping the developer.

I hope you have enjoyed this series on the five SOLID principles and that your perspective on designing a good application has shifted thanks to these pillars. New posts on object-oriented development will come in the future, you may want to subscribe to the feed to be informed of that.

The image at the top of the article is the hood open of a Ferrari F430 Spider. It depends on an Engine - a FerrariEngine maybe - and not on a GasEngine, since it can mount an ethanol one.

Thursday, September 10, 2009

SOLID part 4: Interface Segregation Principle

This is the fourth post in the SOLID principles series. You might want to subscribe to the feed to be notified of new posts.

Classes should not depend on interfaces that they not use.
The meaning of this phrase is to avoid tying a client class to a big interface if only a subset of this interface is really needed. Many times you see an interface which has lots of methods. This is a bad design choice since probably a class implementing it will infringe Single Responsibility Principle and for many other issues which arises when interfaces grow.

Let's see an example of a violation to Interface Segregation Principle. Since Car examples are becoming popular, we will continue with a vehicle example.
The class Car needs to have reference to its passengers: depending on the particular vehicle, it will transport 4 or more people and it must check during construction that it is not overloaded in weight and number of passengers since it would be not secure to drive over cartain limits. We have a class People ready who acts as a collection of Person objects; so, to load a car with People, the following method is used:
public function load(People $p);
We do not want to tie Car to People, which has in turn other dependencies, so we start with extracting an interface:
public function load(IPeople $p);
Hungarian notation is a smell that something. IPeople has many methods, the same of the People concrete class: getWeight(), remove($i), add(Person $p), and a bunch of other functions which Car will never call. What does Car need to know? This is the question that needs an answer: Car needs only a count() method to avoid being overloaded, and a getWeight() method to calculate acceleration and other physical variables. We put these two methods in an interface which will be implemented by People, deleting the awful IPeople component.
public function load(Passengers $p);
In this design, Car depends on the smallest possible interface, Passengers. An interface with a name that does not derive from its implementations is a sign that we are on a good path. Even if People has to be decoupled from Passengers interface, I strongly suggest to write an adapter implementing Passengers, which will wrap a People instance.
The important part is that Car is subjected only to variations to the method which really use, that is actually the minimum coupling introduced in the application. If such a small interface does not exist, it has to be created via extraction from the previous one.
Please note that the same issues are present in abstract base classes: although they cannot be broken down in tiny pieces since multiple inheritance is forbidden in most languages, providing every possible method in a base class can quickly transform it in a God object, and that's exaclt what we must avoid. Helpers and delegations can be used instead when not every subclass will actually need the parent's method.

Small interfaces respect the SRP, and can be combined in many ways. They can also be implemented by the same object, like many classes does in php with Countable and Iterator. Fortunately these two interfaces are separated and allow an Iterator who does not know its length to work.
The advantage of less polluted interfaces is also in simplicity of implementation: less methods result in less tests and less interaction which can cause bugs; also, the classes derived will be much more cohesive as they take only one responsibility to manage from the chosen interface.

The testing point of view results in a easy win for small interfaces. How do you know what to mock when a 20+ methods interface or base class is passed in the constructor of the system under test? When the interface has two or three methods, there is little choice in what can be called by the SUT and you can produce mocks without having to know the internals of it (which of the 20+ methods will be called at what time). Probably in such a case you will end up using a concrete class instead of a mock, transforming your unit tests in integration ones.
You will be satisfied of keeping methods to a minimum while producing a self-shunting also for testing purposes.

I think you are now at a good point in our journey in the principles of object-oriented development. Interfaces are a great decoupling tool and should be used at their full potential. Stay tuned for the next part, on Dependency Inversion (and not Injection).

The image at the top is a Swiss Army Knife. How would you define an interface for one and someone will implement it? Do you prefer a real toolbox?

Wednesday, September 09, 2009

SOLID part 3: Liskov Substitution Principle

This is the third part of the series about the SOLID principles, which governs good object-oriented development. You may want to subscribe to the feed to be updated on new issues of this series.
Check out the previous parts if you missed them.

Every function or method which expects an object parameter of class A must be able to accept a subclass of A as well, without knowing it.
The meaning of this principle is that every time you write a subclass, you have to make sure it is substitutable in every place where you use an instance of the original class. The subclass must respect the contract of the superclass, without changing a behavior in such a way that would be impossible to recognize the new instance as belonging also to the superclass.
The name of this principle came from Barbara Liskov, professor at MIT.

The principle is about bad use of inheritance: long chains of inheritance will probably break it as the leaves of the class tree will likely try to reuse code without being proper subclasses. Let's see an example.

In your application you are writing the (overused example) Car class, and suddenly you feel the need for a Motorcycle class to use along for urban traffic simulation. Since there is much in common in these two classes, like the Engine, Brakes and the correlated calculations and wiring in Car's code, you write Motorcycle as a subclass of Car, redefining the methods where its behavior obviously disagree with Car's one, such as getTires() since it has only two tires instead of four.
This redefinition is a violation of LSP: a feasible method checkUp(Car $c) will be broken if it expects four tires to blow up. The language will allow us to pass a Motorcycle instance since it is an instance of Car also, but it is not really a subtype of Car since its contract is less restrictive of Car's one.

A common rule to discover is the subclassing choice is right is the instanceof operator consistency, available in many object-oriented languages. This operator will return true if the variable under test is built from the chosen class or from a subclass ($a instanceof Car). This means in our example $yamaha instanceof Car will return true, which we know it's a bad behavior of the application since its domain model slides away from reality.
The refactoring choice to fix this design it's to abstract away the common behavior of Car and Motorcycle in a Vehicle base (and possibly abstract) class, or to stop using inheritance altogether. Inheritance is widely overrated in the object-oriented programming and composition should be favored in it.

Inheritance is the right choice when an Is-a relationship is present, and in the majority of design should be limited to two or three levels without harm. The original paper from Uncle Bob uses Square and Rectangle as examples. It's obvious that a Rectangle could not be subclassed from a Square, so the reverse is tried. But the contract of Rectangle say that we can change height and width independently (setHeight() and setWidth()), while even if we redefine the methods we cannot in a Square as changing a side will change the other to maintain Square's properties.
There are other designs which will solve this particular problem, like writing immutable objects, but my choice would be to write an helper class which contains common logic and do not chain Square and Rectangle in inheritance at all.
From a more theoretical point of view, preconditions of methods cannot be strenghtened by a subclass while postconditions cannot be weakened: the Square redefinition of setHeight() to modify also the width does not respect the stronger post conditions of Rectangle's method to leave width unchanged; thus, a subclassing is not feasible. This is a bit of design by contract which helps us to detect a bad inheritance strategy: in a particular sense, a Square is not a Rectangle, although it indeed is in a geometric definition; since a Rectangle is identified by an entity whose sides couples can vary indipendently, a Square that forces all four to remain equals is not an instanceof Rectangle.

I hope you're starting to grasp the principles and see the connections between them: to follow religiously one of these first three you're going to apply also the others.
Stay tuned for the next principle explanation, the Interface Segregation Principle.

The image on the top is a photograph of Loris Capirossi on the Ducati Desmosedici, during a MotoGp race. Is a RacingMotorcycle a Motorcycle?

Tuesday, September 08, 2009

SOLID part 2: Open/Closed Principle

The Open/Closed principle is the second of the SOLID principles which governs object-oriented development, formulated by Uncle Bob in the 90s*.
Check out the previous part if you missed Single Responsibility Principle.

Classes and methods should be open for extension but closed for modification.
The meaning of this principle is that when a requirement is added to your application, you should be able to handle it without modifying old source files (supposing you have one class per file), but only by adding subclasses and new implementations and changing the configuration.
Why it is important to be open for extension? Change is the keyword in software development and software components are inserted in new projects and environments every day. What makes them useful is the ability for a developer to write adapter and subclasses to get a job done without reinventing the wheel, but only by smoothing and tuning it.
Why it is important to be closed for modification? Because when a closed unit is fully tested and deployed, if it's not modified it can't break. This is a simple consequence of not changing what already works: it will continue to work.

These are some examples of patterns and techniques that help you follow the OCP:
  • programming to an interface, not an implementation: a oo interface is closed for modification, and multiple implementations can take new behavior and possibilities into the software system;
  • Template Method: some empty methods are called during an algorithm execution to allow overriding by a subclass, providing it hooks in the code flow;
  • Iterator Pattern: abstracts away the mechanics of an iteration to let other iterators substitute it in particular conditions.
Every good pattern resemble the OCP. The Iterator Pattern has been particularly developed in php: we have Iterator and IteratorAggregate instances which can be swapped in foreach construct; but we also have a bunch of subclasses that extends the core behavior: FilterIterator, CachingIterator, LimitIterator, RecursiveIteratorIterator...
The strategy of extending behavior without cluttering a base unit is one of object oriented pillars: subclassing, decorators and helpers are only strategies to keep responsibilities out of the base class, which can quickly became a God one if too much code fills it.

Let's take the Car example from part 1 and see if we are violating OCP: a Car is composed by an Engine, a Trasmission and the Brakes. What if we need to move the Car without using a gas or diesel engine? We can design a ElectricEngine subclass which will substitute the former Engine without breaking its contract. The same can be done with CarboniumBrakes or BremboBrakes.
What if the Engine has a method called injectGas()? This violates encapsulation and affects OCP also. An electric engine would not use gas as a combustible and thus the contract is not closed for modification: the problem is in the abstraction of an Engine which is in reality an abstraction for a gas engine. What can be done it's decoupling the Engine with an interface Propulsor which will contain a GasEngineElectronicBoard which translates to the Engine the commands from the driver.
Now if we want to put in an ElectricEngine, we will provide a Propulsor instance which governs the ElectricEngine in some way, with electronic or analogic circuits. Take the time to think about possible changes and how they affect your software systems: find a way to add features without have to resort to old classes modification.

Encapsulation is a checkpoint to achieve OCP: the more you keep private and hide from the public view, the less is assumed in the behavior an interface or an abstract base class. This leads you to write implementations which exposes very few methods and can perform work in unthinkable ways: the Propulsor interface can be implemented by a ElectricEngine but also from a ReactionEngine or a SteamEngine or a HyperDriveEngine if you are a science fiction passionate. The only requirement is that it manages to accelerate the Car and choosing this abstraction makes this parts totally interchangeable.
The Propulsor interface is an example of closure: not only it is not editable since clients expects determinate features, but we cannot add methods because they will break the implementors, especially if their code is not under our control.

Summarizing, Open/Closed Principle forces decoupled and extendable code: it's another milestone towards a maintainable and functional object-oriented application.

You may want to subscribe to the feed to be notified of new articles in this series. The picture at the top is the Usb symbol: nowadays every device that uses Usb and provide a proper driver can extend the behavior of a pc without having to open the case: webcams, printers, scanners...
*
Rogerio Liesenfeld points out in the comments that the original formulation of this principle comes from Bertrand Meyer.

Monday, September 07, 2009

SOLID part 1: Single Responsibility Principle

The Single Responsibility Principle (SRP) is the first of the five SOLID principles which governs the object-oriented design. They have been formulated by Robert Martin aka Uncle Bob and are universally recognized as good architecture.

This post is the first in a five-part series which will give an introduction to every principle. You may want to subscribe to Invisible to the eye feed if you want to stay tuned on new issues of this series.

There can be only one reason for a class to change.
This is the common formulation of SRP, the Divide et impera of software development. The meaning of this phrase is that when you're adding features to your application, two different, unrelated stories to implement should not affect the same class. What is implied by this principle is that every class you design should have only one responsibility, and often in software development change is the unit of measure: since your class Car have only one responsibility, only a change in the requirements of this responsibility should make you open the source file and modify the code of the class.

Here are some examples of classes which does not follow the SRP:
  • a Car which fills its fuel tank by itself
  • a CreditCard which knows how to charge itself (the classic Misko Hevery's example)
  • a CreditCardProcessor which knows how to do http requests
  • a Comment which sends mails
The scope of your application decides when responsibility should be splitted among more than one class: if braking and accelerating are complex processes, they should be factored out from the Car class, in two classes such as BrakeSystem and Engine respectively (or maybe Transmission, which will decouple Engine from the Car). Abstracting what is really complex is the point of this principle: if you need only to save the number of wheels in a Car you probably do not need to write a Wheel class whose instances will be placed in the Car one: it is a unuseful layer of indirection. If your wheels needed to know when they are totally consumed or are going to explode due to external solicitation, then they would become instanceof Wheels and would earn a class on their own: this is an example of refactoring. Until then, you are building a model of reality and you should not include a customer eye's color in their bank account information.

A rule of thumb to make this decision is to describe the behavior of a class with a single phrase. If you cannot formulate such a phrase or it contains the word and or other connectives, probably the behavior should be divided in more simple parts.
Particularly, entity classes (in respect to service ones) should have only the purpose to maintain state along with their business, inherent behavior. That's why a Comment instance contains name, text and mail fields but not a method warnAuthor(), which sends mails, to call on subsequent comments insertion.
The advantages of religiously following SRP are:
  • classes are more reusable, since you can pick only the one which implements a specific behavior without reusing a God class which can do everything and depends on everything.
  • classes are shorter and simpler to maintain.
  • design is more fine-grained, because every bit of behavior has its place in a small class. Knowing what you are going to modify it's half of the work in object-oriented development: with smaller classes, it will be obvious where a change belongs.
  • writing small and cohesive classes leads to testable code, while writing God classes leads to a non-testable ball of mud. A maintainable system is composed by a graph of object whose classes depends on each other for collaboration: this picture is obtained with dependency injection techniques.
Think of a complex object, like your computer: it composed by a moltitude of objects, but it respects the SRP; every component has its responsibility, from the resistor to the monitor, and complex components are built by wiring together smaller and cohesive ones.
Now that we are inspired by the principle, let's factor out some responsibilities from our example classes:
  • a GasStation or a GasPump will fuel a Car's tank
  • a CreditCardProcessor should take care of charging a CreditCard
  • a CreditService will insulate CreditCardProcessor from the Internet
  • a CommentRepository will call a Mailer or a CommentSubscriber whenever a new Comment instance is add to it
I hope you start to consider Single Responsibility Principle whenever you are designing a component of your application. It's a bit difficult to grasp at the exact level of detail, but if you find the right level of abstraction that is needed, the benefit will be beautiful and simple code; which, for a complex application, is a great result.

* The object in the image at the top of the post is Bicycle Wheel from Marcel Duchamp, a surrealist artist. It is an artwork, but to me is a perfect symbol of an object that does too much.

Featured post

A map metaphor for architectural diagrams

It is a (two-dimension) representation of a pipe. The map is not the territory , but in software engineering terms they are models of it....

Popular posts