Refactoring with SOLID Principles (FalafelCon 2013)


Published on

We’ve all heard about well-designed software projects, where things aren’t (yet) a big ball of mud that’s painful to work with, but many of us aren’t lucky enough to work on these. Nor is it an option for us to get to start a brand new “greenfield” project, where we can leave behind the legacy of spaghetti code and technical debt our current project might have.

But there is hope! By applying refactoring steps based on SOLID principles of object oriented design, we can reduce the technical debt of our existing application, improve our code quality, and hopefully make our application more enjoyable and productive to work with.

In this session, we’ll briefly introduce the SOLID principles and several design patterns, and then we’ll apply these principles and patterns by improving the design of an existing application through several refactoring steps.

Published in: Technology
  • Be the first to comment

  • Be the first to like this

No Downloads
Total views
On SlideShare
From Embeds
Number of Embeds
Embeds 0
No embeds

No notes for slide
  • 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.
  • 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.
  • 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.
  • 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.
  • 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)
  • 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?
  • 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).
  • 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.
  • 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
  • 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.
  • Extract interfaceImplement interface with tightly coupled original code
  • Refactoring with SOLID Principles (FalafelCon 2013)

    1. 1. Follow me at Applicationsusing SOLID PrinciplesSteve @ardalis
    2. 2. Follow me at on this topicComing Soon: Refactoring Fundamentals
    3. 3. Follow me at
    4. 4. Follow me at Maintenance• Refactoring– Eliminate Duplication– Simplify Design• Automated Tests– Verify correctness– Avoid regressions– Increase Confidence
    5. 5. Follow me at Process• Verify existing behavior• Write Characterization Tests if none exist– Find test points– Break dependencies• Apply Refactoring• Confirm existing behavior is preserved
    6. 6. Follow me at TestsProcess1. Write a test you know will fail2. Use the output of the failing test todetermine the existing behavior to assert3. Update the test with the newvalue/behavior4. Run the test again – it should pass
    7. 7. S O L I DPrinciples
    8. 8. Follow me at of OO Design0. Don’t Repeat Yourself (DRY)1. Single Responsibility2. Open/Closed3. Liskov Substitution4. Interface Segregation5. Dependency Inversion
    9. 9. Follow me at’t RepeatRepeat Yourself• Duplication in logic calls for abstraction• Duplication in process calls forautomation
    10. 10. Follow me at Refactorings• Replace Magic Number/String• Parameterize Method• Pull Up Field• Pull Up Method• Replace Conditional With Polymorphism• Introduce Method
    11. 11. Follow me at Checksif(user.IsInRole(“Admins”){// allow access to resource}// favor privileges over role checks// priv = new ContentPrivilege(user, article);if(priv.CanEdit()){// allow access}
    12. 12. Follow me at Responsibility PrincipleThe Single Responsibility Principle states that every objectshould have a single responsibility, and thatresponsibility should be entirely encapsulated by theclass.WikipediaThere should never be more than one reason for a class tochange.Robert C. “Uncle Bob” Martin
    13. 13. Follow me at Responsibilities• Persistence• Validation• Notification• Error Handling• Logging• Class Selection / Construction• Formatting• Parsing• Mapping
    14. 14. Follow me at and Coupling• Excessive coupling makes changinglegacy software difficult• Breaking apart responsibilities anddependencies is a large part of workingwith existing code
    15. 15. Follow me at Refactorings• Extract Class• Move Method
    16. 16. Follow me at and Code Smells• Visual Studio Metrics
    17. 17. Follow me at Smell: RegionsMore on Regions:
    18. 18. Follow me at / Closed PrincipleThe Open / Closed Principle states that software entities(classes, modules, functions, etc.) should be open forextension, but closed for modification.Wikipedia
    19. 19. Follow me at / Closed PrincipleOpen to ExtensionNew behavior can be added in the futureClosed to ModificationChanges to source or binary code are not requiredDr. Bertrand Meyer originated the OCP term in his 1988book, Object Oriented Software Construction
    20. 20. Follow me at Refactorings• Extract Interface / Apply Strategy Pattern• Parameterize Method• Form Template Method
    21. 21. Follow me at Fail
    22. 22. Follow me at OK
    23. 23. Follow me at 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;}
    24. 24. Follow me at OKprivate IEnumerable<ICustomerRule> _rules;public bool IsSpecialCustomer(Customer c){foreach(var rule in _rules){if(rule.Evaluate(c) == false) return false;}return true;}
    25. 25. Follow me at Substitution PrincipleThe Liskov Substitution Principle states thatSubtypes must be substitutable for theirbase types.Agile Principles, Patterns, and Practices inC#Named for Barbara Liskov, who firstdescribed the principle in 1988.
    26. 26. Follow me at Refactorings• Collapse Hierarchy• Pull Up / Push Down Field• Pull Up / Push Down Method
    27. 27. Follow me at Substitution Failforeach(var employee in employees){if(employee is Manager){Helpers.PrintManager(employee as Manager);break;}Helpers.PrintEmployee(employee);}
    28. 28. Follow me at Substitution OKforeach(var employee in employees){employee.Print();// orHelpers.PrintEmployee(employee);}
    29. 29. Follow me at Segregation PrincipleThe Interface Segregation Principle states thatClients should not be forced to depend onmethods they do not use.Agile Principles, Patterns, and Practices in C#Corollary:Prefer small, cohesive interfaces to “fat” interfaces
    30. 30. Follow me at Refactorings• Extract Interface
    31. 31. Follow me at Fail (sometimes)public IRepository<T>{T GetById(int id);IEnumerable<T> List();void Create(T item);void Update(T item);void Delete(T item);}
    32. 32. Follow me at 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);}
    33. 33. Follow me at Inversion PrincipleHigh-level modules should not depend on low-levelmodules. Both should depend on abstractions.Abstractions should not depend on details. Detailsshould depend on abstractions.Agile Principles, Patterns, and Practices in C#
    34. 34. Follow me at Inversion Principle• Depend on Abstractions– Interfaces, not concrete types• Inject Dependencies into Classes• Structure Solution so Dependencies FlowToward Core– Onion Architecture
    35. 35. Follow me at Layers
    36. 36. Follow me atUser InterfaceDatabaseCompile TimeRuntime
    37. 37. Follow me atUser InterfaceDatabaseCompile TimeRuntimeHelper Class
    38. 38. Follow me atUser InterfaceDatabaseCompile TimeRuntimeCoreIFooRepositoryInfrastructureSqlFooRepository
    39. 39. Follow me at “Onion” Architecture
    40. 40. Follow me at Dependencies• Framework• Third Party Libraries• Database• File System• Email• Web Services• System Resources (Clock)• Configuration• The new Keyword• Static methods• Thread.Sleep• RandomSee also responsibilities:• Persistence• Validation• Notification• Error Handling• Logging• Class Selection /Construction• Formatting• Parsing• Mapping
    41. 41. Follow me at Refactorings• Extract Class• Extract Interface / Apply Strategy Pattern• Extract Method• Introduce Service Locator / Container
    42. 42. Follow me at Fail
    43. 43. Follow me at Improvement (Façade)
    44. 44. Follow me at OK (Strategy)
    45. 45. Follow me at OK (Strategy)
    46. 46. Follow me at• Maintain / Improve Application Code• Follow DRY/SOLID Principles• Use Characterization Tests to “fix”behavior• Apply Common Refactorings• Re-run Tests After (and during)Refactorings
    47. 47. Follow me at
    48. 48. Follow me at
    49. 49. Follow me at You!Find Me Online:Blog: http://ardalis.comTwitter: @ardalis
    50. 50. Follow me at