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