Design patterns provide common templates for solving similar problems. They also provide a higher-level language for software developers to use to describe approaches they might choose when designing part of an application. This session introduces and applies several patterns useful to web application developers. Examples will primarily use C#/.NET.
10. Singleton: Intent
Ensure a class has only one instance
Make the class itself responsible for
keeping track of its sole instance
“There can be only one”
14. Singleton Consequences
The default implementation is not thread-safe
Avoid in multi-threaded environments
Avoid in web server scenarios (e.g. ASP.NET)
Introduce tight coupling among collaborating
classes
Singletons are notoriously difficult to test
Commonly regarded as an anti-pattern
Some commercial tools
can be used to mock and
test Singletons
But why would you
intentionally write code
you can only test with
certain premium tools?
15. Singleton Consequences
Violate the Single Responsibility
Principle
Class is responsible for managing its
instances as well as whatever it does
Using an IOC Container it is
straightforward to avoid the coupling
and testability issues
17. Object Lifetime in ASP.NET Core
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
// you don’t need to implement singleton behavior in class!
services.AddSingleton<ISomeService>();
}
18. Singleton behavior is fine and
often desirable.
Implementing this behavior using the
Singleton Pattern on the class itself is
usually not ideal.
20. Strategy: Intent
Encapsulate a family of related algorithms
Let the algorithm vary and evolve independently from the class(es) that use it
Allow a class to maintain a single purpose
Single Responsibility Principle (SRP)
Also enables Open/Closed Principle (OCP)
Learn more about SRP and OCP in my Pluralsight course:
http://bit.ly/SOLID-OOP
22. Strategy : Common Usage
Dependency Inversion and Dependency Injection
Decouple class dependencies and responsibilities
Refactoring Steps
Create interfaces for each responsibility
Inject the implementation of the interface via the constructor
Move the implementation to a new class that implements the interface
23. Hidden Dependencies
Classes should declare their dependencies via their constructor
Follow the Explicit Dependencies Principle
Avoid hidden dependencies
Anything the class needs but which is not passed into constructor
Avoid making non-stateless static calls
Avoid directly instantiating classes (except those with no dependencies, like
strings)
Instead, move these calls to an interface, and call a local instance of the interface
24. “New is Glue”
Be conscious of the
consequences of using
“new”
If you need loose coupling,
replace “new” with Strategy
Pattern
http://flic.kr/p/aN4Zv
“new” creates tight coupling
between classes
28. Data Access Evolution
Initially 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
29. 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
30. 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
31. Repository
A Data Access Pattern
Introduced as part of Domain-Driven Design, but very popular outside of DDD as well
Separates persistence responsibility from business classes
Enables
Single Responsibility Principle
Separation of Concerns
Testability
32. Repository
Frequently Separate Interfaces for Each Entity in
Application
E.g. CustomerRepository, OrderRepository, etc.
Or using Generics: IRepository<TEntity>
May also organize Repositories based on
Reads vs. Writes
Bounded Contexts (a Domain-Driven Design concept)
33. Consider!
Repositories should return domain objects
Don’t let persistence concerns leak through the repository interface
What should Repository List methods return?
IEnumerable<T>
IQueryable<T>
What about deferred execution?
What about lazy loading?
35. Repository - Example
(1)
Create/extend an interface to represent
the data access you need
(2)
Copy the
implementation from
your class into a new
class implementing this
interface.
(3)
Inject the interface using the
Strategy Pattern. Use the local field
of this interface type in place of
previous implementation code.
36. Where do Repositories Live?
Place interfaces in Core
Core includes Model classes and most business logic
Core has no dependencies (ideally)
Place implementation in Infrastructure
Infrastructure references Core
UI layer (app entry point) references Core
Accesses Infrastructure at runtime
Responsible for creating object graph, either manually or via an
IOC Container
38. Proxy
A class that controls access to another
Implemented via subclass or via
delegation using a common interface
Frequent use cases:
Remote Proxy for web services / network calls
Lazy loading implementations
Security / access control
42. Adding Caching to a Repository
Caching is frequently added to query methods in repositories
Caching logic is a separate concern and should live in a separate class from the
main data access logic
Proxy pattern provides a straightforward means to control access to the “real”
data, versus the cache
47. Command
Represent an action as an object
Decouple performing the action from the client that is issuing
the command
A Command is a message
Common scenarios:
Delayed execution
Logging activity
Enabling Undo / Revoke Transaction functionality
48. Command : Usage
Combine with messaging for scalability
What happens when you complete an order from Amazon?
50. Factory: Intent
Separate object creation from the decision of which object to create
Defer creation of objects
Only create them if and when needed
Add new classes and functionality without breaking client code
Only factory needs to know about new types available
Store possible objects to create outside of program
Configuration
Persistent Storage
Plug-in assemblies
51. Lazy<T>
Provide simple built-in Factory behavior for lazy loading
Available in .NET Framework 4, 4.5, later
Learn more:
http://msdn.microsoft.com/en-us/magazine/ff898407.aspx
54. Alternate Factories
Write your own method
Use an IOC Container
Lambda expressions
Func<ExpensiveObject> factory
() => new ExpensiveObject();
“IOC Containers are
basically just factories on
steroids.”
57. Nulls
“I call it my billion-dollar mistake. It
was the invention of the null
reference in 1965.”
Sir Charles Antony Richard Hoare
58. Null Object
Replace null with an actual instance object with
default values
Reduce number of null checks in application
Allow methods on this “null” object to be called,
without generating reference exceptions
59. Use When…
A method requires a collaborator
And you want to ensure it is never null
It’s necessary to support a “do nothing” option
Especially with Command or Strategy
60. Implementation
BaseType
Real Objects
The Null object is simply another implementation of a base
type (class or interface)
For ease of use, it is often added as a static property on BaseType (e.g.
BaseType.Null or BaseType.NotSet)
NullObject
61. Nulls Break Polymorphism
Polymorphism is a key benefit of OO systems
Null breaks polymorphism – calling methods on null instances results in
exceptions
64. Practice PDD
Pain Driven Development
If it’s causing pain, fix it.
“Don’t apply every pattern
you know to a problem
from the start; practice
Pain Driven Development.”
This is where a lot of people work. Don’t be these guys. When you have a lot of wood to cut, it pays off to periodically sharpen the saw. Sharpening the saw is one of the seven habits of highly effective people. Take time to improve your skills, and practice, and in the long run you will be more effective.
Contact me if you’d like a 30-day pass to Pluralsight. Please consider some of my courses on design patterns, architecture, and domain-driven design.
General, reusable solutions to common problems
Not a complete, finished solution
A template, recipe, or approach to solving certain problems
Each has a specific name, so we can discuss common elements of design using a common vocabulary
Learning anything – a musical chord, a martial arts technique, etc.
Zero: You used a what? Never heard of it.
Awakening: Wow, I just learned how XYZ pattern can improve my design. I’m not really sure where it would work in my code, but I’m definitely looking.
Overzealous: I totally “get” the XYZ pattern; I’m adding it everywhere I can shoehorn it into my code. My design’s gonna be better now, for sure!
Mastery: In response to some specific design pain points, the XYZ pattern was a logical next step and was achieved through a simple refactoring.
Design pattern names provide placeholders or complex abstractions and sets of refactorings.
Consider the difference between:
We have some tight coupling to the database here. We can probably fix it if we apply these refactorings;
extract an interface,
extract a method,
Extract a class,
Replace local with parameter
OR
Let’s apply the Repository pattern to fix it.
Used Regularly or Daily by most respondents
Factory (194)
Repository (185)
Iterator (139)
Adapter/Wrapper (122)
Observer (113)
Command (106)
Singleton (105)
Strategy (101)
Proxy (91)
This joke is probably dating me – how many of you know the Highlander these days?
It’s possible for the new Singleton(); line to be called more than once.
Jon Skeet lays out several different ways to implement the Singleton pattern in C#, with different performance and thread safety considerations.
Here are three example registration commands using an IOC container, in this case StructureMap. If you’re not familiar with IOC containers, they’re basically just factories on steroids, which you configure to create instances of types according to certain rules.
In the first case, one new instance of the type is created per thread, or per HTTP request. Thus, in an ASP.NET application, a new DbContext will be created with each new web request, but if we used this same code in a non-web context, such as in an integration test, it would create a single instance per thread. In the second case, the Singleton() call will literally configure the lifetime of the instance to be a singleton, with one and only one instance available for the life of this application. In the last, default case, each new request for an instance will get its own, separate instance of the requested type.
The benefit of this approach is that it makes it very clear which objects are configured with which lifetimes, and none of the objects themselves are cluttered with these concerns. The resulting code has less repetition, is simpler, and its classes have fewer responsibilities.
Add Kragle slide next
If you’re working with web forms, whether adding new features or maintaining a large codebase in which better dependency management would help, realize that you can use dependency injection and the strategy pattern there, too. Doing so with Structuremap typically follows three steps.
Create a basepage class and call ObjectFactory.BuildUp(this) in its constructor. This will populate any properties Structuremap knows about in the object.
Configure StructureMap to know about certain types by telling it to set all properties of that type. In this case, we’re telling Structuremap to populate any property it finds of type IAlbumRepository
In your actual page, add a property of the interface type you want injected. You can now count on this property being supplied by Structuremap.
Remember, although your current strategy may be beautiful, you need to occasionally check to see how well it’s actually working. Using the strategy pattern, if you find that a particular implementation isn’t working, you can easily swap it out for another one.
In my own career as a developer, I’ve seen an evolution in how most data access is performed. I started with classic ASP and soon after, the first version of ASP.NET. In both cases, there was typically little separation of concerns between UI, business rules, and data access. This tended to result in a lot of duplicate concepts and code, as well as direct dependence on a database.
To address the duplication, helper classes were often created to take repetitive tasks and centralize them. However, in this case the runtime program flow still mirrored the compile time dependency tree, in both cases targeting a database. This made testing such applications difficult.
The problem with these approaches is that there is no abstraction layer between the application and its infrastructure – in this case the database.
Consider grabbing repository image from DDD slide here
Split into two slides
Note – this cannot easily be tested without an actual database instance in place.
Add image showing Onion Architecture and/or a simple solution with Core, Infrastructure, and Web.
Note that the structure of the Proxy is very similar to the Strategy pattern, which we already saw (click). The key difference between the two is in the intent. Note that this is just one Proxy implementation – it can also be achieved with a proxy class that inherits from the subject class, rather than sharing a common interface implementation.
TODO: Show an example of adding caching logic to a repository without any separation of concerns.
Consider starting with an MVC method that does direct data access without any repository, uses caching, etc, and lament its lack of testability. Perhaps a Music Store method, maybe with some modification.
Maybe look for an image representing military orders being received
Ask about Amazon and what actually happens when you place an order there, compared to with most other online stores.
The background for this slide is the FirstOrDefault image I liked for the Null Object pattern
Some polymorphic code
Change this to something that isn’t displayed in the UI layer, such as a function that must be called or a number to be summed.
Apply patterns via refactoring,
Not Big Design Up Front
Show the MvcMusicStore application
Hg clone https://hg.codeplex.com/forks/ssmith/mvcmusicstorerepositorypattern
Talk about the CacheDependencyBug in AdSignia and how it was introduced by moving a hard-coded instantiation to a parameter, and then fixed by replacing the parameter with a Factory (lambda).
Update with imagery and review of patterns and when to use them. Fewer words.
Don’t be too busy to improve
Avoid adding coupling to your system with Singleton
Decouple specific implementation from use with Strategy
Avoid hidden dependencies; remember New is Glue!
Decouple your database with Repository
Decouple caching, lazy loading, and other access concerns with Proxy
Decouple execution of other actions with Commands
Decouple construction with Factory – remember Lazy<T> and lambda as factory shortcuts
Cut down on prolific null checks with the Null Object Pattern
Don’t try to use every pattern up front – practice Pain Driven Development
Update with imagery and review of patterns and when to use them. Fewer words.
Don’t be too busy to improve
Avoid adding coupling to your system with Singleton
Decouple specific implementation from use with Strategy
Avoid hidden dependencies; remember New is Glue!
Decouple your database with Repository
Decouple caching, lazy loading, and other access concerns with Proxy
Decouple execution of other actions with Commands
Decouple construction with Factory – remember Lazy<T> and lambda as factory shortcuts
Cut down on prolific null checks with the Null Object Pattern
Don’t try to use every pattern up front – practice Pain Driven Development
Update with imagery and review of patterns and when to use them. Fewer words.
Don’t be too busy to improve
Avoid adding coupling to your system with Singleton
Decouple specific implementation from use with Strategy
Avoid hidden dependencies; remember New is Glue!
Decouple your database with Repository
Decouple caching, lazy loading, and other access concerns with Proxy
Decouple execution of other actions with Commands
Decouple construction with Factory – remember Lazy<T> and lambda as factory shortcuts
Cut down on prolific null checks with the Null Object Pattern
Don’t try to use every pattern up front – practice Pain Driven Development
Update with imagery and review of patterns and when to use them. Fewer words.
Don’t be too busy to improve
Avoid adding coupling to your system with Singleton
Decouple specific implementation from use with Strategy
Avoid hidden dependencies; remember New is Glue!
Decouple your database with Repository
Decouple caching, lazy loading, and other access concerns with Proxy
Decouple execution of other actions with Commands
Decouple construction with Factory – remember Lazy<T> and lambda as factory shortcuts
Cut down on prolific null checks with the Null Object Pattern
Don’t try to use every pattern up front – practice Pain Driven Development
Update with imagery and review of patterns and when to use them. Fewer words.
Don’t be too busy to improve
Avoid adding coupling to your system with Singleton
Decouple specific implementation from use with Strategy
Avoid hidden dependencies; remember New is Glue!
Decouple your database with Repository
Decouple caching, lazy loading, and other access concerns with Proxy
Decouple execution of other actions with Commands
Decouple construction with Factory – remember Lazy<T> and lambda as factory shortcuts
Cut down on prolific null checks with the Null Object Pattern
Don’t try to use every pattern up front – practice Pain Driven Development
Update with imagery and review of patterns and when to use them. Fewer words.
Don’t be too busy to improve
Avoid adding coupling to your system with Singleton
Decouple specific implementation from use with Strategy
Avoid hidden dependencies; remember New is Glue!
Decouple your database with Repository
Decouple caching, lazy loading, and other access concerns with Proxy
Decouple execution of other actions with Commands
Decouple construction with Factory – remember Lazy<T> and lambda as factory shortcuts
Cut down on prolific null checks with the Null Object Pattern
Don’t try to use every pattern up front – practice Pain Driven Development
Update with imagery and review of patterns and when to use them. Fewer words.
Don’t be too busy to improve
Avoid adding coupling to your system with Singleton
Decouple specific implementation from use with Strategy
Avoid hidden dependencies; remember New is Glue!
Decouple your database with Repository
Decouple caching, lazy loading, and other access concerns with Proxy
Decouple execution of other actions with Commands
Decouple construction with Factory – remember Lazy<T> and lambda as factory shortcuts
Cut down on prolific null checks with the Null Object Pattern
Don’t try to use every pattern up front – practice Pain Driven Development
Decouple construction with Factory
Remember Lazy<T> and lambda as factory shortcuts
Remember IOC containers are factories on steroids
Decouple construction with Factory
Remember Lazy<T> and lambda as factory shortcuts
Remember IOC containers are factories on steroids
Don’t try to apply every pattern you know up front.