0% found this document useful (0 votes)
2 views79 pages

6 Design Patterns to Improve Your Web Apps

The document outlines six design patterns to enhance web application development, including Singleton, Strategy, Repository, Proxy, Command, and Builder. Each pattern is explained with its intent, structure, common usage, and consequences, emphasizing the importance of decoupling and reducing complexity in code. The document also encourages practicing Pain Driven Development to address issues as they arise.

Uploaded by

aliayaydin715
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
2 views79 pages

6 Design Patterns to Improve Your Web Apps

The document outlines six design patterns to enhance web application development, including Singleton, Strategy, Repository, Proxy, Command, and Builder. Each pattern is explained with its intent, structure, common usage, and consequences, emphasizing the importance of decoupling and reducing complexity in code. The document also encourages practicing Pain Driven Development to address issues as they arise.

Uploaded by

aliayaydin715
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 79

6 Design Patterns

to Improve
Your Web Apps
Steve Smith (@ardalis)
steve@ardalis.com
Ardalis.com
Podcast: WeeklyDevTips.com
Design Patterns

http://flic.kr/p/8XgqKu
Stages of Learning

Stage Zero - Ignorance

Stage One - Awakening

Stage Two - Overzealous

Stage Three – Mastery

http://flic.kr/p/6StgW5
Language

http://flic.kr/p/6UpMt9
Design Patterns

Iterator

Unit of
Work Composite

Specification
Most Useful (for web app design)
 (Singleton)
 Strategy
 Repository
 Proxy / Decorator
 Command
 Builder
 Null Object
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”


Singleton Structure (not thread-safe)
Singleton Structure
(thread-safe and fast)

Source: http://csharpindepth.com/Articles/General/Singleton.aspx
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
Some commercial tools
can be used to mock and
 Singletons are notoriously difficult to test test Singletons
 Commonly regarded as an anti-pattern
But why would you
intentionally write code
you can only test with
certain premium tools?
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
Managing Object Lifetime
Using an IOC Container (StructureMap)
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>();
}
Singleton behavior is fine and
often desirable.

Implementing this behavior using the


Singleton Pattern on the class itself is
usually not ideal.
Strategy

http://flic.kr/p/6spkwo
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
Strategy : Structure
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
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
“New is Glue”
“new” creates tight coupling
between classes

Be conscious of the
consequences of using
“new”

If you need loose coupling,


replace “new” with Strategy
Pattern

http://flic.kr/p/aN4Zv
Also works with WebForms
Repository
Data Access Evolution
Compile Time
Initially no separation of concerns
Runtime

 Data access logic baked directly into UI


 ASP.NET Data Source Controls User Interface
 Classic ASP scripts

 Data access logic in UI layer via codebehind


 ASP.NET Page_Load event
 ASP.NET Button_Click event

Database
Data Access : Helper
Classes Compile Time

 Calls to data made through a Runtime


utility

User Interface
 Example: Data Access
Application Block (SqlHelper)

 Logic may still live in UI layer Helper Class

 Or a Business Logic Layer may


make calls to a Data Access
Layer which might then call the
helper
Database
What’s Missing?
Abstraction! Compile Time

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!
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
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)
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?


Avoid Lazy Loading in Web Apps
Avoid Lazy Loading in Web Apps
@{
ViewBag.Title = "Home Page";

22 Queries!
}
@model IEnumerable<LazyLoadingMvc5.Models.Session>

<h2>Sessions</h2>

@foreach (var session in Model)


{
<div class="row">
<h3>@session.Name</h3>
<h4>@string.Join(",", session.SpeakerSessions?.Select(ss => ss.Speaker.Name).ToArray())</h4>
<h4>@string.Join(",", session.SessionTags?.Select(st => st.Tag.Name).ToArray())</h4>
<p>@session.Description</p>
</div>
}
https://ardalis.com/avoid-lazy-loading-entities-in-asp-net-applications
Repository - Example
Repository - Example
(2)
Copy the
implementation from
(1) your class into a new
Create/extend an interface to represent class implementing this
the data access you need interface.

(3)
Inject the interface using the
Strategy Pattern. Use the local field
of this interface type in place of
previous implementation code.
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
Proxy
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
Proxy - Structure
Bonus Pattern: Decorator!
Stacking Patterns
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
Proxy – CachedRepository Implementation
Implementing with IOC (StructureMap)
// Doesn’t work:
x.For<IAlbumRepository>().Use<CachedAlbumRepository>();

