动机

  • 抽象框架感知抽象类,抽象框架必须实例化一个具体类,但抽象框架不知道哪个类要被实例化。
  • 绕开常规创建对象方法new,提供一种”封装机制”来避免客户程序和具体对象的紧耦合

定义

  • 意图定义一个用于创建对象的接口,让子类决定实例化哪个类。
  • 使一个类的实例化延迟到其子类。

引出问题

  • 一个用于文件分割的程序

  • 变化在于对于不同的文件有不同的分割操作

  • 以下代码中忽略了实际工程中的参数细节

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    class MainForm : public Form
    {
    public:
    void Button1_Click(){
    ISplitter * splitter1= new BinarySplitter();//依赖具体类
    splitter1->split();
    }
    void Button2_Click(){
    ISplitter * splitter2= new ImageSplitter();//依赖具体类
    splitter1->split();
    }
    };
    1
    2
    3
    4
    5
    6
    class Splitter{
    protected:
    vitural split() = 0;
    ~Splitter() {}

    };
    1
    2
    3
    4
    5
    6
    7
    8
    9
    class BinarySplitter : public Splitter{
    protected:
    split();
    };

    class ImageSplitter : public Splitter{
    protected:
    split();
    };
  • 该例子中MainForm作为高层模块中依赖于底层模块的变化,违背了依赖倒置原则

  • 具体讲就是MainForm作为抽象功能却依赖的具体的对象BinarySplitterImageSplitter

思路:隐藏变化(具体实现)

  • MainForm中必须要用到一个实现,但我们又要隐藏他的实现细节
  • 利用虚函数的多态性,使用一个抽象接口来实例化具体类
    1
    2
    3
    4
    5
    6
    7
    8
    9
    class MainForm : public Form
    {
    public:
    void Button_Click(){
    SplitterFactory sf;
    ISplitter * splitter = sf.CreateSplitter();//依赖具体类
    splitter->split();
    }
    };
    1
    2
    3
    4
    5
    6
    class SplitterFactory{
    public:
    ISplitter * CreateSplitter(){
    return new BinarySplitter(); //or new ImageSplitter()
    }
    };
  • 这一版代码,虽然看似在MainForm隐藏了具体实现,但他依赖的SplitterFactory类还是要依赖具体实现,所以还需要进一步改造

将实现放在未来

  • SplitterFactory::CreateSplitter定义为纯虚函数
    1
    2
    3
    4
    5
    6
    7
    8
    9
    class MainForm : public Form
    {
    public:
    void Button_Click(){
    SplitterFactory *sf;
    ISplitter * splitter = sf->CreateSplitter();
    splitter->split();
    }
    };
    1
    2
    3
    4
    5
    class SplitterFactory{
    public:
    virtual ISplitter * CreateSplitter() = 0;
    ~SplitterFactory(){}
    };
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    class BinarySplitterFactory : SplitterFactory{
    public:
    ISplitter * CreateSplitter() {
    return new BinarySplitter();
    }
    };

    class ImageSplitterFactory : SplitterFactory{
    public:
    ISplitter * CreateSplitter() {
    return new ImageSplitter();
    }
    };
  • 此时,MainForm就完全不依赖具体的实现了,所有的方法都是抽象的
  • 那么新的问题来了,MainForm中的sf如何初始化呢

最终的版本

  • 通常是将sf作为MainForm的成员变量,因为他可能被多次使用
  • 并通过MainForm的构造函数初始化
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    class MainForm : public Form
    {
    SplitterFactory *sf;
    public:
    MainForm(SplitterFactory *sf) : sf(sf) {}
    void Button_Click(){
    ISplitter * splitter = sf->CreateSplitter();
    splitter->split();
    }
    };
    1
    2
    3
    4
    5
    class SplitterFactory{
    public:
    virtual ISplitter * CreateSplitter() = 0;
    ~SplitterFactory(){}
    };
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    class BinarySplitterFactory : SplitterFactory{
    public:
    ISplitter * CreateSplitter() {
    return new BinarySplitter();
    }
    };

    class ImageSplitterFactory : SplitterFactory{
    public:
    ISplitter * CreateSplitter() {
    return new ImageSplitter();
    }
    };
一点疑问
  • 既然MainForm可以传入多态指针,那为什么不直接传入ISplitter*呢,何必绕一圈? - 猜想可能是sf中未来可能集成各种不同对象的create

总结

  • 类图

以上。