Object Oriented Design Principles Java Programmer should know
We’ve noticed that many Java programmers chase design patterns like Singleton, Decorator, or Observer instead of learning Object Oriented Analysis and Design.
Understanding the fundamentals of Object Oriented Programming, such as Abstraction, Encapsulation, Polymorphism, and Inheritance, is critical, but so is understanding these design principles in order to create clean and modular designs.
We’ve encountered Java programmers and developers of varying levels of experience who are either unfamiliar with these OOPS and SOLID design principles, or who are simply unaware of the benefits each design principle provides, or how to apply these design principles in coding.
The bottom line is that you should always strive for a highly cohesive and loosely coupled solution, code, or design. Looking at open source code from Apache projects is a great way to learn Java and OOPS design principles. They demonstrate how to apply design principles to coding and Java programmes. The Java Development Kit adheres to several design principles, including the Factory Pattern in the BorderFactory class, the Singleton Pattern in the Runtime class, and the Decorator Pattern on various java.io classes.
The best way to learn any design principle or pattern is through real-world examples and understanding the consequences of breaking that design principle, the topic of this article is Introducing Object Oriented Design Principles for Java Programmers who are either unfamiliar with it or in the process of learning it. Personally, I believe that each of these OOPS and SOLID design principles requires its own article to be clearly explained, and I will definitely try to do so here, but for now, prepare yourself for a quick overview of these design principles:)
DRY (Don’t repeat yourself)
DRY (don’t repeat yourself) is our first object oriented design principle. As the name implies, DRY (don’t repeat yourself) means don’t write duplicate code, instead use Abstraction to abstract common things in one place. If you have a block of code that appears in more than one place, consider making it a separate method, or if you use a hard-coded value more than once, make it a public final constant. The advantage of using this Object Oriented Design principle is in maintenance. It is critical not to abuse it; duplication is for functionality rather than code. It means that just because you used the same code to validate the OrderID and SSN, it doesn’t mean they are the same or will remain the same in the future.By using the same code for two different functions or things, you permanently link them together, and if your OrderID format changes, your SSN validation code will break. So be wary of such coupling and avoid combining anything that uses similar code but is unrelated.
Encapsulate What Changes
Only one thing is constant in the software industry, and that is “Change.” Encapsulate the code that you expect or suspect will be changed in the future. The advantage of this OOPS Design principle is that proper encapsulated code is simple to test and maintain. If you are writing code in Java, follow the principle of making variables and methods private by default and gradually increasing access, for example, from private to protected and not public. Encapsulation is used in several design patterns in Java. The Factory design pattern is one example of Encapsulation, which encapsulates object creation code and provides flexibility to introduce new products later with no impact on existing code.
Open Closed Design Principle
Classes, methods, and functions should be Open for new functionality and Closed for modification. This is yet another lovely SOLID design principle that prevents someone from changing previously tried and tested code. If you are only adding new functionality, your code should ideally be tested, which is the goal of the Open Closed Design principle. By the way, the Open Closed principle is represented by the letter “O” in the SOLID acronym.
Single Responsibility Principle (SRP)
Another SOLID design principle is the Single Responsibility Principle, which is represented by the letter “S” in the SOLID acronym. According to SRP, a class should never change for more than one reason, and it should always handle a single function. When you put more than one functionality in one Java Class, you introduce coupling between two functionality, and even if you change one functionality, there is a chance you broke coupled functionality, which necessitates another round of testing to avoid surprises in the production environment.
Dependency Injection or Inversion principle
Don’t ask for dependencies; they will be provided by the framework. The beauty of this design principle is that any class that is injected by DI framework is easy to test with mock objects and easier to maintain because object creation code is centralised in framework and client code is not littered with it. Dependency injection can be implemented in a variety of ways, including using byte code instrumentation, as some AOP (Aspect Oriented programming) frameworks, such as AspectJ, do, or by using proxies, as Spring does. See this IOC and DI design pattern example to learn more about this SOLID design principle. It represents the letter “D” in the acronym SOLID.
Favor Composition over Inheritance
If at all possible, prefer composition to inheritance. Some of you may disagree, but I discovered that composition is far more adaptable than inheritance. Composition allows us to change the behaviour of a class at runtime by setting properties, and by using Interfaces to compose a class, we use polymorphism, which allows us to replace with a better implementation at any time. Even Effective Java recommends choosing composition over inheritance.
Liskov Substitution Principle (LSP)
Subtypes must be substitutable for supertypes, according to the Liskov Substitution Principle, which states that “methods or functions that use superclass type must be able to work with object of subclass without any issue.” LSP is associated with the Single Responsibility Principle and the Interface Segregation Principle.If a class has more functionality than a subclass, the subclass may not support some of the functionality and thus violates the LSP.
To adhere to the LSP SOLID design principle, derived classes or subclasses must enhance rather than reduce functionality.LSP stands for “L” in the SOLID acronym.
Interface Segregation principle (ISP)
According to the Interface Segregation Principle, a client should not implement an interface if it does not use it. This happens most often when a single interface contains multiple functions and the client only requires one of them. Interface design is a difficult task because once your interface is released, you cannot change it without breaking all implementation. Another advantage of this design principle in Java is that interfaces have the disadvantage of requiring all methods to be implemented before any class can use them, so having single functionality means fewer methods to implement.
Programming for Interface not implementation
Always programme for the interface rather than the implementation; this results in flexible code that can be used with any new interface implementation. In Java, use interface types on variables, method return types, and method argument types. Many Java programmers have recommended this, including in Effective Java and the head first design pattern book.
Delegation principle
Don’t do everything yourself; instead, delegate it to the appropriate class. In Java, the equals() and hashCode() methods are classic examples of delegation design principles. To compare two objects for equality, we ask the class itself to perform the comparison rather than the Client class. This design principle has the advantage of avoiding code duplication and making it relatively simple to modify behavior.
All of these object-oriented design principles assist us in writing more flexible and effective code by aiming for high cohesion and low coupling. Theorizing is the first step, but the most important thing is to develop the ability to determine when to apply these design principles. Determine whether we are violating any design principles and compromising code flexibility; however, because nothing is perfect in this world, don’t always try to solve problems with design patterns and design principles; they are mostly for large enterprise projects with longer maintenance cycles.
By the way, if you want to learn more about Java coding practices, read Effective Java by Joshua Bloch, a gem written by the guy who wrote the Java API. Another personal favorite on object oriented design patterns is Head First Design Pattern by Kathy Sierra and others, as well as Head First Object Oriented Analysis and Design by Kathy Sierra and others.
Happy Coding…