// Strategy Pattern with Proxy Pattern (using composition)


x.For<IAlbumRepository>().Use<CachedAlbumRepository>()
.Ctor<IAlbumRepository>().Is<EfAlbumRepository>();
Implementing with IOC (.NET Core)

services.AddTransient<IDataService, DataService>((ctx) =>


{
IOtherService svc = ctx.GetService<IOtherService>();
return new DataService(svc);
});
Command

http://flic.kr/p/5Yb5i
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
Command : Usage
 Combine with messaging for scalability

 What happens when you complete an order from Amazon?


Builder
Intent
 A creational pattern

 Used to provide a simple mechanism for constructing


complex objects (or many variations of an object)

 “The intent of the Builder design pattern is to separate the


construction of a complex object from its representation”
Common Uses in ASP.NET Core
 WebHostBuilder

var host = new WebHostBuilder()


.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseStartup<Startup>()
.Build();
Common Uses in ASP.NET Core
 Configuration

var builder = new ConfigurationBuilder()


.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json);

if (env.IsDevelopment())
{
builder.AddUserSecrets<Startup>();
}

builder.AddEnvironmentVariables();
Configuration = builder.Build();
Builder for Test Data (TypeScript)
export class PrincipalBuilder {
private _principal: Principal = this.getTestPrincipal();
constructor() {}

public withName(name: string) {


this._principal.name = name;
return this;
}

public build() {
const newPrincipal = Object.assign({}, this._principal);
this._principal = this.getTestPrincipal();
return newPrincipal;
}
// getTestPrincipal
}
Builder for Test Data (TypeScript) -
Usage
let builder: PrincipalBuilder;
builder = new PrincipalBuilder();

it('returns 2 principals successfully', () => {


const testPrincipals: Principal[] = [
builder.withName('test principal 1').build(),
builder.withName('test principal 2').build()
];
principalService.getPrincipals().subscribe(principals => {
expect(principals.length).toBe(2);
expect(principals).toEqual(testPrincipals);
});
Builder for Test Data (C#)
public class AddressDTOBuilder
{
private AddressDTO _entity = new Address;

public AddressBuilder WithId(int id)


{
_entity.Id = id;
return this;
}

// more methods omitted

public AddressDTO Build()


{
return _entity;
}
}
Builder for Test Data (C#) - Usage
_testAddress = new AddressDTOBuilder()
.WithTestValues()
.Id(TEST_ADDRESS_ID1)
.Build();
_testAddress2 = new AddressDTOBuilder()
.WithTestValues()
.Id(TEST_ADDRESS_ID2)
.Line1("A Different Test Street")
.City("Columbus")
.ZipCode("43200")
.Description("Another test Address")
.Build();
Null Object
Nulls
Nulls

“I call it my billion-dollar mistake. It


was the invention of the null
reference in 1965.”
Sir Charles Antony Richard Hoare
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
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
Implementation

BaseType

Real Objects NullObject

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)
Nulls Break Polymorphism
 Polymorphism is a key benefit of OO systems

 Null breaks polymorphism – calling methods on null instances results in


exceptions
Null Checks Everywhere
With Null Object
Client code:

Repository:
Practice PDD
 Pain Driven Development
“Don’t apply every pattern
you know to a problem
from the start; practice
Pain Driven Development.”

If it’s causing pain, fix it.


Let’s Review
Real World Design Patterns
1. Don’t be too busy to improve
2. Avoid adding coupling via Singletons
3. Decouple specific implementation with
Strategy
4. Avoid hidden dependencies – New is Glue!
5. Decouple your database with Repository
6. Decouple access control with Proxy
7. Decouple execution of other actions with Commands
8. Complex construction with Builder
9. Reduce null checks with Null Object
10. Practice Pain Driven Development
References
 Design Patterns, http://amzn.to/95q9ux
 Design Patterns Explained, http://amzn.to/cr8Vxb
 Design Patterns in C#, http://amzn.to/bqJgdU
 Head First Design Patterns, http://amzn.to/aA4RS6

Pluralsight Resources
 N-Tier Application Design in C# http://bit.ly/Msrwig
 Design Patterns Library http://bit.ly/vyPEgK
 SOLID Principles of OO Design http://bit.ly/OWF4la

 My Blog
 http://ardalis.com
Discuss

Contact
steve@ardalis.com
Twitter: @ardalis
Web: ardalis.com
http://about.me/stevenasmith

You might also like