• Design Pattern in Simple Examples


    Instead of defining what is design pattern lets define what we mean by design and what we mean by pattern. According to me design is blue print or sketch of something so it can be defined as creation of something in mind. Moving to pattern, we can define it as guideline, or something that repeats. Now the definition of design pattern becomes creating something in mind that repeats or in other words capturing design ideas as a "pattern" to the problems.

    Some problem patterns happen over and over again in a given context and Design Pattern provides a core of the solution in such a way that you can use the core solution every time but implementation should and may vary and the main reason behind that is we have the core solution and not the exact solution. We can discuss an example here about database normalization. Normalization is a pattern (Core solution to database design) but what level of normalization you need (exact solution) depends on your requirement and context.

    In this article I will be discussing the following Design patterns ( or common problems and there common solutions which are time tested and have worked when applied ).

    I will not be defining the design patterns because you can always find them in any standard book but I will be dealing with the actual use and examples of them. To understand the article in a better I would suggest you first download the demo project. To see any sample you make the project as the StartUp project and compile the project and execute it. The solution is build using Microsoft Visual Studio 2005. I have purposely used the abstract classes over interface so the class diagram of the samples looks exactly similar to one suggested by gang of four. If you find a class whose Name is IClassName please don't get upset but its done purposefully to make the class diagrams. In the article I have tried to use as many pictures as possible because "A picture is worth a thousand words"

    Creational Patterns

    Abstract Factory

    - Do we need to create families of objects.

    Factory method takes care of one product where as the abstract factory Pattern provides a way to encapsulate a family of products.

    Typically the class diagram looks like



    Example

    We have a requirement where we need to create control library and the same library supports multiple platforms but the client code should not be changed if we import from one operating system to the other. The solution is



    The client uses the GuiFactory to get the required factory of the supported operating system and calls the same Show Method. Now depending on the platform we change the factory but the client implementation remains the same. If support for new operating system is to be added we need the new factory and the exact implementation of the buttons and without changing the existing code we can support the new platform.

    Builder

    - Do we need to create object in several steps.

    Builds a class based on requirements where Director asks the builder to build each of the parts. Mainly the builder pattern is not used independently but other patterns may have builder pattern as a part where they create the complicated objects using the builder.

    Typically the class diagram looks like



    Example



    Who is what?

    Waiter is Director
    PizzaBuilder is Builder
    CheesePizzaBuilder and MixedPizzaBuilder are Concretebuilder
    Pizza is product

    When Waiter (Director) is asked to serve it creates the Pizza (Product) using the PizzaBuilder.

    Another example could be Maze which is a complicated object build from walls and rooms.

    Factory

    - Do we need to have derived classes figure out what to instantiate and decouple client from instantiated class.

    Client uses the factory to create products and its the factory which decides when the actual product is needed for instantiation. This way client decouples the instance and can be saved from some of the crucial operations of object copy if the type of object may change after creation.

    Typically the class diagram looks like



    Example



    Who is what?

    ComputerFactory (Creator)
    ConcreteComputerFactory (ConcreteCreator)
    Processor (Product)
    ConcreteProcessor (ConcreteProduct)

    When the GetProcessor of ComputerFactory is called its the ConcreteComputerFactory creates the ConcreteProcessor and the creation of ConcreteProcessor is delayed till we call the GetProcessor() function.

    Another good example could be logging, where we create the instance of the logger factory but instantiate the logger class when actual logging is done.

    Prototype

    - Do we have too many classes to instantiate / or is the object creation a cumbersome process.

    Mainly we don't create the objects of a class directly but clone the existing object and change the state of the object as needed. The main application of such pattern is when the object creation is costly. As an example we have a database class the constructor sets up the database for the class. Now for each new user logging to the system once the system is up we don't setup the database but just clone the first object and change the user specific details like user name / password to validate the user.

    Typically the class diagram looks like



    Example



    I would not explain here who is what because its pretty much evident.

    Singleton

    - Do we need to limit the no of objects of a class.

    Ensures only one (n = 1..n) instance. The pattern explains how you can achieve the singleton class. It says to have the constructor as private and have a static method to access the instance of the class using that method.

    Typically the class diagram looks like



    Example



    I would not explain here who is what because its pretty much evident.

    Structural Patterns

    Adapter

    - Do we have the right stuff but wrong interface.

    We use the adapter design pattern where the requirements is to convert between one interface to another. Adapter pattern is never implemented when designing a new system but with the changing requirements we have deferring interfaces then adapter comes into picture.

    Typically the class diagram looks like



    Example

    We have used some library where we have Add function which takes two integer and provides the sum of them. Now when upgrading the libray we find that the library has changed the Add function such that it takes 2 floating point number. Now one option could be to change all the client code where we have used the Add method or other option is to have an Adapter.



    CalcAdapter calls the necessary library function after making the necessary changes (in our example conversion between the data types)

    Bridge

    - Do we have one variation using another variation in a varying way.

    Decouple an abstraction from its implementation so that two can vary independently. In strategy pattern we decouple the behavior but in Bridge we decouple the abstraction.

    Typically the class diagram looks like



    Example

    In Abstract Factory we discussed about the problem of creating a control library for various operating system. Now creating the library from the scratch is never a good idea and so we may need to use some of the existing infrastructure or library available. We may use the XWindow toolkit or MacWindow toolkit as the base depending on the user platform and toolkit available.

    The DrawRect actually uses the DrawLine function which actually is dependent on the type of implementation and we seperate the implementation from the abstraction and have a link ( or bridge ) between the two.


    Composite

    - Do we have units and groups and want to treat them the same way.

    Compose the objects in a tree structure where individual objects as well as the composed objects behave uniformly. Composed objects delegates the requests to the individual leaf objects.

    Typically the class diagram looks like



    Example

    We have some simple graphics and have some graphics which are composed of these simple graphics and as a client both should behave uniformly.



    Folder browsing could be other example where from the client's point of view its an operation on the folder tree irrespective of its folder ( composite object ) or file ( leaf object ).

    Decorator

    - Do we need multiple additional functions we may need to apply, but which and how many we add varies, without sub classing.

    Attach additional responsibilities to an object dynamically. It has the capability of performing some additional operations before or after the basic operation.

    Typically the class diagram looks like



    Example

    Say we have a FileReader class where the file can be read based on the combination of applying any of the formulas like it could be

    • Zipped.
    • Encrypted.
    • Zipped and encrypted.
    • encrypted then zipped and ecrypted again.


    The solution is Decorator pattern where we apply the options based on the requirement.

    Code: CSharp

    FileReader file = new FileReader();
    // Zip the File
    ZipReader zip = new ZipReader(file);
    // Encrypt the zip file
    EncryptedReader enc = new EncryptedReader(zip);

    enc.Read();


    Code: CSharp

    FileReader file = new FileReader();
    // Encrypt the file
    enc = new EncryptedReader(file);
    // Zip the encrypted file
    zip = new ZipReader(enc);

    zip.Read();


    We can apply any combination as and when needed and also new methods of encryption is very simple. Just add the new class as sibling of EncryptedReader


    Facade

    - Do we want simplify, beautify or OO-fy an existing class or subsystem.

    When client is decoupled from the system using an inter mediator its called facade. Facade behaves as a door to subsystem and provides a single interface to complex interface in the subsystem. Here a point to note is that the subsystem should not have a dependency on the FACADE and if thats the case then the facade is a part of the sub-system and it should move into the sub-system and we should have a new facade class.

    Also Facade is not the only entry point to the sub-system but is a convenient point of communication to the subsystem and client can always have the direct access to the subsystem.

    This methods helps in developing the subsystem independently without affecting the clients using them.

    Typically the class diagram looks like



    Example

    We have a Car System creation where the car is created based on the complex subsystems like wheel, steering, chassis, body ...


    Flyweight

    - Do we have too many part objects.

    Any objects state can be classified into two types of data that it can store one is intrinsic (static and independent of object) and one is extrinsic (non-static and depend on the state of the object) then flyweight pattern can be applied. The design pattern is useful when we have large no of objects which can be grouped once the extrinsic state is removed and it uses de-encapsulation to split the objects.

    Typically the class diagram looks like



    Example

    We have some shape objects and the shape objects are really costly and so we can have the shape object split itself such that some data which is independent of the state of the object is kept in the object and other data is provided externally. Say in our shape object how the shape is drawn is same but where the shape is drawn is dependent on the state of the object and so the print method should know where to print which is extrinsic and should be supplied.


    Proxy

    - Do we need to optionally add some new functionality to something that already exists. Do we need to control how an object is accessed.

    It provides a placeholder for another objects and the proxy object gives the impression to the client that actual request is handled by the proxy but it just delegates the request to the real subject.

    Typically the class diagram looks like



    Example

    Say we have a system setup where we send and receive data over the network. Now due to some security reasons data are encrypted and so now they need to be decrypted before processing and so now we are in trouble because all the client code needs to be changed to decrypt the data or we may need to change the stable library code which use to send/recieve the data. Now the solution could be to have the proxy which will do the additional responsibility given to the system and then send the data using the well tested system in place.



    We should be using the proxy system where we think we may need to add some additional responsibilities later.

    Behavioral Patterns

    Chain of Responsibility

    - Do we have diff. objects that can do the job but we do not want the client object know which is actually going to do it.

    Avoid coupling the sender of a request to its reciever by giving more than one object a chance to handle the request. Helps in reducing the coupling though they are chained, but does not know about other objects apart fron the fact that they derive from the common interface.

    Best example could be Context help where the each help component tries to perform the request and when fail they pass the request in the chain to other members.

    Typically the class diagram looks like



    Example

    In the banking system where cheque's for clearing is approved by the person but if the cheque amount is beyond certain limit, the approving responsibility moves the person higher in authority in the bank.

    Windows messaging system works in the similar method where the messages are processed by the controls if the point likes with the bounds of the control or goes to the parent.


    Command

    - Do we need to decouple request from handler.

    Encapsulate requests for service from an object inside other object(s) and manipulate requests. Command objects are mainly helpful in undo/redo operation where the previous state can be saved for reloading or even the necessary command(s) can be stored in stack for the same.

    Typically the class diagram looks like



    Example

    Say we have designed an image editor and user can have the option of opening file from various ways like menu, tool bar, double click on a file in the explorer. The solution is the command pattern where the FileOpen command is associated in the viewer itself and when the command executes the file is shown in the viewer.



    The other example could be the operations on the images.

    Interpreter



    As the name suggest it interpret your expression. Some expressions are atomic and some complex which are made up of atomic and it interprets the atomic and complex expressions uniformly. It mainly uses in the compilers / parsers / Macro expansions.



    I have not provided any sample because this one is not that often used just for completeness of the article have mentioned the pattern here.

    Iterator

    - Do we want to separate collection from client that's using.

    Provide a way to access the elements of an aggregate objects sequentially without exposing its representation. We can make the client independent on the movement of the cursors like forward traversal / backward traversal as well as the internal implementation of the list.

    Typically the class diagram looks like



    Example



    I would not explain here who is what because its pretty much evident and nowadays any modern language supports this and so you could use the foreach loop. In C-Sharp if you would like to have a class that will be a list you need to use the IEnumerator and IEnumerable interfaces and then it will support the foreach loop.

    List should implement IEnumerable and the Iterator should implement IEnumerator.

    Mediator

    - Do we have a lot of coupling in who must talk to whom.

    Define objects in the subsystem where the set of objects interact. This pattern is much similar to the Facade but in facade the sub-system knows nothing about the Facade but here the sub-system communicate through the mediator and the client also communicate through the mediator. Mediator hides the complicated communication in the subsystem where as facade sub-system classes are open to the client as well, So Facade is an optional point of communication where as Mediator is only point of communication to the subsystem.

    Typically the class diagram looks like



    Example

    Now don't blame me if for the third time I take the example of the control library where we have a dialog and some controls over the dialog. The controls can be interacted through the DialogMediator. Now if the siblings of the Dialog would like to interact they do so through the DialogMediator because its the DialogMediator who has the information about the rest of the sub-system


    Memento



    Delegate some activity to some other class like some helper classes to do the job.

    The memento pattern is used to encapsulate the current state of an object in a memento object in order to be able to restore the object state later without exposing the internal representation of the object to the outside world. The memento pattern is useful when you have an object which you would to take a "snapshot" of so that at a later time you could use the snapshot to restore it to its original state, such as an undo or rollback operation.

    Typically the class diagram looks like



    Example

    Suppose you have a an object which stores form information and you would like to allow the user to make changes in the form and then if they make a mistake later you can put back in the original form values. Well, you could serialize the form object and then un-serialize it later but this is obviously messy and not a good solution. Another possible solution would be to have an outside object use the form's accessors methods to pull out what you need to save the state but this causes high coupling between the class saving the state and the form; any changes in the form would require changes in the other class. We need something that will allow you to save the state and restore it later without having to get involved in the details. This is where the memento pattern comes in.


    Observer

    - Do various entities need to know about events that have occurred? How to keep dependent object up-to-date.

    When we have one to many dependency between objects so when the object changes, its dependents are notified of the update automatically. The best example is something like Excel with data in one cells and we have multiple charts which gets updated when there is change in the data. This design pattern is more useful if we have one sided broadcast communication and is useful when we have one to many relationship.

    Typically the class diagram looks like



    Example

    When any data is changed in the XMLDoc object it calls the Notify Method of the base class which has the list of the active observers attached to the XMLDoc and calls the Update method of all the view of the data.


    State

    - Do we have a system with lots of states where keeping track of code for differrent states is difficult.

    Allow the object to alter it behavior when its internal state change.

    Typically the class diagram looks like



    Example

    We have a Printer system where we have the following states for the printer and based on the actions taken in particular state, the state of the printer changes accordingly.



    Instead of having the state defined in the printer itself we keep the state of the printer into separate object and separate the responsibilities. "Have objects for state."


    Strategy

    - Do we have a varying rule or algorithm.

    Define a family of algorithms, encapsulate each one and make them interchangeable. It allows us to change the algorithm independently with out changing the client using it. It converts the generalization of the template method to composition or aggregation.

    It has various uses like it allows the algorithm to vary frequently and is a very good alternate solution to sub-classing.

    Typically the class diagram looks like



    Example

    We have a payment system where we have an Invoice class where we calculate the total of the amount payable but the requirement is such the amount calculation depends on the type of customer and discount offer. Also over the time the offers / discount may differ and may need to change the offer at run-time. The solution is



    Now if have some special offer coming for the season sale all we need to do is add a new class derived from NetPayable and then add the necessary calculation logic in that class and this way we can have the new algorithm added dynamically.

    Template Method

    - Do we have a skeleton of the algorithm and want to leave upto the sub classes how each step behaves.

    Define the skeleton of the algorithm in an operation and deferring the exact implementations of the steps of the algorithms to its subclasses. Template method uses the HR policy of "we will call you" which means the exact implementations of the algorithm will be called by the base class.

    Typically the class diagram looks like



    Example

    Printing algorithm for various types of documents where the algorithm is fixed of printing the header then body and at last the footer which is defined in the base class the exactly how the headers are printed is defined in the supported document classes.


    Visitor

    - Do we have new tasks that we will need to apply to our existing classes.

    Represent an operation to be performed on the element of an object and helps you add new methods to existing hierarchy without affecting the existing hierarchy. This also de-encapsulate where we break the class so that some future functionality can be added but it has the disadvantage that concrete elements cannot be added without changing the interface. Use of the pattern should be done if you have the concrete elements fixed but you may need to add the new functionality to the existing object.

    Typically the class diagram looks like



    Example

    We have the number of objects supported in the drawing system fixed but we need to add the depth in the way the object does it operations like scale or drawing of the object. We could have the same class do both the job but we need some specialist in the field doing the job. A mathematician is good at doing the scaling but not at drawing or vice versa and so here we use the visitor pattern.



    Accept method actually calls the correct implementation based on the visitor passed, so its the client who tells if he needs to draw or scale.

    GOLDEN RULE(s) of design pattern

      1. Client should always call the abstraction (interface) and not the exact implementation.
      2. Future changes should not impact the existing system.
      3. Change always what is changing.
      4. Have loose coupling
        • Inheritance ( Very coupled )
        • Composition
        • Aggregation
        • Association
        • Dependency
        • Realization ( Least couple )
  • 相关阅读:
    71. Simplify Path
    68. Text Justification
    65. Valid Number
    64. Minimum Path Sum
    More Effective C++ 条款12 了解”抛出一个exception"与“传递一个参数”或“调用一个虚函数”之间的差异
    More Effective C++ 条款11 禁止异常流出destructor之外
    More Effective C++ 条款10 在构造函数内阻止内存泄露
    More Effective C++ 条款9 利用 destructor 避免泄露资源
    More Effective C++ 条款7 千万不要重载&&,||和,操作符
    More Effective C++ 条款6 区别 increment/decrement 操作符的前置(prefix)和后置(postfix)形式
  • 原文地址:https://www.cnblogs.com/UnGeek/p/4486017.html
Copyright © 2020-2023  润新知