www.devreach.com
Refactoring Applicationsusing SOLID Principles
Steve SmithTelerik
ardalis.com @ardalis
www.devreach.com
Software Rots
How? Duplication Excess Coupling Quick Fixes Hacks
www.devreach.com
Preventive Maintenance
• Refactoring– Eliminate Duplication– Simplify Design
• Automated Tests– Verify correctness– Avoid regressions– Increase Confidence
www.devreach.com
Refactoring Process
• Verify existing behavior• Write Characterization Tests if none
exist– Find test points– Break dependencies
• Apply Refactoring• Confirm existing behavior is
preserved
www.devreach.com
Characterization Tests
Process1. Write a test you know will fail2. Use the output of the failing test to
determine the existing behavior to assert
3. Update the test with the new value/behavior
4. Run the test again – it should pass
S O L I DPrinciples
http://flickr.com/photos/kevinkemmerer/2772526725/
www.devreach.com
Principles of OO Design
0. Don’t Repeat Yourself (DRY)
1.Single Responsibility2.Open/Closed3.Liskov Substitution4.Interface Segregation5.Dependency Inversion
www.devreach.com
Don’t RepeatRepeat Yourself
• Duplication in logic calls for abstraction
• Duplication in process calls for automation
www.devreach.com
Common Refactorings
• Replace Magic Number/String• Parameterize Method• Pull Up Field• Pull Up Method• Replace Conditional With
Polymorphism• Introduce Method
www.devreach.com
Role Checksif(user.IsInRole(“Admins”){ // allow access to resource}
// favor privileges over role checks// ardalis.com/Favor-Privileges-over-Role-Checks
var priv = new ContentPrivilege(user, article);if(priv.CanEdit()){ // allow access}
www.devreach.com
Single Responsibility PrincipleThe Single Responsibility Principle states that every
object should have a single responsibility, and that responsibility should be entirely encapsulated by the class.
Wikipedia
There should never be more than one reason for a class to change.
Robert C. “Uncle Bob” Martin
www.devreach.com
Example Responsibilities
• Persistence• Validation• Notification• Error Handling• Logging• Class Selection / Construction• Formatting• Parsing• Mapping
www.devreach.com
Dependency and Coupling
• Excessive coupling makes changing legacy software difficult
• Breaking apart responsibilities and dependencies is a large part of working with existing code
www.devreach.com
Common Refactorings
• Extract Class• Move Method
www.devreach.com
Heuristics and Code Smells
• Visual Studio Metrics
www.devreach.com
Code Smell: Regions
?More on Regions: http://ardalis.com/regional-differences
www.devreach.com
Open / Closed Principle
The Open / Closed Principle states that software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification.
Wikipedia
www.devreach.com
Open / Closed Principle
Open to ExtensionNew behavior can be added in the future
Closed to ModificationChanges to source or binary code are not required
Dr. Bertrand Meyer originated the OCP term in his 1988 book, Object Oriented Software Construction
www.devreach.com
Common Refactorings
• Extract Interface / Apply Strategy Pattern
• Parameterize Method• Form Template Method
www.devreach.com
OCP Fail
www.devreach.com
OCP Failpublic bool IsSpecialCustomer(Customer c){ if(c.Country == “US” && c.Balance < 50) return false; if(c.Country == “DE” && c.Balance < 25) return false; if(c.Country == “UK” && c.Balance < 35) return false; if(c.Country == “FR” && c.Balance < 27) return false; if(c.Country == “BG” && c.Balance < 29) return false;
if(c.Age < 18 || c.Age > 65) return false; if(c.Income < 50000 && c.Age < 30) return false; return true;}
www.devreach.com
OCP OKprivate IEnumerable<ICustomerRule> _rules;
public bool IsSpecialCustomer(Customer c){ foreach(var rule in _rules) { if(rule.Evaluate(c) == false) return false; } return true;}
www.devreach.com
Liskov Substitution Principle
The Liskov Substitution Principle states that Subtypes must be substitutable for their base types.
Agile Principles, Patterns, and Practices in C#
Named for Barbara Liskov, who first described the principle in 1988.
www.devreach.com
Common Refactorings
• Collapse Hierarchy• Pull Up / Push Down Field• Pull Up / Push Down Method
www.devreach.com
Liskov Substitution Failforeach(var employee in employees){ if(employee is Manager) { Helpers.PrintManager(employee as Manager); break; } Helpers.PrintEmployee(employee);}
www.devreach.com
Liskov Substitution OKforeach(var employee in employees){ employee.Print(); // or Helpers.PrintEmployee(employee);}
www.devreach.com
Interface Segregation PrincipleThe Interface Segregation Principle states
that Clients should not be forced to depend on methods they do not use.
Agile Principles, Patterns, and Practices in C#
Corollary:Prefer small, cohesive interfaces to “fat”
interfaces
www.devreach.com
Common Refactorings
• Extract Interface
www.devreach.com
ISP Fail (sometimes)public IRepository<T>{ T GetById(int id); IEnumerable<T> List(); void Create(T item); void Update(T item); void Delete(T item);}
www.devreach.com
ISP OK (for CQRS for example)public IRepository<T> : IReadRepository<T>, IWriteRepository<T>{ }public IReadRepository<T>{ T GetById(int id); IEnumerable<T> List();}public IWriteRepository<T> void Create(T item); void Update(T item); void Delete(T item);}
www.devreach.com
Dependency Inversion PrincipleHigh-level modules should not depend on
low-level modules. Both should depend on abstractions.
Abstractions should not depend on details. Details should depend on abstractions.
Agile Principles, Patterns, and Practices in C#
www.devreach.com
Dependency Inversion Principle• Depend on Abstractions– Interfaces, not concrete types
• Inject Dependencies into Classes
• Structure Solution so Dependencies Flow Toward Core– Onion Architecture
www.devreach.com
Application Layers
www.devreach.com
Data Access EvolutionNo separation of concerns:
Data access logic baked directly into UI ASP.NET Data Source Controls Classic ASP scripts
Data access logic in UI layer via codebehind ASP.NET Page_Load event ASP.NET Button_Click event
User Interface
Database
Compile Time
Runtime
www.devreach.com
Data Access : Helper Classes Calls to data made through
a utility
Example: Data Access Application Block (SqlHelper)
Logic may still live in UI layer
Or a Business Logic Layer may make calls to a Data Access Layer which might then call the helper
User Interface
Database
Compile Time
Runtime
Helper Class
www.devreach.com
What’s Missing? Abstraction! No way to abstract
away data access
Tight coupling
Leads to Big Ball of Mud system
Solution: Depend on interfaces, not
concrete implementations What should we call such
interfaces? Repositories!
User Interface
Database
Compile Time
Runtime
CoreIFooRepository
InfrastructureSqlFooRepository
www.devreach.com
DIP “Onion” Architecture
www.devreach.com
Common Dependencies
• Framework• Third Party Libraries• Database• File System• Email• Web Services• System Resources (Clock)• Configuration• The new Keyword• Static methods• Thread.Sleep• Random
See also responsibilities:• Persistence• Validation• Notification• Error Handling• Logging• Class Selection /
Construction• Formatting• Parsing• Mapping
www.devreach.com
Common Refactorings
• Extract Class• Extract Interface / Apply Strategy
Pattern• Extract Method• Introduce Service Locator / Container
www.devreach.com
DIP Fail
www.devreach.com
Some Improvement (Façade)
www.devreach.com
DIP OK (Strategy)
www.devreach.com
DIP OK (Strategy)
www.devreach.com
Summary
• Maintain / Improve Application Code• Follow DRY/SOLID Principles• Use Characterization Tests to “fix”
behavior• Apply Common Refactorings• Re-run Tests After Refactorings
www.devreach.com
References
Principles of Object Oriented Design (Pluralsight) http://bit.ly/SFkpmq
Refactoring Catalog http://www.refactoring.com/catalog/index.html
Working Effectively with Legacy Code (Book) Michael C. Feathers
Onion Architecture http://jeffreypalermo.com/blog/the-onion-architecture-part-1
/
www.devreach.com
Thank You! – Благодаря!
Find Me Online:Blog: http://ardalis.comTwitter: @ardalishttp://facebook.com/StevenAndrewSmith
www.devreach.com
Discussion