Date post: | 22-Jan-2017 |
Category: |
Software |
Upload: | cameron-presley |
View: | 118 times |
Download: | 0 times |
Establishing a SOLID FoundationAn Intro To Software Design
Cameron Presley@pcameronpresley
http://blog.thesoftwarementor.com
Outline What is SOLID?
Why should our code be SOLID?
Exploring the SOLID principles
Additional Resources
What is SOLID?
What is SOLID? Clever mnemonic for a set of principles that would allow us to write easier to
maintain code
Concepts aren’t new Barbara Liskov – 1987 (Liskov Substitution) Bertrand Meyer – 1988 (Open/Closed)
Robert C. Martin (Uncle Bob) would introduce these concepts in Agile Principles, Patterns, and Practices
Michael Feathers (Working Effectively with Legacy Code) would come up with SOLID
What is SOLID?Single ResponsibilityOpen/ClosedLiskov SubstitutionInterface SegregationDependency Inversion
Why Should We Write SOLID Code? Easier to maintain
Easier to debug Easier to extend
The most expensive part of software development is maintenance Steve McConnell (Code Complete)
It’s fiscally responsible for us to write maintainable code
Why Should We Write SOLID Code? By following SOLID
Small, focused classes Interchangeable abstractions
This allows our code to easily evolve and adapt to changing specifications Which in turn decreases the time it takes to develop features and the likelihood of shipping a bug Which lowers our maintenance cost
This in turn will lead to us to write more testable code Which in turn allows us a way to make sure we don’t ship broken code Which lowers our maintenance cost
Single ResponsibilityOpen/ClosedLiskov SubstitutionInterface SegregationDependency Inversion
Single Responsibility Principle
Single Responsibility Principle Classes should have only one reason to change
NOTE: Doesn’t mean that classes can only do one thing Repository
Coined by Robert C. Martin
Referred to as SRP
SRP – Problem It Solves Large classes are hard to maintain
If multiple developers need to work on the class at the same time… What do you call a group of developers … a merge conflict
SRP forces us to write smaller classes
Easier to combine many small classes than using a large one
SRP – Signs of Violation Opening the class in an editor and the scroll bar disappears
SRP – Signs of Violation 27 public methods
SRP – Signs of Violation 52 private methods, 1 public method
SRP – Resolving Violations Every public method will become a class
Private methods used by a public method will be moved to the new class Private method used everywhere? That becomes a new class
Update places that used the large class to use the smaller classes Can’t change the signature of the large class? Instantiate the newly created class as part of the method
SRP – Violation
SRP – Resolving ViolationsIdentify public methods
SRP – Resolving ViolationsExtract the public methods to new classes
SRP – Resolving ViolationsNow that the methods have been extracted to new classes, identify the helper methods
SRP – Resolving Violations
And add them to the appropriate classes
SRP – Resolving Violations
See the same helper method in multiple places?
Time to move that to a new class
SRP – Resolving Violations
SRP – Resolving ViolationsUpdate the large class to delegate the work to the newly created classes
Single Responsibility PrincipleSummary
Classes should only have one reason to change
By breaking classes down into smaller pieces, it allows us to work on the separate pieces of functionality without stepping on each other
Allows us to combine just-what-we need functionality into new classes
Single ResponsibilityOpen/ClosedLiskov SubstitutionInterface SegregationDependency Inversion
Open/Closed Principle
Open/Closed Principle Classes should be open for extension, closed for modification
Means that we should add new functionality by adding new classes, not modifying existing ones
NOTE: Doesn’t mean that we can’t modify a class to fix bugs
Coined by Bertrand Meyer
Referred to as OCP
OCP – Problem It Solves If we modify a class to add additional functionality, it’s hard to make sure
that we didn’t break something downstream
OCP forces us to add functionality by creating new classes and then using an abstraction
Easier to create a new class and use it then to modify an existing one and make sure we didn’t break anything
OCP – Signs of Violation
OCP – Resolving Violations Instead of adding additional logic, need to move logic to an abstraction
Each if or switch statement needs to be moved to a new class with a public method
Define a new interface that has the method
New classes will implement the interface
Modify the original class to use the new abstraction
OCP – Resolving ViolationsMove logic to new classes
OCP – Resolving ViolationsDefine the new abstraction (Shape) and have the new classes implement it
OCP – Resolving Violations
OCP – Resolving Violations Need to add an additional shape (Triangle)?
Define a Triangle class Have it inherit from Shape abstract class Implement CalculateArea method
Notice how we don’t have to modify the AreaCalculator class to add additional shapes
Open/Closed PrincipleSummary
Classes should be open for extension, closed for modification
By introducing abstractions, we can easily extend our application without worrying about breaking something downstream
Allows us to add additional functionality by creating new classes, not modifying pre-existing ones
Single ResponsibilityOpen/ClosedLiskov SubstitutionInterface SegregationDependency Inversion
Liskov Substitution Principle
Liskov Substitution Principle If two classes are derived from the same abstraction, then they are
interchangeable
NOTE: Without Special Handling
Coined by Barbara Liskov
Referred to as LSP
LSP – Problem It Solves Abstractions hide implementation details
If we have to special handle the abstraction based on what it is, the code becomes much more complex and harder to maintain
Jeff Atwood (Coding Horror) refers to this as “leaky abstractions” because they leak implementation knowledge throughout the code base
LSP – Signs of Violation
LSP – Signs of Violation
What should happen if Quadrilateral is a Square?
LSP – Signs of ViolationSpecial handling based on the class
LSP – Resolving Violations Some cases, the abstraction doesn’t make sense
Square vs Rectangle Square doesn’t have length and width, it has a side Therefore, Quadrilateral is the wrong abstraction
Makes sense to find a better abstraction (not always possible)
In some cases, it’s possible to move the special handling logic to the offending class
LSP – Resolving Violations
Liskov Substitution PrincipleSummary
If two classes are derived from the same abstraction, then they should be interchangeable
We don’t have to add special handling code in the client
If found, try to find a better abstraction or move the special handling to the offending class
Single ResponsibilityOpen/ClosedLiskov SubstitutionInterface SegregationDependency Inversion
Interface Segregation Principle
Interface Segregation Principle Clients should only depend on what they need
Should not have to implement methods that aren’t used
Coined by Robert C. Martin
Referred to as ISP
ISP – Problem It Solves When we define “fat” interfaces, it’s hard to know what functionality we
actually need Ever tried to autocomplete and there were a lot of choices?
By having tighter interfaces, we can control what we expose to our clients
This in turn allows us to tailor our interfaces to what exactly our client needs Role interfaces vs Header interfaces (Martin Fowler)
ISP – Signs of ViolationAccess to Methods and Properties that aren’t being used
ISP – Resolving Violations Identify which methods are being used
Move those methods/properties to their own interface
Update the client to use the new interface
Update the original class to implement the new interface
Similar to Single Responsibility Principle, need to break the interface down into smaller pieces
ISP – Resolving Violation
ISP – Resolving ViolationIdentify methods that are being used in the client
ISP – Resolving ViolationMove the method to new interface, update Atm to implement both interfaces
ISP – Resolving ViolationUpdate AtmBalanceChecker to use IAtmBalanceChecker
Interface Segregation PrincipleSummary
Clients should only have access to methods and properties that they need
This allows us to create role interfaces, not header interfaces
We can spot violations by checking if our client has access to more than we need
Similar to Single Responsibility, break down large interfaces into smaller ones and update the client accordingly
Single ResponsibilityOpen/ClosedLiskov SubstitutionInterface SegregationDependency Inversion
Dependency Inversion Principle
Dependency Inversion Principle Concrete classes should depend upon abstractions, not other concrete
classes
Two rules Classes shouldn’t depend upon concrete classes Classes shouldn’t be responsible for creating their own dependencies
Coined by Robert C. Martin
Referred to as DIP
DIP – Problem It Solves If we have our concrete classes instantiate their own dependencies, the only
way to change these dependencies is to change the class which violates the Open/Closed Principle
Impossible to write unit tests that cross system boundaries (network communication, database access, file system)
Provides another way to determine if a class is doing too much (32 dependencies)
DIP – Signs of ViolationUsing the “new” keyword
DIP – Signs of ViolationUsing static classes
DIP – Signs of ViolationPassing concrete classes instead of abstractions
DIP – Resolving Violations Two goals
Make sure the dependency can be passed in Make sure the dependency has an abstraction
If the class is static, make it not static If the class doesn’t have an interface, define one (remember ISP)
Inject the dependency using constructor injection or method injection
DIP – Static Classes Static classes suck
Can’t pass them as parameters Can’t implement an interface or base class
Removing the static attribute is ideal, but not always possible
Using the Wrapper pattern, we can define another class that is not static to wrap around the static and then pass that around
DIP – Working With Static Classes
DIP – Working With Static Classes Define an interface that has all the public methods from the static class Create a new class that implements the interface and have the new class
call the static class
DIP – Passing Dependencies Method Injection
Passes the dependency as part of the method call Use when the dependency can change per method call
Constructor Injection Passes the dependency as part of instantiation Use when the dependency should be the same for the lifetime of the object
When deciding, we should ask ourselves “Will this change during the course of the program?”
DIP – Passing Dependencies
DIP – Passing Dependencies
Dependency Inversion PrincipleSummary
Concrete classes should depend upon abstractions, not other concrete classes
Allows us to switch dependencies at runtime Useful for testing
Use the Wrapper pattern to handle static classes
Use method injection or constructor injection for passing dependencies
Summary What is SOLID?
Why should our code be SOLID?
What the principles stand for How to spot violations How to fix them
Resources Books
Agile Principles, Patterns, and Practices by Robert C. Martin Working Effectively With Legacy Code by Michael Feathers Code Complete 2 by Steve McConnell
Videos SOLID Principles of Object Oriented Design by Steve Smith
Articles Role Interfaces by Martin Fowler
Music City Code Sessions Why I Love Dependency Injection by Jeremy Clark (Saturday @ 10)