KEMBAR78
Refactoring with SOLID - Telerik India DevCon 2013 | PPTX
Refactoring Applications
       using SOLID Principles

                                       Steve Smith
                                           Telerik
                                   ardalis.com @ardalis



Follow me at twitter.com/ardalis
Software Rots
    How?
     Duplication
     Excess Coupling
     Quick Fixes
     Hacks




Follow me at twitter.com/ardalis
Preventive Maintenance
    • Refactoring
          – Eliminate Duplication
          – Simplify Design


    • Automated Tests
          – Verify correctness
          – Avoid regressions
          – Increase Confidence

Follow me at twitter.com/ardalis
Refactoring Process
    • Verify existing behavior
    • Write Characterization Tests if none exist
          – Find test points
          – Break dependencies
    • Apply Refactoring
    • Confirm existing behavior is preserved



Follow me at twitter.com/ardalis
Characterization Tests
    Process
    1. Write a test you know will fail
    2. 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


Follow me at twitter.com/ardalis
SOLID
  Principles



               http://flickr.com/photos/kevinkemmerer/2772526725
Principles of OO Design
    0. Don’t Repeat Yourself (DRY)

    1. Single Responsibility
    2. Open/Closed
    3. Liskov Substitution
    4. Interface Segregation
    5. Dependency Inversion

Follow me at twitter.com/ardalis
Don’t Repeat
    Repeat Yourself
    • Duplication in logic calls for abstraction

    • Duplication in process calls for
      automation




Follow me at twitter.com/ardalis
Common Refactorings
    •    Replace Magic Number/String
    •    Parameterize Method
    •    Pull Up Field
    •    Pull Up Method
    •    Replace Conditional With Polymorphism
    •    Introduce Method



Follow me at twitter.com/ardalis
Role Checks
     if(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
     }

Follow me at twitter.com/ardalis
Single Responsibility Principle
    The 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




Follow me at twitter.com/ardalis
Example Responsibilities
    •    Persistence
    •    Validation
    •    Notification
    •    Error Handling
    •    Logging
    •    Class Selection / Construction
    •    Formatting
    •    Parsing
    •    Mapping


Follow me at twitter.com/ardalis
Dependency and Coupling
    • Excessive coupling makes changing
      legacy software difficult

    • Breaking apart responsibilities and
      dependencies is a large part of working
      with existing code




Follow me at twitter.com/ardalis
Common Refactorings
    • Extract Class
    • Move Method




Follow me at twitter.com/ardalis
Heuristics and Code Smells
    • Visual Studio Metrics




Follow me at twitter.com/ardalis
Code Smell: Regions




   More on Regions: http://ardalis.com/regional-differences
Follow me at twitter.com/ardalis
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




Follow me at twitter.com/ardalis
Open / Closed Principle
    Open to Extension
      New behavior can be added in the future

    Closed to Modification
      Changes to source or binary code are not required




    Dr. Bertrand Meyer originated the OCP term in his 1988
      book, Object Oriented Software Construction


Follow me at twitter.com/ardalis
Common Refactorings
    • Extract Interface / Apply Strategy Pattern
    • Parameterize Method
    • Form Template Method




Follow me at twitter.com/ardalis
OCP Fail




Follow me at twitter.com/ardalis
OCP Fail
     public 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;
     }

Follow me at twitter.com/ardalis
OCP OK
     private IEnumerable<ICustomerRule> _rules;

     public bool IsSpecialCustomer(Customer c)
     {
       foreach(var rule in _rules)
       {
         if(rule.Evaluate(c) == false) return false;
       }
       return true;
     }




Follow me at twitter.com/ardalis
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.

Follow me at twitter.com/ardalis
Common Refactorings
    • Collapse Hierarchy
    • Pull Up / Push Down Field
    • Pull Up / Push Down Method




Follow me at twitter.com/ardalis
Liskov Substitution Fail
     foreach(var employee in employees)
     {
       if(employee is Manager)
       {
         Helpers.PrintManager(employee as Manager);
         break;
       }
       Helpers.PrintEmployee(employee);
     }



Follow me at twitter.com/ardalis
Liskov Substitution OK
     foreach(var employee in employees)
     {
       employee.Print();
       // or
       Helpers.PrintEmployee(employee);
     }




Follow me at twitter.com/ardalis
Interface Segregation Principle
    The 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




