Related
After reading about SOLID in a few places, I was having trouble mapping between explanations with different vocabularies and code. To generalize a bit, I created the diagrams below, and I was hoping that people could point out any 'bugs' in my understanding.
Of course, feel free to reuse/remix/redistribute as you'd like!
I think your diagrams look quite nice, but I'm afraid that I couldn't understand them (particularly the interface one), so I'll comment on the text.
It's not really clear to me what you mean by layer, in the Open/closed I thought you might mean interface, but the interface and dependency items suggest you don't mean that.
Open/closed : actually your text from the Liskov item is closer to describing open/closed. If the code is open for extension, we can make use of it (by extending it) to implement new requirements, but by not modifying the existing code (it's closed for modification) we know we wont break any existing code that made use of it.
"Only depend on outer layer" - if this means only depend on an interface not the implementation, then yes, that's an important principle for SOLID code even though it doesn't map directly to any of the 5 letters.
Dependency inversion uses that but goes beyond it. A piece of code can make use of another via its interface and this is has great maintainability benefits over relying on the implementation, but if the calling code still has the responsibility for creating the object (and therefore choosing the class) that implements the interface then it still has a dependency. If we create the concrete object outside the class or method and pass it in as an interface, then the called code no longer depends on the concrete class, just the interface
void SomeFunction()
{
IThing myIthing* = new ConcreteThing();
// code below can use the interface but this function depends on the Concrete class
}
void SomeFunctionDependencyInjectedVersion(IThing myIthing*)
{
// this version should be able to work with any class that implements the IThing interface,
// whether it really can might depend on some of the other SOLID principles
}
Single responsibility : this isn't about classes intersecting, this is about not giving a code more than one responsibility. If you have a function where you can't think of a better name than doSomethingAndSomethingElse this might be a sign its got more than one responsibility and could be better if it was split (the point I'm making is about the "and" in the name even when the "somethings" are better named).
You should try to define that responsibility so that the class can perform it entirely, (although it make may use of other classes that perform sub-responsibilities for it) but at each level of responsibility that a class is defined it should have one clear reason to exist. When it has more than one it can make code harder to understand, and changes to code related to one responsibility can have unwanted side-effects on other responsibilities.
Iterface segregation: Consider a class implementing a collection. The class will implement code to add to the collection or to read from it. We could put all this in one interface, but if we separate it then when we have consuming code that only needs to read and doesn't need to add to the collection then it can use the interface made of the reading methods. This can make the code clearer in that it shows quickly that the code only needs those methods, and, if we've injected the collection by interface we could also use that code with a different source of items that doesn't have the ability to add items
(consider IEnumerable vs ICollection vs IList)
Liskov substitution is all about making sure that objects that inherit from an interface/base class behave in the way that the interface/base class promised to behave. In the strictest interpretation of the original definition they'd need to behave exactly the same, but that's not all that useful. More generally its about behaving in a consistent and expected way, the derived classes may add functionality, but they should be able to do the job of the base objects (they can be substituted for them)
The open/closed principle states that a class should be open for extension but closed for modification.
I thought that the modification part referred strictly to altering the source code of the base class. But I had an argument with someone saying that this also involves overriding methods from the base class.
It this interpretation correct?
Virtual methods allow replacing behavior of a base class in a derived class, without having to alter the base class and this means you adhere to the Open/Closed principle since you can extend the system without having to modify existing code.
Base classes (that are not purely abstract) however, tend to violate the Dependency Inversion Principle, since the derived class takes a dependency on the base class, which is a concrete component instead of being an abstraction. Remember, the DIP states that:
High-level modules should [...] depend on abstractions.
Besides this, base classes tend to violate the Interface Segregation Principle as well in case they define multiple public (or protected) methods that are not all used by the derived type. This is a violation of the ISP because:
no client should be forced to depend on methods it does not use
"I thought that the modification part referred strictly to altering the source code of the base class."
You thought right.
There is a plethora of ways to make a class extensible and allowing one to inherit from it is one of them. The keyword extend is even used in a few languages to enable inheritance which makes it quite obvious that we aren't modifying, we are extending...
Whether inheritance is the right solution to extensibility or not is another concern, but usually it is not though. Composition should be the preferred way to make classes extensible (e.g. Strategy, Observer, Decorator, Pipes and Filters, etc...)
An override is a lot like a callback that anyone can register. It's like:
if (IsOverridden) CallCallback();
else DefaultImplementation(); //possibly empty
In that sense there is no modification. You are just reconfiguring the object to call the callback instead of doing the default behavior.
It's just like the click event of a button. You wouldn't consider subscribing to an event a modification. It's extension.
Form "Adaptive Code via C#" book, virtual methods is a instrument to achieve OCP.
In interviews I have been asked to explain the difference between abstraction and encapsulation. My answer has been along the lines of
Abstraction allows us to represent complex real world in simplest manner. It is the process of identifying the relevant qualities and behaviors an object should possess; in other words, to represent the necessary feature without representing the background details.
Encapsulation is a process of hiding all the internal details of an object from the outside real world. The word "encapsulation", is like "enclosing" into a "capsule". It restricts clients from seeing its internal view where the behavior of the abstraction is implemented.
I think with above answer the interviewer was convinced, but then I was asked, if the purpose of both is hiding, then why there is a need to use encapsulation. At that time I didn't have a good answer for this.
What should I have added to make my answer more complete?
Abstraction has to do with separating interface from implementation. (We don't care what it is, we care that it works a certain way.)
Encapsulation has to do with disallowing access to or knowledge of internal structures of an implementation. (We don't care or need to see how it works, only that it does.)
Some people do use encapsulation as a synonym for abstraction, which is (IMO) incorrect. It's possible that your interviewer thought this. If that is the case then you were each talking about two different things when you referred to "encapsulation."
It's worth noting that these concepts are represented differently in different programming languages. A few examples:
In Java and C#, interfaces (and, to some degree, abstract classes) provide abstraction, while access modifiers provide encapsulation.
It's mostly the same deal in C++, except that we don't have interfaces, we only have abstract classes.
In JavaScript, duck typing provides abstraction, and closure provides encapsulation. (Naming convention can also provide encapsulation, but this only works if all parties agree to follow it.)
Its Simple!
Take example of television - it is Encapsulation, because:
Television is loaded with different functionalies that i don't know because they are completely hidden.
Hidden things like music, video etc everything bundled in a capsule that what we call a TV
Now, Abstraction is When we know a little about something and which can help us to manipulate something for which we don't know how it works internally.
For eg:
A remote-control for TV is abstraction, because
With remote we know that pressing the number keys will change the channels. We are not aware as to what actually happens internally. We can manipulate the hidden thing but we don't know how it is being done internally.
Programmatically, when we can acess the hidden data somehow and know something.. is Abstraction .. And when we know nothing about the internals its Encapsulation.
Without remote we can't change anything on TV we have to see what it shows coz all controls are hidden.
Abstraction
Exposing the Entity instead of the details of the entity.
"Details are there, but we do not consider them. They are not required."
Example 1:
Various calculations:
Addition, Multiplication, Subtraction, Division, Square, Sin, Cos, Tan.
We do not show the details of how do we calculate the Sin, Cos or Tan. We just Show Calculator and it's various Methods which will be, and which needs to be used by the user.
Example 2:
Employee has:
First Name, Last Name, Middle Name. He can Login(), Logout(), DoWork().
Many processes might be happening for Logging employee In, such as connecting to database, sending Employee ID and Password, receiving reply from Database. Although above details are present, we will hide the details and expose only "Employee".
Encapsulation
Enclosing. Treating multiple characteristics/ functions as one unit instead of individuals.
So that outside world will refer to that unit instead of it's details directly.
"Details are there, we consider them, but do not show them, instead we show what you need to see."
Example 1:
Instead of calling it as Addition, Subtraction, Multiplication, Division, Now we will call it as a Calculator.
Example 2:
All characteristics and operations are now referred by the employee, such as "John". John Has name. John Can DoWork(). John can Login().
Hiding
Hiding the implemention from outside world.
So that outside world will not see what should not be seen.
"Details are there, we consider them, but we do not show them. You do not need to see them."
Example 1:
Your requirement: Addition, Substraction, Multiplication, Division. You will be able to see it and get the result.
You do not need to know where operands are getting stored. Its not your requirement.
Also, every instruction that I am executing, is also not your requirement.
Example 2:
John Would like to know his percentage of attendance. So GetAttendancePercentage() Will be called.
However, this method needs data saved in database. Hence it will call FetchDataFromDB(). FetchDataFromDB() is NOT required to be visible to outside world.
Hence we will hide it. However, John.GetAttendancePercentage() will be visible to outside world.
Abstraction, encapsulation and hiding complement each others.
Because we create level of abstraction over details, the details are encapsulated. And because they are enclosed, they are hidden.
Difference between Abstraction and Encapsulation :-
Abstraction
Abstraction solves the problem in the design level.
Abstraction is used for hiding the unwanted data and giving relevant data.
Abstraction lets you focus on what the object does instead of how it does it.
Abstraction- Outer layout, used in terms of design.
For Example:-
Outer Look of a Mobile Phone, like it has a display screen and keypad buttons to dial a number.
Encapsulation
Encapsulation solves the problem in the implementation level.
Encapsulation means hiding the code and data into a single unit to protect the data from outside world.
Encapsulation means hiding the internal details or mechanics of how an object does something.
Encapsulation- Inner layout, used in terms of implementation.
For Example:- Inner Implementation detail of a Mobile Phone, how keypad button and Display Screen are connect with each other using circuits.
Encapsulation
Encapsulation from what you have learnt googling around, is a concept of combining the related data and operations in a single capsule or what we could say a class in OOP, such that no other program can modify the data it holds or method implementation it has, at a particular instance of time. Only the getter and setter methods can provide access to the instance variables.
Our code might be used by others and future up-gradations or bug fixes are liable. Encapsulation is something that makes sure that whatever code changes we do in our code doesn't break the code of others who are using it.
Encapsulation adds up to the maintainability, flexibility and extensibility of the code.
Encapsulation helps hide the implementation behind an interface.
Abstraction
Abstraction is the process of actually hiding the implementation behind an interface. So we are just aware of the actual behavior but not how exactly the think works out internally. The most common example could the scenario where put a key inside the lock and easily unlock it. So the interface here is the keyhole, while we are not aware of how the levers inside the lock co-ordinate among themselves to get the lock unlocked.
To be more clear, abstraction can be explained as the capability to use the same interface for different objects. Different implementations of the same interface can exist, while the details of every implementation are hidden by encapsulation.
Finally, the statement to answer all the confusions until now -
The part that is hidden relates to encapsulation while the part that is exposed relates to abstraction.
Read more on this here
Abstraction : Abstraction is process in which you collect or gather relevant data and remove non-relevant data. (And if you have achieved abstraction, then encapsulation also achieved.)
Encapsulation: Encapsulation is a process in which you wrap of functions and members in a single unit. Means You are hiding the implementation detail. Means user can access by making object of class, he/she can't see detail.
Example:
public class Test
{
int t;
string s;
public void show()
{
s = "Testing";
Console.WriteLine(s);
Console.WriteLine(See()); // No error
}
int See()
{
t = 10;
return t;
}
public static void Main()
{
Test obj = new Test();
obj.Show(); // there is no error
obj.See(); // Error:- Inaccessible due to its protection level
}
}
In the above example, User can access only Show() method by using obj, that is Abstraction.
And See() method is calling internally in Show() method that is encapsulation, because user doesn't know what things are going on in Show() method.
I know there are lot's of answers before me with variety of examples.
Well here is my opinion abstraction is getting interested from reality .
In abstraction we hide something to reduce the complexity of it
and In encapsulation we hide something to protect the data.
So we define encapsulation as wrapping of data and methods in single entity referred as class.
In java we achieve encapsulation using getters and setters not just by wrapping data and methods in it. we also define a way to access that data.
and while accessing data we protect it also. Techinical e.g would be to define a private data variable call weight.Now we know that weight can't be zero or less than zero in real world scenario. Imagine if there are no getters and setters someone could have easily set it to a negative value being public member of class.
Now final difference using one real world example,
Consider a circuit board consisting of switches and buttons.
We wrap all the wires into a a circuit box, so that we can protect someone by not getting in contact directly(encapsulation).
We don't care how those wires are connected to each other we just want an interface to turn on and off switch. That interface is provided by buttons(abstraction)
Encapsulation : Suppose I have some confidential documents, now I hide these documents inside a locker so no one can gain access to them, this is encapsulation.
Abstraction : A huge incident took place which was summarised in the newspaper. Now the newspaper only listed the more important details of the actual incident, this is abstraction. Further the headline of the incident highlights on even more specific details in a single line, hence providing higher level of abstraction on the incident. Also highlights of a football/cricket match can be considered as abstraction of the entire match.
Hence encapsulation is hiding of data to protect its integrity and abstraction is highlighting more important details.
In programming terms we can see that a variable may be enclosed is the scope of a class as private hence preventing it from being accessed directly from outside, this is encapsulation. Whereas a a function may be written in a class to swap two numbers. Now the numbers may be swapped in either by either using a temporary variable or through bit manipulation or using arithmetic operation, but the goal of the user is to receive the numbers swapped irrespective of the method used for swapping, this is abstraction.
Abstraction: In case of an hardware abstraction layer, you have simple interfaces to trigger the hardware (e.g. turn enginge left/right) without knowing the hardware details behind. So hiding the complexity of the system. It's a simplified view of the real world.
Encapsulation: Hiding of object internals. The object is an abstraction of the real world. But the details of this object (like data structures...) can be hidden via encapsulation.
Abstraction refers to the act of representing essential features without including the background details or explanations.
Encapsulation is a technique used for hiding the properties and behaviors of an object and allowing outside access only as appropriate. It prevents other objects from directly altering or accessing the properties or methods of the encapsulated object.
Difference between abstraction and encapsulation
1.Abstraction focuses on the outside view of an object (i.e. the interface) Encapsulation (information hiding) prevents clients from seeing it’s inside view, where the behavior of the abstraction is implemented.
2.Abstraction solves the problem in the design side while Encapsulation is the Implementation.
3.Encapsulation is the deliverable of Abstraction. Encapsulation barely talks about grouping up your abstraction to suit the developer needs.
ABSTRACTION:"A view of a problem that extracts the essential information
relevant to a particular purpose and ignores the remainder of
the information."[IEEE, 1983]
ENCAPSULATION: "Encapsulation or equivalently information hiding refers to the
practice of including within an object everything it needs, and
furthermore doing this in such a way that no other object need ever
be aware of this internal structure."
Abstraction is one of the many benefits of Data Encapsulation. We can also say Data Encapsulation is one way to implement Abstraction.
My opinion of abstraction is not in the sense of hiding implementation or background details!
Abstraction gives us the benefit to deal with a representation of the real world which is easier to handle, has the ability to be reused, could be combined with other components of our more or less complex program package. So we have to find out how we pick a complete peace of the real world, which is complete enough to represent the sense of our algorithm and data. The implementation of the interface may hide the details but this is not part of the work we have to do for abstracting something.
For me most important thing for abstraction is:
reduction of complexity
reduction of size/quantity
splitting of non related domains to clear and independent components
All this has for me nothing to do with hiding background details!
If you think of sorting some data, abstraction can result in:
a sorting algorithm, which is independent of the data representation
a compare function, which is independent of data and sort algorithm
a generic data representation, which is independent of the used algorithms
All these has nothing to do with hiding information.
In my view encapsulation is a thought of programmer to hide the complexity of the program code by using access specifier.
Where as Abstraction is separation of method and object according to there function and behavior. For example Car has sheets, wheels, break, headlight.
Developer A, who is inherently utilising the concept of abstraction will use a module/library function/widget, concerned only with what it does (and what it will be used for) but not how it does it. The interface of that module/library function/widget (the 'levers' the Developer A is allowed to pull/push) is the personification of that abstraction.
Developer B, who is seeking to create such a module/function/widget will utilise the concept of encapsulation to ensure Developer A (and any other developer who uses the widget) can take advantage of the resulting abstraction. Developer B is most certainly concerned with how the widget does what it does.
TLDR;
Abstraction - I care about what something does, but not how it does it.
Encapsulation - I care about how something does what it does such that others only need to care about what it does.
(As a loose generalisation, to abstract something, you must encapsulate something else. And by encapsulating something, you have created an abstraction.)
Encapsulation is basically denying the access to the internal implementation or knowledge about internals to the external world, while Abstraction is giving a generalized view of any implementation that helps the external world to interact with it
The essential thing about abstraction is that client code operates in terms of a different logical/abstract model. That different model may be more or less complex than the implementation happens to be in any given client usage.
For example, "Iterator" abstracts (aka generalises) sequenced traversal of 0 or more values - in C++ it manifests as begin(), */-> (dereferencing), end(), pre/post ++ and possibly --, then there's +, +=, [], std::advance etc.. That's a lot of baggage if the client could say increment a size_t along an array anyway. The essential thing is that the abstraction allows client code that needs to perform such a traversal to be decoupled from the exact nature of the "container" or data source providing the elements. Iteration is a higher-level notion that sometimes restricts the way the traversal is performed (e.g. a forward iterator can only advance an element at a time), but the data can then be provided by a larger set of sources (e.g. from a keyboard where there's not even a "container" in the sense of concurrently stored values). The client code can generally switch to another data source abstracted through its own iterators with minimal or even no changes, and even polymorphically to other data types - either implicitly or explicitly using something like std::iterator_traits<Iterator>::value_type available.
This is quite a different thing from encapsulation, which is the practice of making some data or functions less accessible, such that you know they're only used indirectly as a result of operations on the public interface. Encapsulation is an essential tool for maintaining invariants on an object, which means things you want to keep true after every public operation - if client code could just reach in and modify your object then you can't enforce any invariants. For example, a class might wrap a string, ensuring that after any operation any lowercase letters were changed to upper case, but if the client code can reach in and put a lowercase letter into the string without the involvement of the class's member functions, then the invariant can't be enforced.
To further highlight the difference, consider say a private std::vector<Timing_Sample> data member that's incidentally populated by operations on the containing object, with a report dumped out on destruction. With the data and destructor side effect not interacting with the object's client code in any way, and the operations on the object not intentionally controlling the time-keeping behaviour, there's no abstraction of that time reporting functionality but there is encapsulation. An example of abstraction would be to move the timing code into a separate class that might encapsulate the vector (make it private) and just provide a interface like add(const Timing_Sample&) and report(std::ostream&) - the necessary logical/abstract operations involved with using such instrumentation, with the highly desirable side effect that the abstracted code will often be reusable for other client code with similar functional needs.
In my opinion, both terms are related in some sense and sort of mixed into each other. "Encapsulation" provides a way to grouping related fields, methods in a class (or module) to wrap the related things together. As of that time, it provides data hiding in two ways;
Through access modifiers.
Purely for hiding state of the class/object.
Abstracting some functionalities.
a. Through interfaces/abstract classes, complex logic inside the encapsulated class or module can be abstracted/generalized to be used by outside.
b. Through function signatures. Yes, even function signatures example of abstracting. Because callers only knows the signature and parameters (if any) and know nothing about how the function is carried out. It only cares of returned value.
Likewise, "Abstraction" might be think of a way of encapsulation in terms of grouping/wrapping the behaviour into an interface (or abstract class or might be even a normal class ).
As far as iOS is concerned, it can be said that Objective C files (i.e. .h and .m) use abstraction as well as encapsulation.
Abstraction
Header file (.h) only exposes the functions and public members to outside world. No one knows how they are used unless they have the implementation file with them. It is the .m file that holds all the usage and implementation logic with it self. "Implementation remains unexposed".
Encapsulation
The property (#property) encapsulates the memory management attribute (atomic, strong, retain, weak) of an iVar.
A program has mainly two parts : DATA and PROCESS. abstraction hides data in process so that no one can change. Encapsulation hides data everywhere so that it cannot be displayed.
I hope this clarifies your doubt.
Encapsulation is used for 2 main reasons:
1.) Data hiding & protecting (the user of your class can't modify the data except through your provided methods).
2.) Combining the data and methods used to manipulate the data together into one entity (capsule).
I think that the second reason is the answer your interviewer wanted to hear.
On the other hand, abstraction is needed to expose only the needed information to the user, and hiding unneeded details (for example, hiding the implementation of methods, so that the user is not affected if the implementation is changed).
Abstraction: Hiding the data.
Encapsulation: Binding the data.
Why Encapsulation? Why Abstraction?
lets start with the question below:
1)What happens if we allow code to directly access field ? (directly allowing means making field public)
lets understand this with an example,
following is our BankAccount class and following is its limitation
*Limitation/Policy* : Balance in BankAccount can not be more than 50000Rs. (This line
is very important to understand)
class BankAccount
{
**public** double balanceAmount;
}
Following is **AccountHolder**(user of BankAccount) class which is consumer of
**BankAccount** class.
class AccountHolder
{
BankAccount mybankAccount = new BankAccount();
DoAmountCreditInBankAccount()
{
mybankAccount.balanceAmount = 70000;
/*
this is invalid practice because this statement violates policy....Here
BankAccount class is not able to protect its field from direct access
Reason for direct access by acount holder is that balanceAmount directly
accessible due to its public access modifier. How to solve this issue and
successfully implement BankAccount Policy/Limitation.
*/
}
}
if some other part of code directly access balanceAmount field and set balance amount to 70000Rs which is not acceptable. Here in this case we can not prevent some other part of code from accessing balanceAmount field.
So what we can do?
=> Answer is we can make balanceAmount field private so that no other code can directly access it and allowing access to that field only via public method which operates on balanceAmount field. Main role of method is that we can write some prevention logic inside method so that field can not be initialized with more than 50000Rs. Here we are making binding between data field called balanceAmount and method which operates on that field. This process is called Encapsulation.(it is all about protecting fields using access modifier such as private)
Encapsulation is one way to achieve abstraction....but How?
=> User of this method will not know about implementation (How amount gets credited? logic and all that stuff) of method which he/she will invoke. Not knowing about implementation details by user is called Abstraction(Hiding details from user).
Following will be the implementation of class:
class BankAccount
{
**private** double balanceAmount;
**public** void UpdateBankBalance(double amount)
{
if(balanceAmount + amount > 50000)
{
Console.WriteLine("Bank balance can not be more than 50000, Transaction can
not be proceed");
}
else
{
balanceAmount = balanceAmount + amount;
Console.WriteLine("Amount has been credited to your bank account
successfully.....");
}
}
}
class AccountHolder
{
BankAccount mybankAccount = new BankAccount();
DoAmountCreditInBankAccount()
{
mybankAccount.UpdateBankBalance(some_amount);
/*
mybankAccount.balanceAmount will not be accessible due to its protection level
directly from AccountHolder so account holder will consume BankAccount public
method UpdateBankBalance(double amount) to update his/her balance.
*/
}
}
Simply put, abstraction is all about making necessary information for interaction with the object visible, while encapsulation enables a developer to implement the desired level of abstraction.
Encapsulation: Hiding the information at the implementation level. This deals with properties or methods which will be hidden from other objects.
Abstraction: Hiding the information at the idea level/design level. Here we decide that something will be abstract(hidden) from the user while thinking of an idea. Abstraction can be achieved using encapsulation at the implementation level.
In the SRP, a 'responsibility' is usually described as 'a reason to change', so that each class (or object?) should have only one reason someone should have to go in there and change it.
But if you take this to the extreme fine-grain you could say that an object adding two numbers together is a responsibility and a possible reason to change. Therefore the object should contain no other logic, because it would produce another reason for change.
I'm curious if there is anyone out there that has any strategies for 'scoping', the single-responsibility principle that's slightly less objective?
it comes down to the context of what you are modeling. I've done some extensive writing and presenting on the SOLID principles and I specifically address your question in my discussions of Single Responsibility.
The following first appeared in the Jan/Feb 2010 issue of Code Magazine, and is available online at "S.O.L.I.D. Software Development, One Step at a Time"
The Single Responsibility Principle
says that a class should have one, and
only one, reason to change.
This may seem counter-intuitive at
first. Wouldn’t it be easier to say
that a class should only have one
reason to exist? Actually, no-one
reason to exist could very easily be
taken to an extreme that would cause
more harm than good. If you take it to
that extreme and build classes that
have one reason to exist, you may end
up with only one method per class.
This would cause a large sprawl of
classes for even the most simple of
processes, causing the system to be
difficult to understand and difficult
to change.
The reason that a class should have
one reason to change, instead of one
reason to exist, is the business
context in which you are building the
system. Even if two concepts are
logically different, the business
context in which they are needed may
necessitate them becoming one and the
same. The key point of deciding when a
class should change is not based on a
purely logical separation of concepts,
but rather the business’s perception
of the concept. When the business
perception and context has changed,
then you have a reason to change the
class. To understand what
responsibilities a single class should
have, you need to first understand
what concept should be encapsulated by
that class and where you expect the
implementation details of that concept
to change.
Consider an engine in a car, for
example. Do you care about the inner
working of the engine? Do you care
that you have a specific size of
piston, camshaft, fuel injector, etc?
Or, do you only care that the engine
operates as expected when you get in
the car? The answer, of course,
depends entirely on the context in
which you need to use the engine.
If you are a mechanic working in an
auto shop, you probably care about the
inner workings of the engine. You need
to know the specific model, the
various part sizes, and other
specifications of the engine. If you
don’t have this information available,
you likely cannot service the engine
appropriately. However, if you are an
average everyday person that only
needs transportation from point A to
point B, you will likely not need that
level of information. The notion of
the individual pistons, spark plugs,
pulleys, belts, etc., is almost
meaningless to you. You only care that
the car you are driving has an engine
and that it performs correctly.
The engine example drives straight to
the heart of the Single Responsibility
Principle. The contexts of driving the
car vs. servicing the engine provide
two different notions of what should
and should not be a single concept-a
reason for change. In the context of
servicing the engine, every individual
part needs to be separate. You need to
code them as single classes and ensure
they are all up to their individual
specifications. In the context of
driving a car, though, the engine is a
single concept that does not need to
be broken down any further. You would
likely have a single class called
Engine, in this case. In either case,
the context has determined what the
appropriate separation of
responsibilities is.
I tend to think in term of "velocity of change" of the business requirements rather than "reason to change" .
The question is indeed how likely stuffs will change together, not whether they could change or not.
The difference is subtle, but helps me. Let's consider the example on wikipedia about the reporting engine:
if the likelihood that the content and the template of the report change at the same time is high, it can be one component because they are apparently related. (It can also be two)
but if the likelihood that the content change without the template is important, then it must be two components, because they are not related. (Would be dangerous to have one)
But I know that's a personal interpretation of the SRP.
Also, a second technique that I like is: "Describe your class in one sentence". It usually helps me to identify if there is a clear responsibility or not.
I don't see performing a task like adding two numbers together as a responsibility. Responsibilities come in different shapes and sizes but they certainly should be seen as something larger than performing a single function.
To understand this better, it is probably helpful to clearly differentiate between what a class is responsible for and what a method does. A method should "do only one thing" (e.g. add two numbers, though for most purposes '+' is a method that does that already) while a class should present a single clear "responsibility" to it's consumers. It's responsibility is at a much higher level than a method.
A class like Repository has a clear and singular responsibility. It has multiple methods like Save and Load, but a clear responsibility to provide persistence support for Person entities. A class may also co-ordinate and/or abstract the responsibilities of dependent classes, again presenting this as a single responsibility to other consuming classes.
The bottom line is if the application of SRP is leading to single-method classes who's whole purpose seems to be just to wrap the functionality of that method in a class then SRP is not being applied correctly.
A simple rule of thumb I use is that: the level or grainularity of responsibility should match the level or grainularity of the "entity" in question. Obviously the purpose of a method will always be more precise than that of a class, or service, or component.
A good strategiy for evaluating the level of responsibility can be to use an appropriate metaphor. If you can relate what you are doing to something that exists in the real world it can help give you another view of the problem you're trying to solve - including being able to identify appropriate levels of abstraction and responsibility.
#Derick bailey: nice explanation
Some additions: It is totally acceptable that application of SRP is contextual base.
The question still remains: are there any objective ways to define if a given class violates SRP ?
Some design contexts are quite obvious ( like the car example by Derick ) but otherwise contexts in which a class's behaviour has to defined remains fuzzy many-a-times.
For such cases, it might well be helpful if the fuzzy class behaviour is analysed by splitting it's responsibilities into different classes and then measuring the impact of new behavioural and structural relations that has emanated because of the split.
As soon the split is done, the reasons to keep the splitted responsibilities or to back-merge them into single responsibility becomes obvious at once.
I have applied this approach and which has lead good results for me.
But my search to look for 'objective ways of defining a class responsibility' still continues.
I respectful don't agree when Chris Nicola's above says that "a class should presents a single clear "responsibility" to it's consumers
I think SRP is about having a good design inside the class, not class' customers.
To me it's not very clear what a responsability is, and the prove is the number of questions that this concept arises.
"single reason to change"
or
"if the description contains the word
"and" then it needs to be split"
leads to the question: where is the limit? At the end, any class with 2 public methods has 2 reasons to change, isn't it?
For me, the true SRP leads to the Facade pattern, where you have a class that simply delegades the calls to other classes
For example:
class Modem
send()
receive()
Refactors to ==>
class ModemSender
class ModelReceiver
+
class Modem
send() -> ModemSender.send()
receive() -> ModemReceiver.receive()
Opinions are wellcome
I know about "class having a single reason to change". Now, what is that exactly? Are there some smells/signs that could tell that class does not have a single responsibility? Or could the real answer hide in YAGNI and only refactor to a single responsibility the first time your class changes?
The Single Responsibility Principle
There are many obvious cases, e.g. CoffeeAndSoupFactory. Coffee and soup in the same appliance can lead to quite distasteful results. In this example, the appliance might be broken into a HotWaterGenerator and some kind of Stirrer. Then a new CoffeeFactory and SoupFactory can be built from those components and any accidental mixing can be avoided.
Among the more subtle cases, the tension between data access objects (DAOs) and data transfer objects (DTOs) is very common. DAOs talk to the database, DTOs are serializable for transfer between processes and machines. Usually DAOs need a reference to your database framework, therefore they are unusable on your rich clients which neither have the database drivers installed nor have the necessary privileges to access the DB.
Code Smells
The methods in a class start to be grouped by areas of functionality ("these are the Coffee methods and these are the Soup methods").
Implementing many interfaces.
Write a brief, but accurate description of what the class does.
If the description contains the word "and" then it needs to be split.
Well, this principle is to be used with some salt... to avoid class explosion.
A single responsibility does not translate to single method classes. It means a single reason for existence... a service that the object provides for its clients.
A nice way to stay on the road... Use the object as person metaphor... If the object were a person, who would I ask to do this? Assign that responsibility to the corresponding class. However you wouldn't ask the same person to do your manage files, compute salaries, issue paychecks, and verify financial records... Why would you want a single object to do all these? (it's okay if a class takes on multiple responsibilities as long as they are all related and coherent.)
If you employ a CRC card, it's a nice subtle guideline. If you're having trouble getting all the responsibilities of that object on a CRC card, it's probably doing too much... a max of 7 would do as a good marker.
Another code smell from the refactoring book would be HUGE classes. Shotgun surgery would be another... making a change to one area in a class causes bugs in unrelated areas of the same class...
Finding that you are making changes to the same class for unrelated bug-fixes again and again is another indication that the class is doing too much.
A simple and practical method to check single responsibility (not only classes but also method of classes) is the name choice. When you design a class, if you easily find a name for the class that specify exactly what it defines, you're in the right way.
A difficulty to choose a name is near always a symptom of bad design.
the methods in your class should be cohesive...they should work together and make use of the same data structures internally. If you find you have too many methods that don't seem entirely well related, or seem to operate on different things, then quite likely you don't have a good single responsibility.
Often it's hard to initially find responsibilities, and sometimes you need to use the class in several different contexts and then refactor the class into two classes as you start to see the distinctions. Sometimes you find that it's because you are mixing an abstract and concrete concept together. They tend to be harder to see, and, again, use in different contexts will help clarify.
The obvious sign is when your class ends up looking like a Big Ball of Mud, which is really the opposite of SRP (single responsibility principle).
Basically, all the object's services should be focused on carrying out a single responsibility, meaning every time your class changes and adds a service which does not respect that, you know you're "deviating" from the "right" path ;)
The cause is usually due to some quick fixes hastily added to the class to repair some defects. So the reason why you are changing the class is usually the best criteria to detect if you are about to break the SRP.
Martin's Agile Principles, Patterns, and Practices in C# helped me a lot to grasp SRP. He defines SRP as:
A class should have only one reason to change.
So what is driving change?
Martin's answer is:
[...] each responsibility is an axis of change. (p. 116)
and further:
In the context of the SRP, we define a responsibility to be a reason for change. If you can think of more than one motive for changing a class, that class has more than one responsibility (p. 117)
In fact SRP is encapsulating change. If change happens, it should just have a local impact.
Where is YAGNI?
YAGNI can be nicely combined with SRP: When you apply YAGNI, you wait until some change is actually happening. If this happens you should be able to clearly see the responsibilities which are inferred from the reason(s) for change.
This also means that responsibilities can evolve with each new requirement and change. Thinking further SRP and YAGNI will provide you the means to think in flexible designs and architectures.
Perhaps a little more technical than other smells:
If you find you need several "friend" classes or functions, that's usually a good smell of bad SRP - because the required functionality is not actually exposed publically by your class.
If you end up with an excessively "deep" hierarchy (a long list of derived classes until you get to leaf classes) or "broad" hierarchy (many, many classes derived shallowly from a single parent class). It's usually a sign that the parent class does either too much or too little. Doing nothing is the limit of that, and yes, I have seen that in practice, with an "empty" parent class definition just to group together a bunch of unrelated classes in a single hierarchy.
I also find that refactoring to single responsibility is hard. By the time you finally get around to it, the different responsibilities of the class will have become entwined in the client code making it hard to factor one thing out without breaking the other thing. I'd rather err on the side of "too little" than "too much" myself.
Here are some things that help me figure out if my class is violating SRP:
Fill out the XML doc comments on a class. If you use words like if, and, but, except, when, etc., your classes probably is doing too much.
If your class is a domain service, it should have a verb in the name. Many times you have classes like "OrderService", which should probably be broken up into "GetOrderService", "SaveOrderService", "SubmitOrderService", etc.
If you end up with MethodA that uses MemberA and MethodB that uses MemberB and it is not part of some concurrency or versioning scheme, you might be violating SRP.
If you notice that you have a class that just delegates calls to a lot of other classes, you might be stuck in proxy class hell. This is especially true if you end up instantiating the proxy class everywhere when you could just use the specific classes directly. I have seen a lot of this. Think ProgramNameBL and ProgramNameDAL classes as a substitute for using a Repository pattern.
I've also been trying to get my head around the SOLID principles of OOD, specifically the single responsibility principle, aka SRP (as a side note the podcast with Jeff Atwood, Joel Spolsky and "Uncle Bob" is worth a listen). The big question for me is: What problems is SOLID trying to address?
OOP is all about modeling. The main purpose of modeling is to present a problem in a way that allows us to understand it and solve it. Modeling forces us to focus on the important details. At the same time we can use encapsulation to hide the "unimportant" details so that we only have to deal with them when absolutely necessary.
I guess you should ask yourself: What problem is your class trying to solve? Has the important information you need to solve this problem risen to the surface? Are the unimportant details tucked away so that you only have to think about them when absolutely necessary?
Thinking about these things results in programs that are easier to understand, maintain and extend. I think this is at the heart of OOD and the SOLID principles, including SRP.
Another rule of thumb I'd like to throw in is the following:
If you feel the need to either write some sort of cartesian product of cases in your test cases, or if you want to mock certain private methods of the class, Single Responsibility is violated.
I recently had this in the following way:
I had a cetain abstract syntax tree of a coroutine which will be generated into C later. For now, think of the nodes as Sequence, Iteration and Action. Sequence chains two coroutines, Iteration repeats a coroutine until a userdefined condition is true and Action performs a certain userdefined action. Furthermore, it is possible to annotate Actions and Iterations with codeblocks, which define the actions and conditions to evaluate as the coroutine walks ahead.
It was necessary to apply a certain transformation to all of these code blocks (for those interested: I needed to replace the conceptual user variables with actual implementation variables in order to prevent variable clashes. Those who know lisp macros can think of gensym in action :) ). Thus, the simplest thing that would work was a visitor which knows the operation internally and just calls them on the annotated code block of the Action and Iteration on visit and traverses all the syntax tree nodes. However, in this case, I'd have had to duplicate the assertion "transformation is applied" in my testcode for the visitAction-Method and the visitIteration-Method. In other words, I had to check the product test cases of the responsibilities Traversion (== {traverse iteration, traverse action, traverse sequence}) x Transformation (well, codeblock transformed, which blew up into iteration transformed and action transformed). Thus, I was tempted to use powermock to remove the transformation-Method and replace it with some 'return "I was transformed!";'-Stub.
However, according to the rule of thumb, I split the class into a class TreeModifier which contains a NodeModifier-instance, which provides methods modifyIteration, modifySequence, modifyCodeblock and so on. Thus, I could easily test the responsibility of traversing, calling the NodeModifier and reconstructing the tree and test the actual modification of the code blocks separately, thus removing the need for the product tests, because the responsibilities were separated now (into traversing and reconstructing and the concrete modification).
It also is interesting to notice that later on, I could heavily reuse the TreeModifier in various other transformations. :)
If you're finding troubles extending the functionality of the class without being afraid that you might end up breaking something else, or you cannot use class without modifying tons of its options which modify its behavior smells like your class doing too much.
Once I was working with the legacy class which had method "ZipAndClean", which was obviously zipping and cleaning specified folder...