Abstration is a very powerful concept an any OOPs language. Most of the OOAD design patterns lets you develop your components/softwares on abstraction, which further helps you to maintain your software in a better way.
Here are few examples of design principle. (a) Open/Close principle (b) Liskov's Substitution principle. (c) Dependency Inversion Principle (d) Interface Segregation Principle (e) Composite Reuse Principle.
So now lets discuss above design principles one by one. Open/Close Principle : What this principle says is, you should design your software in such a way that it should be OPEN FOR EXTENSION BUT AT THE SAME TIME IT SHOULD BE CLOSED FOR MODIFICATION. Whew... This is the first question you should ask yourself after you have finished writing you application module. Be aware if your answer is closed. If it is the case then you should probably relook at your OOAD design. You may wonder that how it is possible to extend an application without even modifying it. Precisely a good OO design gives you this power. Remember bug fixing of your application is not an extension. Think about how Object1 uses Object2 and Object2 provides some business logic to Object1. This business logic may be customer specific. To meet different customer needs, if you have to change the business logic embedded in Object2, your application is voilating OCP. So far so good, but the example speaks more than thousand words so I will give you an example to illustrate the points about OCP. If you follow the example, you should be able to understand what OCP is and how to use it to provide the abstraction in Java(Or any OOPs language for that matter).
Strong coupling Whas is it? It means that one application module is totally dependent on the implementation of another module. This is the first sign of OCP voilation. Lets consider an example : You have been asked to design a banking application where a particular loan request is passed through a LoanValidator to apprive the load request. LoanValidator looks to see if the balance is above certain values and then approves the request. Given this problem, one guy comes up with a following classes.
The LoanRequestHandler can access a loan request. This class hold a balance and period of existence for a particular bank account. I have simplified all other features that this class should normally bear to represent a physical bank account. But still this is sufficient to make the point clear.
This program can tell you if your loan request is approved or not. It uses an other object PerfonalLoanValidator to decide if the loan request is valid or not. The personal LoanValidator just checks to see if the balance is more than 1000 and approves the loan or else rejects it.
Problem Do you see any problem with the above design?? Probably YES. The LoanRequestHandlerObject is strongly coupled with PersonalLoanValidator object. What happens if the LoanRequestHandler object should be used to verify a businessloan. Certainly, your business logic to approve a business loan will now be a different and PersonalLoanValidator is not capable of handling this requirement. One solution may be to add another method to the PersonalLoanValidator class to verify the business loan approval process. Also we need to modify the LoanRequestHandler object with an if-else statements to apply validator object of the personal loans and business loans This means every time bank decides to provide a different type of loan, we need to make change in the LoanRequestHandler and PersonalValidator objects.
So is it a good way solutions? To me this solution is certainly not letting the application closed for modification. This is where OCP is helpful to provide abstraction into your application.
Now OCP compliant solution To make the design OCP compliant, we need to make the following changes: The strong coupling between LoanRequestHandler and any validator objects should be avoided. Individual validator objects implementing different business logic for approving a loan request should represent an abstract type and the LoanRequestHandler should use this abstract type rather than any specific sub-type to avoid any strong coupling.
Bearing these facts in mind, we come up with following design. I will only show the skeletn of the modified design and the code inside each of them remains same as before. Only design changes and not the implementations.
The LoanRequestHandler now uses an interface type of object named Validator, which defines the method for approving the loan. Each individualValidator object will implement this method, with the object specific business logic within the method inherited from the defined Validator interface.
Now each individual validator object will implement this interface and make use of their individual business logic to approve or reject a loan request. Based on this, the PersonalLoanValidator object changes to the following:
Now we will implement another Validator named BusinessLoanValidator.
The businessLoanValidator only approves the loan request if the balance is more than 5000, whereas the PersonalLoanValidator will approve the loan when balance is more than 1000. Now the important part, LoanRequestHandler remains unaffected by this change in the business logic change.As long as it has been supplied with the correct validator, it will operate correctly.
What you have to say about this design now? better? Yes ofcourse it is. Any loan type can be handled by adding a new XXXValidator object, implementing the validator interface. This is OCP compliant solution.
Hope it would help you to understand the OCP in a better way and you can apply this knowledge to make better software.
I would encourage and advice you to do some google for the remaining design principles as an exercise to understand the powerfulness of the abstraction.
<b>Don't tell "GOD" how "BIG" your "Problem" is; tell the "Problem " how "BIG" your "GOD" is!...<br />....</b>
The purpose of abstraction is not to be vague, but to create a new semantic level in which one can be absolutely precise. Edsger W. Dijkstra, "The Humble Programmer", October 1972
You use abstraction when you refer to an object by an interface or supertype. This indicates you are interested in the abstract features of the interface, but not in the concrete details of the implementation. It frees you from being concerned with things that don't matter. You can set aside a few brain cells to sacrifice to a good glass of beer later.
Is "things" a Set or List? We don't care! One less thing to worry about.
A good question is never answered. It is not a bolt to be tightened into place but a seed to be planted and to bear more seed toward the hope of greening the landscape of the idea. John Ciardi