design-pattern

目录

  1.Builder(生成器模式)
  2.Bridge(桥接模式)
  3.Decorate(装饰模式)
  4.Visitor(访问者模式)
  5.Command(命令模式)
  6.Strategy(策略模式)
  7.Iterator(迭代器模式)
  8.Singleton(单件模式)
  9.State(状态模式)
  10.Abstract Factory(抽象工厂模式)

理解

1.Builder(生成器模式)
----------------------
应用场景
  创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时
  当构建过程必须允许被构造的对象有不同的表示时
我的理解
  将复杂对象创建的接口和实现分离

2.Bridge(桥接模式)
-------------------
我的理解
  著名的pImpl技术,继承造成抽象接口和实现间的紧耦合,虽然客户
  面对的只是接口,但是仍然依赖于接口背后的实现

3.Decorate(装饰模式)
---------------------
我的理解
  GoF中举了一个例子:TextView负责在窗口中显示正文;用户可以选择用
  SrollDecorator添加滚动条或者用BorderDecorator添加粗黑边框或者组
  合使用。如果用静态继承的话,可能会产生类似于BorderScrollableTextView
  或者BorderedTextView这样的类,但是组合产生的类可能产生爆炸效果。
  装饰器通过嵌入component的集成体系并使用pImpl保持一致接口来达到
  递归组合的效果,这样可以避免静态继承造成的紧耦合(有时甚至无法
  继承)。最终可以很神奇的层层wrapper原始的类,并且wrapper过程可以
  随意替换顺序(因为wrapper类混入了原始类的集成体系中)。这样做使
  得装饰类的继承层次达到了三层:Origin->Decorate->ConcreteDecorate

示例代码

  #include <iostream>
  #include <string>

  class VisualComponent {
  public:
    virtual void draw() = 0;
  };

  class TextView : public VisualComponent {
  public:
    explicit TextView(std::string text = "")
      : text_(text)
    {}  

    virtual void draw()
    {
      std::cout << text_ << std::endl;  
    }
  private:
    std::string text_;
  };

  class Decorate : public VisualComponent {
  public:
    void set_component(VisualComponent* component)
    {
      component_ = component;
    }

    virtual void draw()
    {
      component_->draw();
    }

  protected:
    VisualComponent* component_;
  };

  class BorderDecorator : public Decorate {
  public:
    virtual void draw()
    {
      draw_border();
      Decorate::draw();
    }

  private:
    void draw_border()
    {
      std::cout << "border decorate" << std::endl;
    }
  };

  class ScrollDecorator : public Decorate {
  public:
    virtual void draw()
    {
      draw_scroll();
      Decorate::draw();
    }

  private:
    void draw_scroll()
    {
      std::cout << "scroll decorate" << std::endl;
    }
  };

  int main()
  {
    TextView* naked = new TextView("tex");
    BorderDecorator* border = new BorderDecorator();
    ScrollDecorator* scroll = new ScrollDecorator();  

    border->set_component(naked);
    scroll->set_component(border);
    scroll->draw();

    return 0;
  }

