How to ensure maintainability in a class? Can it simply be done by creating class using design patterns or is there something else involved? Also, what are the characteristics of a good method?
You won't do badly by following the SOLID and DRY principles.
SOLID is:
SRP Single responsibility principle
the notion that an object should have only a single responsibility.
OCP
Open/closed principle
the notion that “software entities … should be open for extension, but closed for modification”.
LSP
Liskov substitution principle
the notion that “objects in a program should be replaceable with instances of their subtypes without altering the correctness of that program”. See also design by contract.
ISP
Interface segregation principle
the notion that “many client specific interfaces are better than one general purpose interface.”[5]
DIP
Dependency inversion principle
the notion that one should “Depend upon Abstractions. Do not depend upon concretions.”[5]
Dependency injection is one method of following this principle.
And DRY stands for Don't Repeat Yourself, meaning you should strive to remove any duplication in your code.
Put in a lot of effort to make sure you have a good interface. Once you have that, you can completely rewrite the class, if you want, without affecting any other code in the project. If your class is so big that you can't easily rewrite it, then that is an issue too.
Although Oded's answer is good for ensuring the maintainability of a program or library, this question is about class maintainability and for that, there are only two requirements... a good interface, and strong cohesion.
Related
This is one of the past year's exams that have no answers:
[1]: https://i.stack.imgur.com/RDzz0.jpg
It shows a diagram with two classes, Customer and Supplier, inheriting both from a class Partner. Another class Customer_Supplier inherits from both, Customer and Supplier.
The question asks what SOLID principle this design would violate. Despite attentive verification, I could not find any, and would really like to know.
The following diagram is the famous diamond of death, a very delicate problem when working with multiple inheritance:
The academic answer
The academic answer to your question is that this design violates the Single Responsibility Principle. The reasoning is the following:
A class should have a single responsibility
Supplier and Customer both have a single responsibility
Customer_Supplier inherits of at least two responsibilities
In reality, the SRP is about reason to change. But the general tendency is to apply the same reasoning: Customer_Supplier might have to change because of changes in Supplier or changes in Customer.
Elimination of the other candidates
It is in principle compliant with the Open/Close Principle by design. Each class could be extended, and unless the contrary is proven, there is no need to modify them.
A class diagram is rarely sufficient to confirm or deny compliance with the Liskov Substitution Principle, since this principle is about the contracts/promises of the classes and their subclasses. LSP requires that an object of the subclass can be used whenever an object of the superclass is expected. At first sight, a Customer_Supplier could be used instead of a Supplier, as well as instead of a Customer. Of course, one could easily imagine a class that breaks this. But this is equally true with any of the simplest inheritance. The fact is that nothing in the diagram lets us assume the opposite.
The Interface Segregation Principle is not violated either. On contrary, if a client does not need the Customer_Supplier interface, you could use one of the parent classes. If you use the Customer_Supplier in a client, it's probably because you need the full interface.
Finally, the Dependency Inversion Principle is not relevant here. Nothing indicates that one class is more concrete or more abstract than the other. In fact, these could even all be abstract classes. So there is no reason to think that this design does not comply with "Abstractions should not depend on details. Details (concrete implementations) should depend on abstractions".
Is the academic answer is flawed?
The diamond of death is an extreme case that is often given as example to explain that multiple inheritance would be bad. And multiple inheritance can easily be misused. But so can be single inheritance and any other programming construct.
Let's bring in some more objectivity:
Classes should have a single responsibility. If a class inherits from another it may then have two responsibilities: its own and the responsibility of the superclass. On the other side, nothing tells us that these responsibilities are independent: one could be a sub-responsibility of the other.
Consequently, if Supplier has a sub-responsibility of BusinessPartner, and Customer has a sub-responsibility of BusinessPartner, they both have the sub-responsibilities of the same larger responsibility, especially considering the Open/Closed principle. This means that Customer_Supplier could in the end still just be a sub-responsibility of this single large responsibility. So SRP could perfectly be respected.
This is not advanced computer science, but basic set theory. You can use the same reasoning for the reason to change.
Another reasoning can be used for reason to change: if the subclass uses only the public interface of the superclass (which is a robust practice in view of LSP's history constraint) the subclass would not be impacted by changes to the superclass more than by any other change of a class that its dependent uppon. So the single reason to change could still hold.
For all these reasons, I'd reformulate the academic answer as follows: if this design would violate a SOLID principle, it could be only the the SRP. Nevertheless it does not necessarily violate it.
And to hammer the nail, I'll conclude with a quote from R.C. Martin who invented the SRP concept:
And this gets to the crux of the Single Responsibility Principle. This principle is about people.
And this design does not say anything about people.
And to finish with a philosophical question: is multiple inheritance really needed?
I try to apply SOLID principles in my project's class design. Are there any exceptions of SOLID principles? Do we HAVE to apply these principle DEFINITELY. For example I prepared a factory class.
class XAdderFactory
{
private Person _person;
public bool PersonHasNoRecords
{
get
{
return string.IsNullOrEmpty(_person.HasXRecords);
}
}
public XAdderFactory(Person person)
{
this._person = person;
if (PersonHasNoRecords)
{
new XListMakerAFactory(person);
}
else
{
new XListMakerB(person);
}
}
}
This class never conforms to the OCP.
New type list makers may be required in the future and I must add a new else if block.
Is my design bad?
Or are there exceptions of SOLID principles that are not mentioned too often?
I am not sure but my example complies with "Strategic Closure" of OCP?
If you have another examples about SOLID exceptions,i think it would be helpful for designers.
The Open-Closed principle is important and useful, but it is not something that should be applied blindly to all classes and all modules. In some cases, creating the abstractions that enable extensibility is just not worth it, because no extensions are expected. In other cases, we can anticipate that requirements will change, but we're not sure what kind of changes to expect, or we're not sure about the other requirements, so we prefer to postpone the decision, and begin with a simpler module implementation that does not respect OCP.
SOLID principles are just guidelines. You can arrive at solution to your problem with or with out using these principles.
Your main focus should be designing a solution to the problem rather than fitting your solution to a specific design pattern or principle.
If you really think that your class should not be modified, then only implement Open/Closed principle. Generally I don't see any issue in modification of existing Factory classes to add new types.
Below three principles are really useful for designing solution
Interface_segregation_principle: No client should be forced to depend on methods it does not use
Don't use fat interfaces in your code where implementation classes have to override un-used or un-related methods. Design granular interfaces and create classes, which implement these granular interfaces.
Related SE question:
Interface Segregation Principle- Program to an interface
Dependency_inversion_principle: It's good principle. You should program to interface rather than implementation.
Liskov_substitution_principle : Use this principle if you need to change your implementation at run time dynamically. If your application does not change its implementation, you may not require this feature.
But Single_responsibility_principle is debatable among all five principles.
Module may have single responsibility but designing a class catering to single responsibility will lead to hundreds/thousands of classes.
SOLID principles (or any related principles) are guideline to avoid potential pitfalls/threats in a software project in terms of implementation and maintenance. And just following a principle blindly without knowing its reflection won't work at all.
As your example, I'll take OCP. The key concept behind OCP is that, if your project 100% complies with OCP, any other (may be by an outsider, new member) can do coding without looking at the current code but just looking at your api documentation (about method exposed) which really makes that person's life easy. And also no need to test the existing code again and again because no modifications will be happened for the existing code. But indeed there are some situations where we have to break OCP.
Ex:
A new requirement.(needs to implement inside an existing class),
A bug-fix
Limited framework support (any MVC framework) etc.
And also there might be some situations where we are breaking OCP knowing that it will not harm.
About the principles, you can have a simple analogy like this. When you walk on road there are lots of principles to follow as a pedestrian.
Ex:
Walk on left hand side. (So that you can see incoming vehicles)
Cross only on pedestrian-crossing (So that vehicles can see you clearly and they would stop).
Following them as far as possible definitely make you safe. But imagine a situation where there are no vehicles for miles on road, are you still searching for a pedestrian crossing to cross the road? no right? You know you are safe even its not a pedestrian crossing and you cross. And if there's a situation where the left hand side on the road is pretty muddy and cannot walk on, would you still go on mud just to follow the principle? no right. You'd rather walk on right hand side knowing the situation.
I think you got an idea about principles. :)
I think i found the first thing that must be considered when writing codes.It is UNIT TESTS.Solid principles,design patterns etc are tools which helps to make unit tests.According to me any unexperiented programmer ( like me ) must apply UNIT TESTS with no exceptions.Test results already leads to programer toward better design.
The DIP states:
High-level modules should not depend on low-level modules. Both should depend on abstractions.
Abstractions should not depend upon details. Details should depend upon abstractions.
And the OCP states:
Software entities (classes, modules, functions, etc.) should be open
for extension, but closed for modification.
I think if we satisfy the DIP, it will cover the OCP too, So, why we separate these two principles?
Uncle Bob Martin, who popularized the Open-Closed Principle (OCP) and Dependency Inversion Principles (DIP) as two of the SOLID principles, states himself that DIP arises from an application of OCP and the Liskov Substitution Principle:
In this column, we discuss the structural implications of the OCP and
the LSP. The structure that results from rigorous use of these
principles can be generalized into a principle all by itself. I call
it “The Dependency Inversion Principle” (DIP).
Robert C. Martin, Engineering Notebook, C++ Report, 1996.
So you're right in stating that every instance of DIP will be an instance of OCP, but OCP is much more general. Here's a use-case of OCP but not DIP I ran into recently. Many web frameworks have a notion of signals, where upon one action, a signal is fired. The object sending the signal is completely unaware of the listeners who are registered with the signal. Every time you want to add more listeners to the signal, you can do so without modifying the sender.
This is clearly exemplifying OCP ("closed to modification, open for extension"), but not DIP, as the sender is not depending on anything, so there's no sense in talking about whether it depends on something more abstract or less so.
More generally you can say the Observer Pattern (one of the GoF patterns) describes how to comply with OCP but not DIP. It'd be interesting to go through the GoF book and see which ones have to do with OCP and how many of those are not DIP-related.
I think adhering to the DIP makes it easier to comply with the OCP. However, one does not guarantee the other.
For example, I can create a class that has a method that takes a parameter of base. If base is an abstract class then I'm adhering to the DIP as I have inverted the dependency to the caller. However, if the code in that method does something like:
if (base is derived)
(derived)base.DoSomethingSpecificToDerived;
elsif (base is evenMoreDerived)
(evenMoreDerived)base.DoSomethingSpecificToEvenMoreDerived;
Then it's not OCP compliant as I have to modify it every time I add a new derivative.
It's very contrived example, but you get my point.
The DIP tells you how to organize the dependencies. It doesn't tell you when you are done with a particular interface.
Roughly speaking, the message of OCP is to have complete but minimalistic interfaces. In other words, it tells you when you are done with an interface but it doesn't tell you how to achieve this.
In some sense, DIP and OCP are orthogonal.
So, why we separate these two principles?
As for design patterns and named principles, almost all of them have in common that:
Find what varies and encapsulate (hide) it.
Prefer aggregation over inheritance.
Design to interfaces.
Even if the named patterns and principles partially overlap in some sense, they tell you something more specific (in a more specific situation) than the above three general principles.
Good answer by #CS. To summarize,
The DIP is an extension of the OCP, so
When we satisfy the DIP, we generally satisfy the OCP as well.
The reverse is not true, and we can conceive of OCP-compliant, DIP violations. Here is one more (Java) example.
public abstract class MyClass {
DependencyOne d1;
DependencyTwo d2;
MyClass() {
d1 = new DependencyOne();
d2 = new DependencyTwo();
}
}
The OCP is satisfied because we can extend the class. The DIP is violated because we directly instantiate dependencies.
Now the challenge is, can we think of a DIP-compliant, OCP violation. The best example I can come up with is an annotation. In Java we use the #Deprecated annotation to mark code which is open for modification, thereby violating the OCP. At the same time, this code may be perfectly DIP compliant in terms of its abstractions and dependencies. Certain libraries use an #Beta annotation to similar effect.
I cannot imagine an example that is DIP-compliant and yet closed to extension, beyond the nullary example of a class which has no dependencies, which is not very interesting. I would say the DIP implies openness to extension. However, there may be edge cases where the DIP does not imply closedness to modification.
The OCP makes a dependent class easy to consume. The OCP enables asynchronous consumption of an interface by decoupling old implementations from newer versions. It allows the things that depend upon it to continue to depend on it even in the face of change for other purposes. That way a class never has to care who's calling it.
The DIP does a couple of things. It makes depending on external classes easy. Dependency Injection enables the substitutions of dependencies by encouraging the separation of creation duties from consumption. Instead of creating the external dependency that is to be consumed, the pattern states that it should be provided externally. Ultimately, this encourages code that is idempotent (code that does not change external state). Idempotent code is good because it can be verified that it does only what is immediately visible. It doesn't have external side effects. It's very testable, understandable, and readable.
Recently I heard that there are 9 rules for OOP(Java). I know only four as Abstraction, Polymorphism, Inheritance and Encapsulation. Are there any more rules for OOP?
Seems like what you're looking for are the Principles of Object-Oriented Design.
Summarized from Agile Software Development Principles, Patterns, and Practices. These principles are the hard-won product of decades of experience in software engineering. They are not the product of a single mind, but they represent the integration and writings of a large number of software developers and researchers. Although they are presented here as principles of object-oriented design, they are really special cases of long-standing principles of software engineering.
SRP The Single Responsibility Principle A class should have only one reason to change.
OCP The Open-Closed Principle Software entities (classes, packages, methods, etc.) should be open for extension, but closed for modification.
LSP The Liskov Substition Principle Subtypes must be substitutable for their base types.
DIP The Dependency Inversion Principle Abstractions should not depend upon details. Details should depend upons abstractions.
ISP The Interface Segregation Principle
Clients shold not be forced to depend upon methods that they do not use. Interfaces belong to clients, not to hierarchies.
REP The Release-Reuse Equivalency Principle
The granule of reuse is the granule of release.
CCP The Common Closure Principle
The classes in a package should be closed together against the same kinds of changes. A change that affects a closed package affects all the classes in that package and no other packages.
CRP The Common Reuse Principle
The classes in a package are reused together. If you reuse one of the classes in a package, you reuse them all.
ADP The Acylcic Dependencies Principle
Allow no cycles in the dependency graph.
SDP The Stable Dependencies Principle
Depend in the direction of stability.
SAP The Stable Abstractions Principle
A package should be as abstract as it is stable.
Not sure about any rules. All these mentioned things are more like OO paradigms to me. There are few advices we follow like,
Separation of Concern
Single Responsibility per Class
Prefer Composition over Inheritance
Programming to Interface
Plus all mentioned by Billybob, already
These OO principles are straight from Head First Design Patterns:
Encapsulate what Varies
Program to an Interface, rather than an Implementation
Favour Composition over Inheritance
A Class should have only one reason to Change (Single Responsibility Principle)
Sub-Types must be substitutable for their Base (Liskov Substitition Principle)
Classes shoule be Open for extension, but Closed for Modification (Open-Closed Principle)
These are concepts, not rules. There are no rules really, just decisions to make, some designs are better than others, some much better than others :-)
There are plenty of guidelines though :-) Some are language specific (C++ is riddled with them) others are OO specific. Too many to list though :-)
Off the top of my head, important ones are:
Loose coupling, high cohesion
Write testable classes, which you test
Use inheritence sparingly and only where it makes sense (prefer composition)
Try stick to the open/close principle.
(most important) KISS
Plenty to expand upon and add :-)
EDIT: I should add, the rules which you listed are not unique to OO
According to the Pragmatic Programmers - the rules are:
Keep it DRY (Don't Repeat Yourself)
Keep it SHY (Ensure that your classes have high cohesion and low coupling)
and tell the other GUY (Separation of concerns)
http://media.pragprog.com/articles/may_04_oo1.pdf
There are no "Rules" to OOP.
There are 4 language properties that make a language object-oriented or not (these are the things you listed in your question).
The rest of the material out there are guidelines. The best/most helpful guidelines I've read are GRASP
Many of the suggestions are not readily understandable by laymen (non-CS majors). I thought GRASP was pragmatic and approachable.
I think GRASP is nice because it suggests the most critical part of OO in its name - Assignment of Responsibility (to objects not programmers).
The two most critical GRASP concepts from which everything else derives are coupling and cohesion. These two concepts/principals drive all other patterns and approaches.
BTW - did I just interview you? You transcribed the question incorrectly...
I remember seeing a debate about this somewhere, and am currently considering removing a base object that every business object, in a system I'm working on, inherits from. It contains a few properties, some database logic, and some constructor logic.
Is this an anti pattern, or is the jury still out? Would it be better to have a base contract to inherit from, which would require a certain amount of boilerplate coding to be done in each object?
EDIT: I do like dsimcha and feel it reflects very well on the issue, I am still happy to hear any further answers
The standard rule of thumb is to use inheritance only to provide flexibility for users of a class through polymorphism, and use composition if you want to reuse code from other classes. However, as long as you're not violating the Liskov Substitution Principle it's probably not too bad. Writing tons of boilerplate is inherently a bad thing, too, since it obscures the parts of your code where the real action is happening and is anti-DRY. If you are violating the Liskov Substitution Principle, though, then absolutely this is a bad idea.
I also would like to understand what problems I may encounter, or should be aware of
A potential problem is if you use multiple inheritance: your subclass then inherits two instances of the 'Eve' classes ... which is why C++ supports so-called virtual inheritance.
It's a frequently-used idiom: for example in .Net everything derives from System.Object ... and/or, all COM objects implement the IQueryInterface interface.
Nothing is an anti-pattern in a vacuum. Is your 'Eve class' causing you problems? What benefits do you expect to realize from removing it? Asking whether it's on some standard list of anti-patterns only helps if it aids in identifying actual issues.