Follow me at twitter.com/ardalis
Common Refactorings
    • Extract Interface




Follow me at twitter.com/ardalis
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);
     }




Follow me at twitter.com/ardalis
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);
     }

Follow me at twitter.com/ardalis
Dependency Inversion Principle
    High-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#




Follow me at twitter.com/ardalis
Dependency Inversion Principle
    • Depend on Abstractions
          – Interfaces, not concrete types


    • Inject Dependencies into Classes

    • Structure Solution so Dependencies Flow
      Toward Core
          – Onion Architecture

Follow me at twitter.com/ardalis
Application Layers




Follow me at twitter.com/ardalis
Compile Time
    Data Access Evolution                              Runtime

    No separation of concerns:
                                                     User Interface
     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
                                                       Database




Follow me at twitter.com/ardalis
Data Access : Helper                   Compile Time

    Classes                                  Runtime
     Calls to data made through a
      utility
                                         User Interface
     Example: Data Access
      Application Block (SqlHelper)

                                          Helper Class
     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                               Database




Follow me at twitter.com/ardalis
What’s Missing?                                         Compile Time

    Abstraction!                                                Runtime
     No way to abstract away
      data access                                  User Interface

     Tight coupling

     Leads to Big Ball of Mud              Core              Infrastructure
      system                            IFooRepository      SqlFooRepository

     Solution:
           Depend on interfaces, not
            concrete implementations
           What should we call such                 Database
            interfaces? Repositories!



Follow me at twitter.com/ardalis
DIP “Onion” Architecture




Follow me at twitter.com/ardalis
Common Dependencies
    •    Framework                  See also responsibilities:
    •    Third Party Libraries      • Persistence
                                    • Validation
    •    Database                   • Notification
    •    File System                • Error Handling
    •    Email                      • Logging
    •    Web Services               • Class Selection /
                                       Construction
    •    System Resources (Clock)   • Formatting
    •    Configuration              • Parsing
    •    The new Keyword            • Mapping
    •    Static methods
    •    Thread.Sleep
    •    Random



Follow me at twitter.com/ardalis
Common Refactorings
    •    Extract Class
    •    Extract Interface / Apply Strategy Pattern
    •    Extract Method
    •    Introduce Service Locator / Container




Follow me at twitter.com/ardalis
DIP Fail




Follow me at twitter.com/ardalis
Some Improvement (Façade)




Follow me at twitter.com/ardalis
DIP OK (Strategy)




Follow me at twitter.com/ardalis
DIP OK (Strategy)




Follow me at twitter.com/ardalis
Summary
    • Maintain / Improve Application Code
    • Follow DRY/SOLID Principles
    • Use Characterization Tests to “fix”
      behavior
    • Apply Common Refactorings
    • Re-run Tests After Refactorings



Follow me at twitter.com/ardalis
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/


Follow me at twitter.com/ardalis
Thank You! – Благодаря!

    Find Me Online:
    Blog: http://ardalis.com
    Twitter: @ardalis
    http://facebook.com/StevenAndrewSmith




Follow me at twitter.com/ardalis
Discussion




Follow me at twitter.com/ardalis