4.Visitor(访问者模式)
----------------------
我的理解
  GoF中举了一个例子:对一个过程的抽象语法树做typecheck,但是过程内
  节点可能有多种类型(比如:赋值节点和算术表达式节点),如果用过程
  式处理可能如下:
    void typecheck(procedure)
    {
      for_each_node(procedure, node) {
        if (is_assign_node(node)) {
          typecheck_assign_node(node);
        } else if (is_operation_node(node)) {
          typecheck_operation_node(node);
        } 
        ...
      }   
    }
  
  问题是增加新的操作需要增加if/else选项,同时需要手工判断节点类型
  而如果是OO处理可能如下:

    class Node {
    public:
      ...
      virtual void typecheck();
    };

    class AssignNode : public Node {       class OperationNode : public Node {
    public:                                public:
      ...                                    ...
      virtual void typecheck();              virtual void typecheck();
    };                                     };
    
    void typecheck(procedure)
    {
      for_each_node(procedure, node) {
         node->typecheck();
      }   
    }

  就是把程序员的工作交给了CPP编译器来做,node->typecheck()保持不变;
  问题在于如果类层次相对稳定而其中的操作需要不断变化,那么对于当前
  的方法,变化会波及到所有的类和客户代码。访问者模式将动作单独放到
  一个继承层次中,在原始的类中加入一个accept方法,依靠多态将不同的
  动作正确的分发到对应的类中,同时原始类将自己作为参数传递给动态获
  取到的动作。

    #include <iostream>
    #include <vector>
    
    class AssignNode;
    class OperationNode;
    
    class Action {
    public:
      virtual void operate_on_assignnode(AssignNode* assignnode) = 0;
      virtual void operate_on_operationnode(OperationNode* operationnode) = 0;
    };
    
    class TypeCheck : public Action {
    public:
      virtual void operate_on_assignnode(AssignNode* assignnode)
      {
        std::cout << "typecheck assign node" << std::endl;
      }
      virtual void operate_on_operationnode(OperationNode* operationnode)
      {
        std::cout << "typecheck operation node" << std::endl;
      }
    };
    
    class PrettyPrint : public Action {
    public:
      virtual void operate_on_assignnode(AssignNode* assignnode)
      {
        std::cout << "pretty assign node" << std::endl;
      }
      virtual void operate_on_operationnode(OperationNode* operationnode)
      {
        std::cout << "pretty operation node" << std::endl;
      }
    };
    
    class Node {
    public:
      virtual void accept(Action* action) = 0;
    };
    
    class AssignNode : public Node {
    public:
      virtual void accept(Action* action)
      {
        action->operate_on_assignnode(this);
      }
    };
    
    class OperationNode : public Node {
    public:
      virtual void accept(Action* action)
      {
        action->operate_on_operationnode(this);
      }
    };
    
    class ObjectStructure {
    public:
      void add(Node* n)
      {
        nodes_.push_back(n);
      }
      void operate_on_nodes(Action* action)
      {
        std::vector<Node*>::iterator iter;
        for (iter = nodes_.begin(); iter != nodes_.end(); ++iter)
          {
    	(*iter)->accept(action);
          }
      }
    private:
      std::vector<Node*> nodes_;
    };
    
    int main()
    {
      ObjectStructure* os = new ObjectStructure();
      os->add(new AssignNode());
      os->add(new OperationNode());
    
      TypeCheck* typecheck = new TypeCheck();
      os->operate_on_nodes(typecheck);
      
      PrettyPrint* prettyprint = new PrettyPrint();
      os->operate_on_nodes(prettyprint);
    
      return 0;
    }   

5.Command(命令模式)
--------------------
我的理解
  将请求抽象成command对象,该command统一暴露出execute和unexecute接
  口,每个具体的command对象需要将请求的接收者封装进去,并且向接收者
  发送消息实现execute和unexecute。

    #include <iostream>
    #include <string>
    #include <vector>
    #include <stdlib.h>
    
    class Document {
    public:
      explicit Document(std::string text = "")
        : text_(text)
      {}
      virtual ~Document() {}
      void move_down()
      {
        text_ = "\n" + text_;
      }
      void move_up()
      {
        std::size_t pos = text_.find_first_of("\n");
        if (pos == 0) {
          text_ = text_.substr(1, text_.length() - 1);
        }
      }
      void make_copy()
      {
        text_ = text_ + text_;
      }
      std::string get_text()
      {
        return text_;
      }
      void set_text(std::string text)
      {
        text_ = text;
      }
      void display()
      {
        std::cout << "+------------------------------------------+" << std::endl;
        std::cout << "|undo | redo | movedown | moveup | makecopy|" << std::endl;
        std::cout << "+------------------------------------------+" << std::endl;
        std::cout << text_ << std::endl;
        std::cout << "--------------------------------------------" << std::endl;    
      }
    private:
      std::string text_;
    };
    
    class Command {
    public:
      virtual void set_context(Document* document) = 0;
      virtual void execute() = 0;
      virtual void unexecute() = 0;
    };
    
    class MoveDownCommand : public Command {
    public:
      MoveDownCommand()
      {
        document_ = NULL;
        backup_ = "";
      }
      void set_context(Document* document)
      {
        document_ = document;
        backup_ = document->get_text();
      }
      virtual void execute()
      {
        backup_ = document_->get_text();
        document_->move_down();
      }
      virtual void unexecute()
      {
        document_->set_text(backup_);
      }
    private:
      Document* document_;
      std::string backup_;
    };
    
    class MoveUpCommand : public Command {
    public:
      MoveUpCommand()
      {
        document_ = NULL;
        backup_ = "";
      }
      void set_context(Document* document)
      {
        document_ = document;
        backup_ = document->get_text();
      }
      virtual void execute()
      {
        backup_ = document_->get_text();
        document_->move_up();
      }
      virtual void unexecute()
      {
        document_->set_text(backup_);
      }
    private:
      Document* document_;
      std::string backup_;
    };
    
    class MakeCopyCommand : public Command {
    public:
      MakeCopyCommand()
      {
        document_ = NULL;
        backup_ = "";
      }
      void set_context(Document* document)
      {
        document_ = document;
        backup_ = document->get_text();
      }
      virtual void execute()
      {
        backup_ = document_->get_text();
        document_->make_copy();
      }
      virtual void unexecute()
      {
        document_->set_text(backup_);
      }
    private:
      Document* document_;
      std::string backup_;
    };
    
    class Factory {
    public:
      Factory();
      ~Factory();
    
      static Command* create_command(std::string cmd)
      {
        if (cmd == "moveup") {
          return static_cast<Command*>(new MoveUpCommand());
        } else if (cmd == "movedown") {
          return static_cast<Command*>(new MoveDownCommand());      
        } else if (cmd == "makecopy") {
          return static_cast<Command*>(new MakeCopyCommand());      
        } else {
          return NULL;
        }
      }
    };
    
    void shell()
    {
      std::string text;
      std::string cmd;
      Document* document = NULL;
      Command* command = NULL;
      std::vector<Command*> history;
      std::size_t pos = -1;
      std::size_t len = 5;
    
      history.reserve(len);
      system("clear");
      std::cout << "input text: ";
      std::cin >> text;
      document = new Document(text);
    
      while (1) {
        system("clear");
        document->display();
        std::cout << "input command:";
        std::cin >> cmd;
        if (cmd == "quit") 
          return;
    
        if (cmd == "undo") {
          if (pos < 0 || pos >= history.size()) continue;
          command = history.at(pos);
          command->unexecute();
          pos = (pos == 0 ? 0 : pos - 1);
          continue;
        }
    
        if (cmd == "redo") {
          if (pos < 0 || pos >= history.size()) continue;
          command = history.at(pos);
          command->execute();
          pos = (pos >= len - 1 ? len : pos + 1);
          continue; 
        }
        
        command = Factory::create_command(cmd);
        if (command == NULL) continue;
        if (history.size() == len)
          history.erase(history.begin());
        pos = (pos >= len - 1 ? len - 1 : pos + 1);
        command->set_context(document);
        history.push_back(command);
        command->execute();
      }
    }
    
    int main()
    {
      shell();
      return 0;
    }
    
