












Study with the several resources on Docsity
Earn points by helping other students or get them with a premium plan
Prepare for your exams
Study with the several resources on Docsity
Earn points to download
Earn points by helping other students or get them with a premium plan
Community
Ask the community for help and clear up your study doubts
Discover the best universities in your country according to Docsity users
Free resources
Download our free guides on studying techniques, anxiety management strategies, and thesis advice from Docsity tutors
SOLID principles with Examples
Typology: Slides
1 / 20
This page cannot be seen from the preview
Don't miss anything!
The Single Reponsibility Principle (SRP) 64
Many of these smells are a result of mismanaged dependencies. Mismanaged dependen- cies conjure the view of code that is a tangled mass of couplings. Indeed, it is this view of entanglement that was the origin of the term “spaghetti code”.
Object oriented languages provide tools that aid in managing dependencies. Interfaces can be created that break or invert the direction of certain dependencies. Polymorphism allows modules to invoke functions without depending upon the modules that contain them. Indeed, an OOPL gives us lots of power to shape the dependencies the way we want.
So, how do we want them shaped? That’s where the following principles come in. I have written a great deal about these principles. The definitive (and most long-winded) treatment is [Martin2002]. There are also quite a number of papers describing these princi- ples on. What follows is a very brief summary.
You’ve probably read the nonsense about objects needing to know how to draw them- selves in a GUI, or save themselves to disk, or convert themselves to XML, right? Begin- ning OO texts like to say things like that. Ridiculous! Classes should know about only one thing. They should have a single responsibility. More to the point, there should only be one reason for a class to change.
65 Chapter : Principles of OOD
Consider Figure 6-1. This class knows way too much. It knows how to calculate pay and taxes, how to read and write itself on disk, how to convert itself to XML and back, and how to print itself on various reports. Can you smell the? Change from SAX to JDOM and you have to change. Change from Access to Oracle, and you have t change. Change the format of the tax report and you have to change
. This design is badly coupled.
In reality we want to separate all these concepts into their own classes so that each class has one, and only one, reason to change. We’d like the Employee class to deal with pay and taxes, an XML related class to deal with converting Employee instances to and from XML, an EmployeeDatabase class to deal with reading and writing Employee instances to and from the database, and individual classes for each of the various reports. In short, we want a separation of concerns. A potential structure is shown in Figure 6-
Figure 6- Class knows too many things.
Figure 6- Separation of concerns.
Employee
Employee
Employee XML Converter
Employee Database
TaxReport EmployeeReport PayrollReport
67 Chapter : Principles of OOD
changes to the database. In a test environment we don’t want the real database to change. We also don’t want to create dummy databases just for the purposes of unit testing. Instead, we’d like to change the environment so that the test catches all the calls that makes to the database, and verifies that those calls are made correctly. We can do this by converting to an interface as in Figure 6-5. Then we can create derivatives that either invoke the true database API, or that support our tests. The interface separates from the database API, and allows us the change the database environment that surrounds without affecting at all.
Among the systems that are plauged by violations of OCP are GUIs. Despite the fact that M ODEL -VIEW -C ONTROLLER has been known for nearly three decades, we often can’t seem to get the design of GUI systems right. All too often the code that manipulates the GUI API is inextricably bound to the code that manages and manipulates the data being displayed.
Consider, for exaple, a very simple dialog box that displays a list of employees. The user selects an employee from the list and then clicks the “Terminate” button. We expect that if no employee is selected, then the “Teminate” button is disabled. On the other hand, if we select an employee from the list, the “Terminate” button will be enabled. When the user clicks the “Terminate” button, the terminated employee disappears from the list, none of the remaining employees is shown as selected, and the terminate button is disabled.
Figure 6- Violation of OCP
Figure 6- Conforming to the OCP
Employee (^) + readEmployee
EmployeeDB TheDatabase
«api»
Employee
EmployeeDB
TheDatabase
«api»
«interface»
Employee Database Implementation
UnitTest Database
The Open Closed Principle (OCP) 68
Implementations that violate the OCP would put all this behavior in the class that invokes the calls to the GUI API. OCP compliant systems would seperate the manipula- tion of the GUI from the manipulation of the data.
Figure 6-6 shows the structure of an OCP compliant system. The manages the list of employees, and is informed when the user selects or terminates an employee. The manages the GUI. It is given the list of employees to display, and informs its controller when a selection changes or when the terminate button is pressed.
The is responsible for actually deleting the selected employee from the list. It is also responsible for determining whether or not the terminate control is enabled or disabled. It doesn’t know that this control is implemented with a but- ton. It simply tells its associated view whether or not the user is allowed to terminate. Sim- ilarly, even though the model doesn’t know anything about a listbox, it can tell its view to clear the selection.
The is brainless. It makes no decisions on its own, and manages no data. The pulls its strings, and the dialog reacts. If the user interacts with the dialog, it simply tells its controller what’s going on by calling methods on the interface. These messages are passed to the model which then interprets and acts upon them 1.
Figure 6- Isolating GUI from Data Manipulation
EmployeeTerminatorController
«interface»
EmployeeTerminatorView
«interface»
Employee Terminator Model
Employee Terminator Dialog
String javax.swing
0..* employees
The Open Closed Principle (OCP) 70
The class is the most complex of the set. Fortunately that complexity is related only to managing the GUI widgets and has nothing to do with the business rules of the system. It simply builds the terminate button and list box, wires them up appropriately and then gets them ready for display. The implementation of the are all trivial and unsurprising.
Listing 6- EmployeeTerminatorDialog.java
Listing 6-3 (Continued) EmployeeTerminatorModel.java
71 Chapter : Principles of OOD
Listing 6-4 (Continued) EmployeeTerminatorDialog.java
73 Chapter : Principles of OOD
Listing 6-5 (Continued) TestEmployeeTerminatorModel.java
The Open Closed Principle (OCP) 74
The class also uses the S ELF -S HUNT pattern, pretending to be a. It catches the messages sent from the to the , and verifies that they are called at the right times and contain the appropriate data. Most of this test just checks the wiring of the dialog box. It checks to make sure that the listbox and button are created properly and that they function as they are supposed to.
Listing 6- TestEmployeeTerminatorDialog.java
Listing 6-5 (Continued) TestEmployeeTerminatorModel.java
The Open Closed Principle (OCP) 76
The tests demonstrate conformance to the OCP because it is possible to change the environment surrounding both the and the to a test environment without the or knowing it. Consider what this means in terms of the flexibility of the modules. We could easily replace the with a command line UI, or a text menu UI. The would never know the difference. We can put the and on different machines using RMI. We can change the environment of each module without affecting the other.
It’s easy to see the mechanism of OCP conformance. Look again at Figure 6-6. We see the familiar F LIP -F LOP pattern 3 of modules implementing one interface and communi-
Listing 6-6 (Continued) TestEmployeeTerminatorDialog.java
77 Chapter : Principles of OOD
cating with another. Clearly we are free to change the environment surrounding the mod- ules because of their use of abstract interfaces. Indeed, abstraction is the key to OCP conformance.
How do we identify the abstractions that help us to conform to the OCP? Most often I achieve OCP compliance simply by writing the unit tests before I write the actual code. Both of the preceeding unit tests were written using the test-first method. Each of the test functions were written first, followed by just enough code in the module to make the test function pass.
For the sake of completeness, Listing 6-7 shows code that binds the dialog and model together and displays the dialog I used this module as a final, manual, test. It allowed me to verify the (admittedly sparse) look and feel of the dialog. This is the only code I wrote that actually displays the dialog. The dialog unit test above simply checks the wiring and function of the dialog, and does not actually display it.
Listing 6- ShowEmployeeTerminator.java
79 Chapter : Principles of OOD
What would happen if we decided to add a? How would we implement? At first this may seem obvious. We’d implement calcPay to return zero as shown below.
But is this right? Does it make any sense to even call on a ? After all, by returning zero we are implying that is a reasonable function to call, and payment is possible. We might find ourselves in the embarrasing situ- ation of printing and mailing a paycheck with a gross pay of zero, or some other similar non-sequitur.
So maybe the best thing to do is to throw an exception, indicating that this function really shouldn’t have been called.
At first this seems like a reasonable thing to do. After all, it’s illegal to call on a. Exceptions are meant to be thrown for illegal situations like this.
Unfortunately, now, every call to can throw an , so either the exception must be caught or declared by the caller. Thus, a con- straint upon a derivative has impacted the users of the base class.
To make matters worse, the following code is now illegal.
To make it legal we have to wrap the call to in a block.
This is ugly, complicated, and distracting. We might easily be tempted to change it to
The Dependency Inversion Principle (DIP) 80
But this is even worse because now code that was supposed to operate on the base class makes explicit reference to one of its derivatives. All this confusion has come about because we have violated the LSP. is not substitutable for. Users of are impacted by the very presence of. And this results in strange exceptions and odd clauses in statements, all of which violate the OCP. You know you are violating the LSP whenever you try to make it illegal to invoke a function on a derivative. You may also be violating the LSP if you make a derivative method degenerate , i.e. implemented with nothing. In both cases you are saying that this function makes no sense in this derivative. And that’s a violation of LSP that can eventu- ally lead to nasty exceptions and tests.
What’s the solution to the problem? Volunteers are not employees. It makes no sense to call on them, so they should not derive from , and they should not be passed to functions that need to call.
Better still: Don’t depend upon volatile concrete classes. If you inherit from a class, make it an abstract class. If you hold a reference to a class, make it an abstract class. If you call a function, make it an abstract function.
In general, abstract classes and interfaces change far less often than their concrete derivatives. Therefore we would rather depend upon the abstractions than the concretions. Following this principle reduces the impact that a change can have upon the system.
Does this mean we can’t use , or? After all, they are concrete classes. Does using them constitute a violation of DIP? No. It is perfectly safe to depend upon con- crete classes that are not going to change. and are not going to change (much) in the next decade, so we can feel relatively safe using them.
It’s the volatile concrete classes that we want to avoid depending upon. These are the concrete classes that are under active development, and that capture business rules that are likely to change. We want to create interfaces for these classes and depend upon those interfaces.
From a UML standpoint, this principle is very easy to check. Follow every arrow in a UML diagram and check to make sure the target of the arrowhead is an interface or an abstract class. If not, and if the concrete class is volatile, then the DIP has been violated, and the system will be sensitive to change.
Conclusion 82
Each user of a object is given an interface that provides just the methods that it is interested in. This protects the user from changes in methods that don’t concern it. It also protects the user from knowing too much about the implementa- tion of the object it is using.
So, five simple principles:
Figure 6- Segregated Enrollment System
Enrollment Report Generator
Student Enrollment
Course
0..*
«parameter»
Accounts Receivable
«parameter»
Enrollment Reporter
Enrollment Accounting
«interface» «interface»
83 Chapter : Principles of OOD
The best way to apply these principle is reactively as opposed to proactively. When you first detect that there is a structural problem with the code, or when you first realize that a module is being impacted by changes in another, then you should see whether one or more of these principles can be brought to bear to address the problem.
Of course if you take a reactive approach to applying the principles, then you also need to take a proactive approach to putting the kinds of pressure on the system that will create pain early. If you are going to react to pain, then you need to diligently find the sore spots.
One of the best ways to hunt for sore spots is to write lots and lots of unit tests. It works even better if you write the tests first, before you write the code that passes them. But that’s a topic for the next chapter.
[Feathers2001]: The 'Self'-Shunt Unit Testing Pattern , Michael Feathers, May, 2001,
[Martin2002]: The Principles, Patterns, and Practices of Agile Software Development , Robert C. Martin, Prentice Hall, 2002