Software Design Patterns 1 What is a design pattern? 1. It describes a general usable solution to a recurring problem in an environment 2. It describes core of solution in a procedurallike structure 2 Why Design Patterns? 1. Simplifies object identification 2. Simplifies system decomposition 3. Proven & tested technique for problem solving 4. Improves speed & quality of design / implementation 5. Can be adapted / refined for specific system under construction 3 Design Patterns Classification Two categories class scope: relationship between classes & subclasses statically defined at run-time object scope: object relationships (what type?) Can be manipulated at runtime (so what?) 4 THE CONCEPT OF PATTERNS Construction Architecture Patterns The first idea of using patterns was for building and proposed by the architect Christopher Alexander. He found recurring themes in architecture, and captured them into descriptions He called them patterns. The term 'pattern' appeals to the replicated similarity in a design The similarity makes room for variability and customization in each of the elements Alexander defines: «Each pattern is a three part rule which express a relation between a certain context, a problem and a solution. Each pattern is a relationship between a certain context, a certain system of forces which occurs repeatedly in that context a certain spatial configuration which allows these forces to resolve themselves. A pattern is an instruction and shows how this configuration can be used over and over again. The pattern is a thing that happens in the world The rule which tell us how to create that thing and when we must create it. KitchenViewer Interface: An architectural pattern example Wall cabinet menu Counter display area styles Floor cabinet Modern Classic 9 Antique Arts & Crafts KitchenViewer Example Modern Classic Antique 10 Arts & Crafts Selecting Antique Style Modern Classic Antique 11 Arts & Crafts Specific Design Purposes for KitcherViewer The procedure of rendering the various styles is basically the same regardless of the style . The code is as follows: Counter counter =new Counter(); draw (counters); A single block of code that executes in several possible ways, depending on the context Polymorphism An application must construct a family of objects at runtime. The design must enable choice among several families of styles An Introduction to Design Pattens Example Application: Without applying a Design Pattern renderKitchen() method is used. This code would have to be repeated for every style The code that is supposed to be duplicated becomes different in different places. Example Application: Applying a Design Pattern renderKitchen(myStyle) method is used KitchenViewer design purpose is implemented by applying Abstract Factory design pattern. KitchenViewer Without Design Patterns Client renderKitchen() Kitchen WallCabinet ModernWallCabinet FloorCabinet AntiqueWallCabinet ModernFloorCabinet 14 AntiqueFloorCabinet Abstract Factory Design Pattern Properties Provide an interface for creating families of related or dependent objects without specifying their concrete classes. A hierarchy that encapsulates: many possible platforms, and the construction of a suite of products. The new operator considered harmful Problem If an application is to be portable, it needs to encapsulate platform dependencies. These platforms might include: windowing system, operating system, database… General Structure The Abstract Factory defines a Factory Method per product. Each Factory Method encapsulates the new operator and the concrete, platform-specific, product classes. Each platform is then modeled with a Factory derived class. The Abstract Factory Idea KitchenStyle WallCabinet getWallCabinet() getFloorCabinet() … FloorCabinet AntiqueWallCabinet ModernKStyle AntiqueKStyle getWallCabinet() getFloorCabinet() getWallCabinet() getFloorCabinet() FloorCabinet getFloorCabinet() { return new ModernFloorCabinet(); } … AntiqueFloorCabinet FloorCabinet getFloorCabinet() { return new AntiqueFloorCabinet(); } 17 Abstract Factory Design Pattern Applied to Client KitchenViewer renderKitchen( KitchenStyle ) KitchenStyle getWallCabinet() getFloorCabinet() Kitchen getWallCabinet() getFloorcabinet() WallCabinet FloorCabinet ModernWallCabinet ModernKStyle getWallCabinet() getFloorCabinet() AntiqueKStyle getWallCabinet() getFloorCabinet() AntiqueWallCabinet ModernFloorCabinet AntiqueFloorCabinet 18 Another Example Factory Method Online bookstores that can choose different book distributors to ship the books to the customers Both BookStoreA and BookStoreB choose which distributor (EastCoastDistributor or MidWestDistributor or WestCoastDistributor) to use based on the location of the customer. This logic is in each bookstore's GetDistributor method. Abstract Factory Pattern The abstract factory design pattern is an extension of the factory method pattern, The abstract factory pattern allows to create objects without being concerned about the actual class of the objects being produced. The abstract factory pattern extends the factory method pattern by allowing more types of objects to be produced. Extension of GetDistributor() Method to Abstract Factory Pattern We can extend GetDistributor() method by Adding another product that the factories can produce. In this example, we will add Advertisers that help the bookstores advertise their stores online. Each bookstore can then choose their own distributors and advertisers inside their own GetDistributor and GetAdvertiser method. public void Advertise(IBookStore s) { IAdverister a = s.GetAdvertiser(); a.Advertise(); } This allows to have client code (calling code) such as: public void Advertise(IBookStore s) { IAdverister a = s.GetAdvertiser(); a.Advertise(); } Regardless if you pass in BookStoreA or BookStoreB into the method, this client code does not need to be changed since it will get the correct advertiser automatically using the internal logics within the factories. It is the factories (BookStoreA and BookStoreB) that determines which advertiser to produce. The same goes for choosing which book distributor to produce UML of the Abstract Factory Design Pattern The Benefit of the Abstract Factory Pattern The benefit of the Abstract Factory pattern is that it allows you to create a groups of products (the distributors and the advertisers) without having to know the actual class of the product being produced. The result is that you can have client code that does not need to be changed when the internal logic of the factories changed. We can change the types of the products (the distributors and the advertisers) by changing the code in the factories (the bookstores) without changing the client code public enum CustomerLocation { EastCoast, WestCoast } class Program { static void Main(string[] args) { IBookStore storeA = new BookStoreA(CustomerLocation.EastCoast); Console.WriteLine("Book Store A with a customer from East Coast:"); ShipBook(storeA); Advertise(storeA); IBookStore storeB = new BookStoreB(CustomerLocation.WestCoast); Console.WriteLine("Book Store B with a customer from West Coast:"); ShipBook(storeB); Advertise(storeB); } //**** client code that does not need to be changed *** private static void ShipBook(IBookStore s) { IDistributor d = s.GetDistributor(); d.ShipBook(); } //**** client code that does not need to be changed *** private static void Advertise(IBookStore s) { IAdvertiser a = s.GetAdvertiser(); a.Advertise(); } //the factory public interface IBookStore { IDistributor GetDistributor(); IAdvertiser GetAdvertiser(); } //concrete factory public class BookStoreA : IBookStore { private CustomerLocation location; public BookStoreA(CustomerLocation location) { this.location = location; } IDistributor IBookStore.GetDistributor() { //internal logic on which distributor to return / /*** logic can be changed without changing the client code **** switch (location) { case CustomerLocation.EastCoast: return new EastCoastDistributor(); case CustomerLocation.WestCoast: return new WestCoastDistributor(); } return null; } IAdvertiser IBookStore.GetAdvertiser() { //internal logic on which distributor to return //*** logic can be changed without changing the client code **** switch (location) { case CustomerLocation.EastCoast: return new RedAdvertiser(); case CustomerLocation.WestCoast: return new BlueAdvertiser(); } return null; } } //end of factory class public class BookStoreB : IBookStore //concrete factory { private CustomerLocation location; public BookStoreB(CustomerLocation location) { this.location = location; } IDistributor IBookStore.GetDistributor() { switch (location) { case CustomerLocation.EastCoast: return new EastCoastDistributor(); case CustomerLocation.WestCoast: return new WestCoastDistributor(); } return null; } IAdvertiser IBookStore.GetAdvertiser() {switch (location) { case CustomerLocation.EastCoast: return new BlueAdvertiser(); case CustomerLocation.WestCoast: return new RedAdvertiser(); } return null; } } //the product public interface IDistributor { void ShipBook(); } //concrete product public class EastCoastDistributor : Idistributor { void IDistributor.ShipBook() { Console.WriteLine("Book shipped by East Coast Distributor"); } } //concrete product public class WestCoastDistributor : IDistributor { void IDistributor.ShipBook() { Console.WriteLine("Book shipped by West Coast Distributor"); } } public interface IAdvertiser //the product { void Advertise(); } public class RedAdvertiser : IAdvertiser //concrete product { void IAdvertiser.Advertise() { Console.WriteLine("Advertised by RedAdvertiser"); } } public class BlueAdvertiser : IAdvertiser //concrete product { void IAdvertiser.Advertise() { Console.WriteLine("Advertised by BlueAdvertiser"); }}