Presented at DevReach 2013.
As developers, most of our time is spent working on existing software. Sure, occasionally we get the chance to fire up a new Solution in Visual Studio, and that can be exciting. But after the first day, we find ourselves once more having to deal with the work we did yesterday. And today, we know more than we did yesterday, so there are things we’d do differently, if we had it to do over.
Over time, software rots. If we’re not diligent, our beautiful code can degrade into a worthless mess. Keeping our code in working condition is no different than changing the oil in our car – it’s preventive maintenance. In this session, Steve will look at some common places to look for signs of degradation in existing applications, and steps to take to improve the code. Examples will use C# and primarily ASP.NET.
10. Refactoring Process
• Verify existing behavior
• Write Characterization Tests if none exist
– Find test points
– Break dependencies
• Apply Refactoring
• Confirm existing behavior is preserved
11. 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
12.
13.
14. S O L I DPrinciples
http://flickr.com/photos/kevinkemmerer/2772526725
18. Common Refactorings
• Replace Magic Number/String
• Parameterize Method
• Pull Up Field
• Pull Up Method
• Replace Conditional With Polymorphism
• Introduce Method
19. 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
}
20.
21. 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
23. Dependency and Coupling
• Excessive coupling makes changing
legacy software difficult
• Breaking apart responsibilities and
dependencies is a large part of working
with existing code
28. 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
29. 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
34. 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;
}
35.
36. 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.
41. 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
46. 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);
}
47.
48. 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#
49. Dependency Inversion Principle
• Depend on Abstractions
– Interfaces, not concrete types
• Inject Dependencies into Classes
• Structure Solution so Dependencies Flow
Toward Core
– Onion Architecture (a.k.a. Ports and Adapters)
51. Data Access Evolution
No 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
52. 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
53. 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
Core
IFooRepository
Infrastructure
SqlFooRepository
55. 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
56. Common Refactorings
• Extract Class
• Extract Interface / Apply Strategy Pattern
• Extract Method
• Introduce Service Locator / Container
61. Self-Improvement and Quality
• How fast can you produce:
– Code you believe to be of high quality
– Code that maybe gets the job done, but you
believe to be of low quality
• Which one can you produce more quickly?
• Why?
• How can we develop our skills and our tools
so that building quality is natural and easier
than not doing so?