Refactoring with SOLID - Telerik India DevCon 2013

  • 1.
    Refactoring Applications using SOLID Principles Steve Smith Telerik ardalis.com @ardalis Follow me at twitter.com/ardalis
  • 2.
    Software Rots How?  Duplication  Excess Coupling  Quick Fixes  Hacks Follow me at twitter.com/ardalis
  • 4.
    Preventive Maintenance • Refactoring – Eliminate Duplication – Simplify Design • Automated Tests – Verify correctness – Avoid regressions – Increase Confidence Follow me at twitter.com/ardalis
  • 5.
    Refactoring Process • Verify existing behavior • Write Characterization Tests if none exist – Find test points – Break dependencies • Apply Refactoring • Confirm existing behavior is preserved Follow me at twitter.com/ardalis
  • 6.
    Characterization Tests Process 1. Write a test you know will fail 2. 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 Follow me at twitter.com/ardalis
  • 9.
    SOLID Principles http://flickr.com/photos/kevinkemmerer/2772526725
  • 10.
    Principles of OODesign 0. Don’t Repeat Yourself (DRY) 1. Single Responsibility 2. Open/Closed 3. Liskov Substitution 4. Interface Segregation 5. Dependency Inversion Follow me at twitter.com/ardalis
  • 12.
    Don’t Repeat Repeat Yourself • Duplication in logic calls for abstraction • Duplication in process calls for automation Follow me at twitter.com/ardalis
  • 13.
    Common Refactorings • Replace Magic Number/String • Parameterize Method • Pull Up Field • Pull Up Method • Replace Conditional With Polymorphism • Introduce Method Follow me at twitter.com/ardalis
  • 14.
    Role Checks if(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 } Follow me at twitter.com/ardalis
  • 16.
    Single Responsibility Principle The 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 Follow me at twitter.com/ardalis
  • 17.
    Example Responsibilities • Persistence • Validation • Notification • Error Handling • Logging • Class Selection / Construction • Formatting • Parsing • Mapping Follow me at twitter.com/ardalis
  • 18.
    Dependency and Coupling • Excessive coupling makes changing legacy software difficult • Breaking apart responsibilities and dependencies is a large part of working with existing code Follow me at twitter.com/ardalis
  • 19.
    Common Refactorings • Extract Class • Move Method Follow me at twitter.com/ardalis
  • 20.
    Heuristics and CodeSmells • Visual Studio Metrics Follow me at twitter.com/ardalis
  • 21.
    Code Smell: Regions More on Regions: http://ardalis.com/regional-differences Follow me at twitter.com/ardalis
  • 23.
    Open / ClosedPrinciple The Open / Closed Principle states that software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification. Wikipedia Follow me at twitter.com/ardalis
  • 24.
    Open / ClosedPrinciple Open to Extension New behavior can be added in the future Closed to Modification Changes to source or binary code are not required Dr. Bertrand Meyer originated the OCP term in his 1988 book, Object Oriented Software Construction Follow me at twitter.com/ardalis
  • 25.
    Common Refactorings • Extract Interface / Apply Strategy Pattern • Parameterize Method • Form Template Method Follow me at twitter.com/ardalis
  • 26.
    OCP Fail Follow meat twitter.com/ardalis
  • 27.
    OCP Fail public 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; } Follow me at twitter.com/ardalis
  • 28.
    OCP OK private IEnumerable<ICustomerRule> _rules; public bool IsSpecialCustomer(Customer c) { foreach(var rule in _rules) { if(rule.Evaluate(c) == false) return false; } return true; } Follow me at twitter.com/ardalis
  • 30.
    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. Follow me at twitter.com/ardalis
  • 31.
    Common Refactorings • Collapse Hierarchy • Pull Up / Push Down Field • Pull Up / Push Down Method Follow me at twitter.com/ardalis
  • 32.
    Liskov Substitution Fail foreach(var employee in employees) { if(employee is Manager) { Helpers.PrintManager(employee as Manager); break; } Helpers.PrintEmployee(employee); } Follow me at twitter.com/ardalis
  • 33.
    Liskov Substitution OK foreach(var employee in employees) { employee.Print(); // or Helpers.PrintEmployee(employee); } Follow me at twitter.com/ardalis
  • 35.
    Interface Segregation Principle The 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 Follow me at twitter.com/ardalis
  • 36.
    Common Refactorings • Extract Interface Follow me at twitter.com/ardalis
  • 37.
    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); } Follow me at twitter.com/ardalis
  • 38.
    ISP OK (forCQRS 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); } Follow me at twitter.com/ardalis
  • 40.
    Dependency Inversion Principle High-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# Follow me at twitter.com/ardalis
  • 41.
    Dependency Inversion Principle • Depend on Abstractions – Interfaces, not concrete types • Inject Dependencies into Classes • Structure Solution so Dependencies Flow Toward Core – Onion Architecture Follow me at twitter.com/ardalis
  • 42.
    Application Layers Follow meat twitter.com/ardalis
  • 43.
    Compile Time Data Access Evolution Runtime No separation of concerns: User Interface  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 Database Follow me at twitter.com/ardalis
  • 44.
    Data Access :Helper Compile Time Classes Runtime  Calls to data made through a utility User Interface  Example: Data Access Application Block (SqlHelper) Helper Class  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 Database Follow me at twitter.com/ardalis
  • 45.
    What’s Missing? Compile Time Abstraction! Runtime  No way to abstract away data access User Interface  Tight coupling  Leads to Big Ball of Mud Core Infrastructure system IFooRepository SqlFooRepository  Solution:  Depend on interfaces, not concrete implementations  What should we call such Database interfaces? Repositories! Follow me at twitter.com/ardalis
  • 46.
    DIP “Onion” Architecture Followme at twitter.com/ardalis
  • 47.
    Common Dependencies • Framework See also responsibilities: • Third Party Libraries • Persistence • Validation • Database • Notification • File System • Error Handling • Email • Logging • Web Services • Class Selection / Construction • System Resources (Clock) • Formatting • Configuration • Parsing • The new Keyword • Mapping • Static methods • Thread.Sleep • Random Follow me at twitter.com/ardalis
  • 48.
    Common Refactorings • Extract Class • Extract Interface / Apply Strategy Pattern • Extract Method • Introduce Service Locator / Container Follow me at twitter.com/ardalis
  • 49.
    DIP Fail Follow meat twitter.com/ardalis
  • 50.
    Some Improvement (Façade) Followme at twitter.com/ardalis
  • 51.
    DIP OK (Strategy) Followme at twitter.com/ardalis
  • 52.
    DIP OK (Strategy) Followme at twitter.com/ardalis
  • 53.
    Summary • Maintain / Improve Application Code • Follow DRY/SOLID Principles • Use Characterization Tests to “fix” behavior • Apply Common Refactorings • Re-run Tests After Refactorings Follow me at twitter.com/ardalis
  • 54.
    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/ Follow me at twitter.com/ardalis
  • 55.
    Thank You! –Благодаря! Find Me Online: Blog: http://ardalis.com Twitter: @ardalis http://facebook.com/StevenAndrewSmith Follow me at twitter.com/ardalis
  • 56.
    Discussion Follow me attwitter.com/ardalis