6.Strategy(策略模式)
---------------------
我的理解
  解耦 | 运行时替换(还没搞清楚和桥接模式的不同)

示例代码
    #include <vector>
    #include <set>
    #include <string>
    #include <iostream>
    #include <fstream>
    using std::vector;
    using std::set;
    using std::ifstream;
    using std::string;
    using std::cout;
    using std::cin;
    using std::endl;
    
    class Compositor {
    public:
      virtual void compose(vector<string> &text, set<size_t> &break_pos, size_t width) = 0;
    };
    
    class Greedy : public Compositor {
    public:
      Greedy() 
      {}
    
      virtual void compose(vector<string> &text, set<size_t> &break_pos, size_t width)
      {
        int len = text.at(0).length();
        int i, waterline, min = width;
    
        for (i = 1; i < text.size(); ++i) {
          waterline = abs(len + 1 + text.at(i).length() - width);
          if (waterline > min) {
    	    break_pos.insert(i - 1);
    	    len = text.at(i).length();
    	    min = width;
          } else {
    	    min = waterline;
    	    len += (1 + text.at(i).length());
          }
        }
      }
    
    private:
      int abs(int v)
      { return v > 0 ? v : -v; }
    };
    
    class Dynamic : public Compositor {
    public:
      Dynamic() 
      {}
    
      virtual void compose(vector<string> &text, set<size_t> &break_pos, size_t width)
      {
        //-*-TODO-*-dynamic programming implementation
      }
    };
    
    class Composition {
    public:
      Composition(string filepath, Compositor* compositor) 
        : compositor_(compositor)
      {
        load_text(filepath);
      }
    
      ~Composition()
      {
        delete compositor_;
      }
    
      void composing(size_t width)
      {
        size_t i, flag;
    
        break_pos_.clear();
        compositor_->compose(text_, break_pos_, width);
    
        flag = 0;
        for (i = 0; i < text_.size(); ++i) {
          if (flag) cout << " ";
          cout << text_.at(i);
          flag = 1;
          if (break_pos_.find(i) != break_pos_.end()) {
    	    cout << endl;
    	    flag = 0;
          }
        }
        cout << endl;
      }
    
      void display()
      {
        vector<string>::iterator iter;
        for (iter = text_.begin(); iter != text_.end(); ++iter)
          cout << *iter << endl;
      }
    
    private:
      Compositor* compositor_;
      vector<string> text_;
      set<size_t> break_pos_;
    
    private:
      void load_text(const string filepath)
      {
        ifstream fd(filepath.c_str());
        string word;
    
        while (fd >> word)
          text_.push_back(word);
      }
    };
    
    int main()
    {
      size_t width;
      Composition* composition = new Composition("../data/article", new Greedy());
      while (1) {
        printf("input width:");
        cin >> width;
        if (width == 999) break;
        composition->composing(width);
      }
      delete composition;
    
      return 0;
    }
         

7.Iterator(迭代器模式)
-----------------------
我的理解
  抽象出顺序访问容器元素的接口而又不暴露出其内部结构
  在libqb和Harmony中都见识了如何在C语言中应用这个技术     

