Design Patterns in Practice

advertisement
Design Patterns in Practice
By Doug, Jeremy, & Mike
What is a design pattern?
• A design pattern is a reusable solution to
a common problem in software design.
• A design pattern is not code.
• A design pattern can vary in scope from
very simplistic to an entire system.
The different kinds of patterns
• Creational
– Patterns that focus on object creation.
• Structural
– Patterns that focus on the relationships
between entities.
• Behavioral
– Patterns that focus on the communication
patterns between objects.
• Concurrency
– Patterns that deal with multi-threaded
programming techniques.
What are the benefits?
• Can significantly speed up the
development process.
• Wide adoption of common patterns result
in code that is easier to understand and
maintain over time.
• Proper usage of design patterns will
result in a better overall design.
What are the drawbacks?
• Can introduce more levels of indirection.
• Improper usage will negate the benefits.
• Adding complex design patterns to
simple problems can result in over
engineering.
The Interface
• What is it?
– An interface contains only the signatures of
methods, properties, events or indexers.
– It is a contract between you and the rest of
the world.
• Why should you use it?
– It insulates the rest of the world from the
underlying implementation.
• When should you use it?
– Whenever you are needing to isolate
yourself.
The Interface vs. Abstract Classes
• The problem setup:
– A group of physicians want to have a
document management system to keep track
of their publications.
Document
+Classification() : string
AuthoredDocument
+Author() : string
+PublicationYear() : int
1 week later…
• Can we have third party authored
documents about us in our system?
Document
+Classification() : string
AuthoredDocument
PhysicianDocument
+Author() : string
+PublicationYear() : int
+PhysicianName() : string
After 2 months of sheer bliss…
• Can we have procedure and diagnosis
information on the documents that are
related to procedures they have
performed?
Document
+Classification() : string
MedicalDocument
AuthoredDocument
PhysicianDocument
+Procedure() : string
+Diagnosis() : string
+Author() : string
+PublicationYear() : int
+PhysicianName() : string
Our code is starting to cave…
• There are dozens of places in our code
that have logic similar to the following:
public void ProcessDocument(Document doc)
{
if (doc is AuthoredDocument)
{
// Do author specific stuff...
}
if (doc is MedicalDocument)
{
// Do medical stuff...
}
if (doc is PhysicianDocument)
{
// Do physician stuff...
}
}
This is
dangerous!
1 day later…
• Can we have patient related documents
for the procedures we have done or have
found on-line?
Document
+Classification() : string
MedicalDocument
AuthoredDocument
PhysicianDocument
+Procedure() : string
+Diagnosis() : string
+Author() : string
+PublicationYear() : int
+PhysicianName() : string
?
PatientDocument
+MRN() : string
What we could have done…
«interface»
IDocument
+Classification() : string
«interface»
IMedicalDocument
+Procedure() : string
+Diagnosis() : string
«interface»
IAuthoredDocument
+Author() : string
+PublicationYear() : int
«interface»
if (doc is IMedicalDocument) IPatientDocument
{
+MRN() : string
// Do medical stuff...
}
if (doc is IPhysicianDocument)
{
// Do physician stuff...
}
«interface»
IPhysicianDocument
+PhysicianName() : string
The Singleton
Singleton
-m_instance : Singleton
-Singleton()
+Instance() : Singleton
• The singleton pattern is used to restrict
the number of instances of a class to
one.
• How is this useful?
• Why is this controversial?
The Singleton - Implementation
• Singleton C# code
public sealed class Singleton
{
private static readonly Singleton m_instance = new Singleton();
/// <summary>
/// Explicit static constructor to tell C# compiler
/// not to mark this type as beforefieldinit
/// </summary>
static Singleton()
{
}
/// <summary>
/// Private constructor to enforce singleton pattern.
/// </summary>
private Singleton()
{
}
/// <summary>
/// Static property to return the singleton instance.
/// </summary>
public static Singleton Instance
{
get { return m_instance; }
}
}
The Factory
«interface»IPerson
+FirstName() : string
+LastName() : string
PersonFactory
Person
-m_instance : PersonFactory
-PersonFactory()
+Instance() : PersonFactory
+FetchPerson(in key : object) : IPerson
+FirstName() : string
+LastName() : string
• What
are
common
uses
for apattern
doessome
the
factory
with?
The factory
pattern
is ahelp
creational
factory?
that
uses a Interface
singleton
object to provide
– Returning
implementations
implementations
of object
a requested
– Logging
Simplifies complex
creation interface
to
caller.
–a
Configuration
Caching
– Data access
– Testing
The Factory in Data Access
DAOFactory
«uses»
+<static> GetInstance() : DAOFactory
+GetDAO<T>() : T where T : IDAO
«interface»
IDAO
DAOFactoryConfiguration
+GetTypeName(in key : string) : string
+SetTypeName(in key : string, in typeName : string)
// Retrieve our DAO from the factory.
IPersonDAO dao = DAOFactory.GetInstance.GetDAO<IPersonDAO>();
// Invoke the appropriate retrieval method.
IPerson person = dao.GetPerson(modelId);
«interface»
IPersonDAO
+GetPerson(in modelId : guid) : IPerson
MsSqlPersonDAO
LdapPersonDAO
«interface»
«interface»
IPerson
IPerson
+ModelId()
+ModelId() :: guid
guid
+FirstName()
+FirstName() :: string
string
+LastName()
+LastName() :: string
string
PersonImpl
Other Factory Uses
«interface»
IDAO
«interface»
IPersonDAO
+GetPerson(in modelId : guid) : IPerson
Create fake
implementations
instead of
waiting
MockPersonDAO
TestPersonDAO
Test really
hard
scenarios
The Delegate
Driver
+Driver(in transportation : IDrivableDelegate)
+GoHome()
+GoToWork()
«uses»
«interface»
IDrivableDelegate
+Stop() : void
+MoveForward(in feet : double) : void
+Left(in degrees : int) : void
+Right(in degrees : int) : void
CarDelegate
BicycleDelegate
• What
The delegate
doessome
are
thepattern
delegate
common
is ahelp
uses
technique
with?
for a
where
delegate?
an object
expresses certain
– Separation
of concerns
behavior,
in reality
– Data
Reuseaccess
ofbut
common
logicdelegates the
responsibility
fordelegates
implementing/executing
– Business layer
that
behavior toprogramming
an associated object.
– Asynchronous
The Delegate Inside the DAO
DataCallFactory
+<static> GetInstance() : DataCallFactory
+GetDataCall(in dataDelegate : IDataCallDelegate<T>) : IDataCall
«interface»
IDataCall
+Execute() : T
«interface»
IDataCallDelegate<T>
+Prepare(in command : IDbCommand)
+Translate(in response : IDataReader) : T
MsSqlDataCall
GetPersonDataDelegate
+GetPersonDataDelegate(in modelId : guid)
public IPerson GetPerson(Guid modelId)
{
// Construct our delegate that can fill out our request
// and empty out the response from the data source.
GetPersonDataDelegate del = new GetPersonDataDelegate(modelId);
// Obtain a reference to the call point by passing the delegate
IDataCall<IPerson> call = DataCallFactory.Instance.GetDataCall(del);
// Execute the call and return the result.
return call.Execute();
}
A Common Problem
• How should I instrument my code?
• The real problems are…
–
–
–
–
–
What should I capture?
Where should I store the captured data?
Who should I notify when things go bad?
What does bad mean?
How do I change the system behavior to
optimize the system resources that are
available to me?
– How do I keep my code clean???
A Common Solution
public void Execute()
{
DateTime startedOn = DateTime.UtcNow;
Logger logger = new Logger();
try
{
logger.Write(string.Format(
"Starting GetPerson([{0}]) at {1}", m_modelId, DateTime.UtcNow));
What if my logger doesn’t support
my needs right now?
// Execute call
What if this was slow?
logger.Write(string.Format(
"Finished GetPerson([{0}]) at {1} and it worked!",
m_modelId, DateTime.UtcNow));
}
catch (Exception e)
{
logger.Write(string.Format("GetPerson([{0}]) failed {1}.", m_modelId, e));
throw;
}
finally
{
DateTime endedOn = DateTime.UtcNow;
Should we let someone know
about this?
Who was the user?
TimingSystem.GetInstance().LogExecutionTime("GetPerson", endedOn.Subtract(startedOn));
}
}
A Better Solution - The Interceptor
• First, design the core system without any
instrumentation.
• Next, identify the key points in the call
stack; these are the interception points.
• Finally, identify important information to
communicate at the interception points.
The Interceptor UML
Dispatcher
-m_Interceptors : IList<IInterceptor>
CoreSystem
+Execute()
«call»
+GetInstance() : Dispatcher
+Add(in interceptor : IInterceptor)
+Remove(in interceptor : IInterceptor)
#CreateContext(in modelId : guid) : IContext
#Started(in ctx : IContext)
#Finished(in ctx : IContext)
#Errored(in ctx : IContext, in e : Exception)
«interface»
IContext
+GetId() : guid
+GetModelId() : guid
+GetUserName() : string
+GetStartDateTime() : DateTime
+GetEndDateTime() : DateTime
+GetDuration() : long
«interface»
IInterceptor
+Started(in ctx : IContext)
+Finished(in ctx : IContext)
+Errored(in ctx : IContext, in e : Exception)
Interceptor Implementations
«interface»
IInterceptor
+Started(in ctx : IContext)
+Finished(in ctx : IContext)
+Errored(in ctx : IContext, in e : Exception)
LoggingInterceptor
TimingInterceptor
ImpactInterceptor
• More can be added dynamically at run
time.
Using Our Interceptor
public void Execute()
{
IContext ctx = Dispatcher.GetInstance().CreateContext(m_modelId);
try
{
Dispatcher.Started(ctx);
// Execute call
Dispatcher.Ended(ctx);
}
catch (Exception e)
{
Dispatcher.Errored(ctx, e);
throw;
}
}
• Much cleaner and more extensible
Service Oriented Architecture
• What is it?
Service Oriented Architecture
SOA - Simplified
• A simplistic view of one service
Why do you
have all of
these layers?
What is a
model?
Proper Coupling
Service 1
TCP HTTP
MQ
Service 2
IPC
IPC
Service Endpoints
Business Layer
Data Access Layer
TCP HTTP
...
MQ
IPC
Service Endpoints
Business Layer
Data Access Layer
Is this
allowed?
• Loose coupling is one of the tenants;
however, appropriate sacrifices can be
made to achieve performance goals.
Model Layer
Data Access Layer
MQ
Model Layer
Business Layer
Model Layer
Service Endpoints
TCP HTTP
Service N
Bad SOA!
Service 1
TCP HTTP
MQ
Service 2
IPC
IPC
Service Endpoints
Business Layer
Data Access Layer
TCP HTTP
...
MQ
IPC
Service Endpoints
Business Layer
Data Access Layer
Model Layer
Data Access Layer
MQ
Model Layer
Business Layer
Model Layer
Service Endpoints
TCP HTTP
Service N
• You must adhere to the design pattern or
your implementation will collapse under
its own weight.
Database Coupling
Person Management
Security
Person
PK
U1
PersonId
PersonModelId
Prefix
FirstName
MiddleName
LastName
Suffix
Account
int identity
uniqueidentifier
nvarchar(50)
nvarchar(50)
nvarchar(50)
nvarchar(50)
nvarchar(50)
PersonAccount
PK
FK2,U1
FK1,U1
U1
PersonAccountId
AccountId
PersonId
RemovedIndex
CreatedOn
CreatedBy
RemovedOn
RemovedBy
int identity
int
int
int
datetime
nvarchar(256)
datetime
nvarchar(256)
PK
U1
U2
U2
AccountId
AccountModelId
Identity
IsEnabled
RemovedIndex
int identity
uniqueidentifier
nvarchar(256)
bit
int
Database Loose Coupling
Person Management
Person
PK
U1
PersonId
PersonModelId
Prefix
FirstName
MiddleName
LastName
Suffix
AccountProxy
int identity
uniqueidentifier
nvarchar(50)
nvarchar(50)
nvarchar(50)
nvarchar(50)
nvarchar(50)
PersonAccount
PK
FK1,U1
FK2,U1
U1
PersonAccountId
PersonId
AccountProxyId
RemovedIndex
CreatedOn
CreatedBy
RemovedOn
RemovedBy
Security
int identity
int
int
int
datetime
nvarchar(256)
datetime
nvarchar(256)
PK
U1
AccountProxyId
AccountModelId
RemovedOn
int identity
uniqueidentifier
datetime
Account
PK
U1
U2
U2
AccountId
AccountModelId
Identity
IsEnabled
RemovedIndex
int identity
uniqueidentifier
nvarchar(256)
bit
int
What if an
account is
removed?
• Some data integrity checks must be
moved to higher levels in the
architecture.
Loose Coupling Wrap-up
• True loose coupling is not easy.
• An event model must be part of your
solution to achieve truly isolated services.
• Performance and extensibility are often
at odds with each other.
• These design patterns are really
powerful!
Questions
Future Topics
• Unit Testing
• SOA Deep Dive
• Pub / Sub
• Business Intelligence / Analytics
• Free Text Search
Download