DRY (Don’t Repeat Yourself)
One of the most important OOPs Design Principles is DRY, as the name suggests DRY (don’t repeat yourself) means don’t write duplicate code, instead use Abstraction to abstract common things in one place. If you are using JDK 8 or later versions, you can implement the method in interfaces as well. If you have the same block of code in more than two places, consider making it a separate method. Even if you use a hard-coded value more than once, make them public final constant.
Composition
Over Inheritance (COI)
COI is an acronym for Composition Over Inheritance. As the name implies, this principle emphasizes using Composition instead of Inheritance to achieve code reusability. Inheritance allows a subclass to inherit its superclass’s properties and behavior, but this approach can lead to a rigid class hierarchy that is difficult to modify and maintain. In contrast, Composition enables greater flexibility and modularity in class design by constructing objects from other objects and combining their behaviors. Additionally, the fact that Java doesn’t support multiple inheritances can be another reason to favor Composition over Inheritance.
Composition
allows changing the behavior of a class at run-time by setting property during
run-time, and by using Interfaces to compose a class, we use polymorphism,
which provides flexibility to replace with better implementation at any time.
Difference
between Composition and Inheritance
Now let’s
understand the difference between Inheritance and Composition in a little bit
more detail.
Static
vs Dynamic
The first
difference between Inheritance and Composition comes from a flexibility point
of view. When we use Inheritance, we have to define which class you are
extending in code. It cannot be changed at runtime, but with Composition you
just define a Type which you want to use, which can hold it’s different
implementation. In this sense, Composition is much more flexible than
Inheritance.
Limited
code reuse with Inheritance
As
aforementioned, with Inheritance you can only extend one class, which means you
code can only reuse just one class, not more than one. If you want to leverage
functionalities from multiple classes, you must use Composition. For example,
if your code needs authentication functionality, you can use an Authenticator,
for authorization you can use an Authorizer etc. But with Inheritance you
just stuck with only class, why? Because Java doesn’t support multiple
Inheritance. This difference between Inheritance vs Composition actually
highlights a severe limitation of later reusability.
Unit
Testing
This is in
my opinion, the most important difference between Inheritance and Composition
in OOP probably is the deciding factor whether to use Composition or
Inheritance. When you design classes using Composition, they are easier to test
because you can supply a mock implementation of the classes you are using. But
when you design your class using Inheritance, you must need a parent class in
order to test it’s child class. There is no way you can provide a mock
implementation of the parent class.
Final
Classes
This
difference between them also highlights the other limitation of Inheritance.
Composition allows code reuse even from final classes, which is not possible
using Inheritance because you cannot extend final class in Java, which is
necessary for Inheritance to reuse code.
Encapsulation
The last
difference between Composition and Inheritance in Java in this list comes from
Encapsulation and robustness point of view. Though both Inheritance and
Composition allow code reuse, Inheritance breaks encapsulation because in case
of Inheritance, subclass is dependent upon super class behavior. If parent
classes change its behavior, then child class will also get affected. If
classes are not properly documented and child class has not used the super
class in a way it should be used, any change in super class can break
functionality in the subclass.
The
Composition provides a better way to reuse code and same time protect the class
you are reusing from any of its clients, but Inheritance doesn’t offer that
guarantee. However, sometimes Inheritance becomes necessary, mainly when you
are creating class from the same family.
Programming
for Interface not for Implementation
This OOPs
Design Principles say that Always program for the interface and not for
implementation; this will lead to flexible code that can work with any new
implementation of the interface. But hold on for a min and go through below
lines!
An
interface might be a language keyword and even an interface might also be a
design principle. Don’t confuse both! There are two rules to think of:
- Use
interfaces (the language keyword) if you have multiple concrete
implementations.
- Use interfaces (the design principle) to decouple your own system from external system. It refers to loose coupling between modules or systems.
Minimize Coupling
Coupling
between modules/components is their degree of mutual interdependence; lower
coupling is better. In other words, coupling is the probability that code unit
“B” will “break” after an unknown change to code unit “A”.
Coupling refers to the degree of direct knowledge that one element has of another. In other words, how often do changes in class A force related changes in class B.
What
is Tight Coupling?
In
general, Tight coupling means the two classes often change together. In other
words, if A knows more than it should about the way in which B was implemented,
then A and B are tightly coupled. For example, if you want to change the skin,
you would also have to change the design of your body as well because the two
are joined together, they are tightly coupled. The best example of tight
coupling is RMI (Remote Method Invocation).
What
is Loose Coupling ?
In simple
words, loose coupling means they are mostly independent. If the only knowledge
that class A has about class B, is what class B has exposed through its
interface, then class A and class B are said to be loosely coupled. In order to
overcome from the problems of tight coupling between objects, spring framework
uses dependency injection mechanism with the help of a POJO/POJI model.
Needless to say, through dependency injection its possible to achieve loose
coupling.
Maximize
Cohesion
The
Cohesion of a single module/component is the degree to which its
responsibilities form a meaningful unit; higher cohesion is better. We should
group the related functionalities as to share a single responsibility (e.g. in
a class).
In
general, Cohesion is most closely associated with making sure that a class is
designed with a single, well-focused purpose. The more focused a class is, the
cohesiveness of that class is more. The advantages of high cohesion is
that such classes are much easier to maintain (and less frequently changed)
than classes with low cohesion. Another benefit of high cohesion is that
classes with a well-focused purpose tend to be more reusable than other
classes.
Suppose we
have a class that multiply two numbers, but the same class creates a pop-up
window displaying the result. This is the example of low cohesive class because
the window and the multiplication operation don’t have much in common. To make
it high cohesive, we would have to create a class Display and a class Multiply.
The Display will call Multiply’s method to get the result and display it.
Therefore, this could be an example to develop a high cohesive solution within
OOPs Design Principles.
KISS
(Keep It Simple, Stupid)
The Keep
it Simple, Stupid (KISS) principle states that most systems work the
best if they are kept simpler rather than made complex. Therefore, we should
consider the simplicity as a key goal in the design, and avoid the unnecessary
complication.
The Keep
it Simple, Stupid (KISS) principle is a reminder to keep your code
simple and readable for humans. If your method handles multiple use-cases,
split them into smaller methods. If it performs multiple functionalities, make
multiple methods instead.
Furthermore,
if a single method handles multiple functionalities, it will become long and
bulky. A long method will be very hard to maintain for programmers.
Consequently, bugs will be harder to find, and we might find ourselves
violating other design principles as well. If a method does two things, you
can’t call it to do just one of them, so you’ll obviously make another method.
Also, you
should keep your code simple to be easily understood by other developers.
Learning of OOPs Design Principles can also help you to achieve this. For
example, if a simple for loop does the job efficiently, you should not use a
stream API unnecessarily.
Delegation
Principles
Don’t do
all stuff by yourself, delegate it to the respective classes. A classical
example of the delegation design principle is equals() and hashCode() method in
Java. In order to compare two objects for equality, we ask the class itself to
make comparison instead of the Client class doing that check.
The key benefit of this OOPs Design Principles is no duplication of code and pretty easy to modify behavior. Event delegation is another example of this principle, where an event is delegated to handlers for handling.
Encapsulate
What Changes
Only one
thing is constant in the software field, and that is “Change,” So encapsulate
the code you expect or suspect to be changed in the future. The benefit of this
OOP Design principles is that It’s easy to test and maintain proper
encapsulated code.
YAGNI
(you aren’t gonna need it)
YAGNI
stands for “you aren’t gonna need it”: don’t implement something until it is
necessary. Always implement things when you actually need them, never when you
just foresee that you need them. It leads to code bloat; the software becomes
larger and more complicated. The YAGNI principle suggests that developers
should avoid adding unnecessary functionality or code that is not currently
needed. By focusing on the current requirements and keeping the code simple,
developers can improve the overall quality of the software.
The YAGNI
principle can help developers avoid wasting time on developing features that
may never be used. Instead, developers should focus on delivering software that
meets the current requirements and can be easily maintained and extended in the
future if necessary.
No comments:
Post a Comment