8.Singleton(单件模式)
----------------------
我的理解
  对象在CPP中始终和int、double等内置类型一致么?这是
  CPP的设计初衷。但是事与愿违,通过拷贝、赋值可以获取
  一个新的打印机资源么?显然不是。RAII技术一直都被CPP
  之父大力推广(这个技术似乎并非本意为之),但是我们这
  种对客观世界对象建模的方式显然存在问题!
  单件模式保证一个类仅有一个实例,并提供一个访问它的全
  局访问点

9.State(状态模式)
------------------
我的理解
  GoF中举得实例大都与图形用户界面有关(这说明什么?)
  关于状态模式的例子出现在已知应用中关于交互式绘图程序
  的设计技术的描述:这类程序通常都为用户提供了一个工具
  箱,用户可以点击画直线工具,然后点击-拖动-再点击来确
  定一条直线,也可以点击选择一个图形,对于编辑器来说实
  际上是一个内部状态的转换:从直线工具转换到选择工具。
  图形编辑器内部维护一个tool对象并将请求委托给它,当用
  户选择一个新的工具时,就将这个工具对象换成新的,从而
  使得图形编辑器的行为相应地发生改变。
  pacemaker的核心功能就是实现了一个大的状态转换机,内部
  是通过维护一个巨大的状态转换表来实现的,似乎并无不妥,
  维护起来也还好。
  将switch/case语句代码拆分到多个state子类中,添加新的
  状态并进行状态转换更为方便
  
10.Abstract Factory(抽象工厂模式)
----------------------------------
我的理解
  比如一个Panel上有button和textbox,Panel内部维护一个
  pImpl类型的抽象工厂,该工厂对外暴露create_button和
  create_textbox接口,Panel依赖于该pPmpl实现控件的创建
  可以在运行时设置抽象工厂的具体实例,比如windowsstyle
  和macstyle两个具体工厂,当然button和textbox也要实现为
  抽象基类。
  将产品和工厂都实现为抽象方法,客户代码仅依赖于抽象接口
  这样可以隔离客户代码与特定工厂的依赖关系,也可以实现动
  态替换工厂实例。

示例代码
    #include <iostream>
    
    class Button {
    public:
      virtual void draw() = 0;
    };
    
    class WindowsStyleButton : public Button {
    public:
      virtual void draw()
      {
        std::cout << "windows style button" << std::endl;
      }
    };
    
    class MacStyleButton : public Button {
    public:
      virtual void draw()
      {
        std::cout << "Mac style button" << std::endl;
      }
    };
    
    class TextBox {
    public:
      virtual void draw() = 0;
    };
    
    class WindowsStyleTextBox : public TextBox {
    public:
      virtual void draw()
      {
        std::cout << "windows style textbox" << std::endl;
      }
    };
    
    class MacStyleTextBox : public TextBox {
    public:
      virtual void draw()
      {
        std::cout << "Mac style textbox" << std::endl;
      }
    };
    
    class WidgetFactory {
    public:
      virtual Button* create_button() = 0;
      virtual TextBox* create_textbox() = 0;
    };
    
    class WindowsStyleWidgetFactory : public WidgetFactory {
    public:
      virtual Button* create_button()
      {
        return static_cast<Button*>(new WindowsStyleButton());
      }
    
      virtual TextBox* create_textbox()
      {
        return static_cast<TextBox*>(new WindowsStyleTextBox());
      }
    };
    
    class MacStyleWidgetFactory : public WidgetFactory {
    public:
      virtual Button* create_button()
      {
        return static_cast<Button*>(new MacStyleButton());
      }
    
      virtual TextBox* create_textbox()
      {
        return static_cast<TextBox*>(new MacStyleTextBox());
      }
    };
    
    class Panel {
    public:
      Panel()
      {}
    
      ~Panel()
      {
        if (widget_factory_)
          delete widget_factory_;
        if (button_)
          delete button_;
        if (textbox_)
          delete textbox_;
      }
    
      void set_widget_factory(WidgetFactory* widgetfactory)
      {
        widget_factory_ = widgetfactory;
      }
    
      void create_panel()
      {
        button_ = widget_factory_->create_button();
        textbox_ = widget_factory_->create_textbox();
      }
    
      void show_panel()
      {
        button_->draw();
        textbox_->draw();
      }
      
    private:
      WidgetFactory* widget_factory_;
      Button* button_;
      TextBox* textbox_;
    };
    
    int main()
    {
      Panel* panel = new Panel();
      panel->set_widget_factory(new WindowsStyleWidgetFactory());
      panel->create_panel();
      panel->show_panel();
      delete panel;
      
      return 0;
    }
  
posted @ 2013-01-23 22:13  buaaspy  阅读(220)  评论(0编辑  收藏  举报