Docsity
Docsity

Prepare for your exams
Prepare for your exams

Study with the several resources on Docsity


Earn points to download
Earn points to download

Earn points by helping other students or get them with a premium plan


Guidelines and tips
Guidelines and tips

SOLID principles with Examples, Slides of Software Engineering

SOLID principles with Examples

Typology: Slides

2024/2025

Uploaded on 04/10/2025

fadna-oumast
fadna-oumast 🇺🇸

1 document

1 / 20

Toggle sidebar

This page cannot be seen from the preview

Don't miss anything!

bg1
64The Single Repons ibility Principle (SRP)
2. : A change to one part o f the system causes it to break in many other,
completely unrelat ed, parts.
3. : It is hard to disentangle the system into components that can be
reused in other systems.
4. : The development environment is held to gether with scotch tape and
toothpaste. It takes forever to go around the edit, compile, test loop.
5. : There are lots of very clever code structures that aren’t
acutally necessary right now, but could be very useful one d ay.
6. : The code looks like it was writt en by two programmers
named Cut and Pas te.
7. : Elucidation of th e originator ’s intent presents certain difficulties
related to convolution of expression.
It is our desire to rid the code of these sm ells. UML diagrams can often help with this
because many of the smells can be seen by examining the dependencies in the diagrams.
Dependency Management
Many of these smells are a result of mism anaged depende ncies. Mismana ged dependen-
cies conjure the view of code that is a tangled mass of couplings. Indeed, it is this view of
entanglement that w as the origin of the term “spaghetti code”.
Object oriented l anguages provide tools that aid in managing dependencies. Interfaces
can be created th at break or invert the direction of certain depen dencies. Polymo rphism
allows modules to invoke functions w ithout depending upon the modules that contain
them. Indeed, an OOPL g ives us lots of power to shape the d ependencies t he way we
want.
So, how do we wan t them shaped? That’s where the following principles com e in. I
have written a great dea l about these principl es. 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 summ ary.
The Single Reponsibility Principle (SRP)
A
CLASS SHOULD HAVE ONL Y ONE REASON TO CHANGE
.
You’ve probably rea d the nonsense about objects needing to know how to draw them-
selves in a GUI, or save themsel ves to disk, or convert themselves to XML, right? Begin-
ning OO texts like to say things like that. Ridiculous! Classes s hould 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.
pf3
pf4
pf5
pf8
pf9
pfa
pfd
pfe
pff
pf12
pf13
pf14

Partial preview of the text

Download SOLID principles with Examples and more Slides Software Engineering in PDF only on Docsity!

The Single Reponsibility Principle (SRP) 64

  1. : A change to one part of the system causes it to break in many other, completely unrelated, parts.
  2. : It is hard to disentangle the system into components that can be reused in other systems.
  3. : The development environment is held together with scotch tape and toothpaste. It takes forever to go around the edit, compile, test loop.
  4. : There are lots of very clever code structures that aren’t acutally necessary right now, but could be very useful one day.
  5. : The code looks like it was written by two programmers named Cut and Paste.
  6. : Elucidation of the originator’s intent presents certain difficulties related to convolution of expression. It is our desire to rid the code of these smells. UML diagrams can often help with this because many of the smells can be seen by examining the dependencies in the diagrams.

Dependency Management

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.

The Single Reponsibility Principle (SRP)

A CLASS SHOULD HAVE ONLY ONE REASON TO CHANGE.

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.

  • calculatePay
  • calculateTaxes
  • writeToDisk
  • readFromDisk
  • createXML
  • parseXML
  • displayOnEmployeeReport
  • displayOnPayrollReport
  • displayOnTaxReport

Employee

  • calculatePay
  • calculateTaxes

Employee

  • EmployeeToXML
  • XMLToEmployee

Employee XML Converter

  • writeEmployee
  • readEmployee

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

  • writeEmployee

EmployeeDB TheDatabase

«api»

Employee

  • readEmployee
  • writeEmployee

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

  1. Readers who know MVC will recognize this as a variation. In more complex situations the con- troller would be a true object rather than just being an interface for the model. In this case, how- ever, the application is so simple that the controller simply acts as a pass through to the model.
  • selectionChanged(employee)
  • terminate()

EmployeeTerminatorController

«interface»

  • enableTerminate(boolean)
  • setEmployeeList(employees)
  • clearSelection()

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.

  1. So named because of it’s resemblance to the circuit diagram of an Eccles-Jordan flip-flop. I know of no written description of this pattern, yet I run into it all the time.

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.

The Dependency Inversion Principle (DIP)

A. H IGH LEVEL MODULES SHOULD NOT DEPEND UPON LOW LEVEL

MODULES. B OTH SHOULD DEPEND UPON ABSTRACTIONS.

B. A BSTRACTIONS SHOULD NOT DEPEND UPON DETAILS. DETAILS

SHOULD DEPEND UPON ABSTRACTIONS.

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.

Conclusion

So, five simple principles:

  1. SRP -- A class should have one and only one reason to change.
  2. OCP -- It should be possible to change the environment of a class without chang- ing the class.
  3. LSP -- Avoid making methods of derivatives illegal or degenerate. Users of base classes should not need to know about the derivatives.
  4. DIP -- Depend on interfaces and abstract classes instead of volatile concrete classes.
  5. ISP -- Give each user of an object an interface that has just the methods that user needs. When should these principles be applied? At the first hint of pain. It is not wise to try to make all systems conform to all principles all the time, every time. You’ll spend an eter- nity trying to imagine all the different environments to apply to the OCP, or all the differ- ent sources of change to apply to the SRP. You’ll cook up dozens or hundreds of little interfaces for the ISP, and create lots of worthless abstractions for the DIP.

Figure 6- Segregated Enrollment System

Enrollment Report Generator

  • getName
  • getDate
  • prepareInvoice
  • postPayment

Student Enrollment

Course

0..*

«parameter»

Accounts Receivable

«parameter»

  • getName
  • getDate

Enrollment Reporter

  • prepareInvoice
  • postPayment

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.

Bibliography

[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