Editor's Notes

  • #7 Note that characterization tests, though they should be automated, are often not what we would think of as unit tests, or perhaps even integration tests. For instance, you could dump a log file showing the relevant state of the application, and then use that as the basis for your characterization test by comparing against it after your changes.
  • #8 Avoid creating a big ball of mud system, where tracing through your code and its dependencies is like trying to unwind a tangled mess of spaghetti.
  • #15 A very common source of repetition of code is role checks. These often describe different scenarios in different circumstances. For instance, maybe administrators can do anything, but managers can access resources within their division, etc. Encapsulating the logic of CanView, CanCreate, CanEdit, etc. in privilege objects makes these rules explicit, easier to test, and gives them a single location to live in the application.
  • #21 Visual Studio can quickly analyze a project and show statistics for the classes and methods in the project. The maintainability index, cyclomatic complexity, and lines of code are all great metrics to pay attention to. The ideal maintainability index is 100, but don’t expect to hit that with any code that’s doing real work. However, you should certainly able to keep it above 50.
  • #22 I’m not a fan of regions. They mainly exist because at one time they were a reasonable means of hiding generated code, before we had support for partial classes and other language features to deal with this. The worst offense with regions is when they’re used within a method, like this:(click)They’re also bad when used at the class level for “standard” formatting of code, making it impossible to actually see what the code does, like this:(click)Can someone tell me what this class does?(click)I have a whole article devoted to why using regions is a bad habit, anti-pattern, code smell, whatever you prefer. It includes some survey results on the most common ways people use them as well. (click)
  • #28 What happens when we need to add another country?What happens when we must add another rule?How can we refactor this so this method no longer needs to change?
  • #29 Define a type to describe a rule. Move each rule into its own type. Create a collection of rules to apply and apply them.Pass the set of rules into the IsSpecialCustomer() method’s class (or even the method itself).
  • #33 Any time you find that you need to check the type of an object within a polymorphic block of code (such as a foreach), this is a sign that you are breaking LSP.
  • #38 This is anextemely common example of the Repository design pattern. In fact, I use this exact pattern in quite a few production applications today. There’s nothing inherently wrong with this implementation on its own. However, sometimes it does violate ISP if you need to separate Commands from Queries
  • #39 You can create small interfaces and compose the larger interfaces from the smaller ones if you control all of the code and you can’t simply do away with the larger interfaces. In this case, the separation of interfaces would allow us to do something like implement caching only on the read operations, and implement delayed writes using some kind of queue or message bus for the write operations.
  • #52 Extract interfaceImplement interface with tightly coupled original code
  • #55 http://en.wikipedia.org/wiki/Template_method_pattern