当前位置:   article > 正文

李建忠设计模式之“对象创建”模式_设计模式创建对象

设计模式创建对象


通过“对象创建”模式绕开new,来避免对象创建(new)过程中所导致的紧耦合(依赖具体类),从而支持对象创建的稳定。它是接口抽象之后的第一步工作。

典型模式:Factory Method、Abstract Factory、Prototype、Builder

工厂方法模式(Factory Method)

定义

定义一个用于创建对象的接口,让子类决定实例化哪一个类。Factory Method使得一个类的实例化延迟(目的:解耦,手段:虚函数)到子类。

——《设计模式》GoF

动机

  • 在软件系统中,经常面临着创建对象的工作;由于需求的变化,需要创建的对象的具体类型经常变化。
  • 如何应对这种变化? 如何绕过常规的对象创建方法(new),提供一种“封装机制”来避免客户程序和这种“具体对象创建工作”的紧耦合?

结构图

image-20220925171505890

代码

class IProduct{
    string name;
public:
    virtual void run() = 0;
    virtual ~IProduct(){}
};
class ProductA: public IProduct{
    virtual void run(){
        //...
    }
};
class ProductB: public IProduct{
    virtual void run(){
        //...
    }   
};
class IFactory{
public:
    virtual IProduct * createProduct() = 0;
    virtual ~IFactory(){}
};
class ProductAFactory : public IFactory{
public:
    virtual IProduct * createProduct() {
    	return new ProductA();
    }
};
class ProductBFactory : public IFactory{
public:
    virtual IProduct * createProduct() {
    	return new ProductB();
    }
}; 

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

要点

  • Factory Method模式用于隔离类对象的使用者和具体类型之间的耦合关系。面对一个经常变化的具体类型,紧耦合关系(new)会导致软件的脆弱。
  • Factory Method模式通过面向对象的手法,将所要创建的具体对象工作延迟到子类,从而实现一种扩展(而非更改)的策略,较好地解决了这种紧耦合关系。
  • Factory Method模式解决“单个对象”的需求变化。缺点在于要求创建方法/参数相同。

总结

Factory Method模式封装了对象的创建工作,从而避免了客户程序的new操作,只需调用工厂方法即可,解决了客户程序和调用对象之间的紧耦合。

抽象工厂模式(Abstract Factory)

定义

提供一个接口,让该接口负责创建一系列“相关或者相互依赖的对象”,无需指定它们具体的类。

——《设计模式》GoF

动机

  • 在软件系统中,经常面临着“一系列相互依赖的对象”的创建工作;同时,由于需求的变化,往往存在更多系列对象的创建工作。
  • 如何应对这种变化?如何绕过常规的对象创建方法(new),提供一种“封装机制”来避免客户程序和这种“多系列具体对象创建工作的紧耦合?

结构图

image-20220925171956304

代码

// 假设产品A实现x和产品B只能和对应的实现x搭配,不匹配就会产生错误
// 产品A基类
class IProductA{
    string name;
public:
    virtual void run() = 0;
    virtual ~IProductA(){}
};
// 产品A实现1
class ProductA1: public IProductA{
    virtual void run(){
        //...
    }
};
// 产品A实现2
class ProductA2: public IProductA{
    virtual void run(){
        //...
    }   
};
// 产品B基类
class IProductB{
    string name;
public:
    virtual void run() = 0;
    virtual ~IProductB(){}
};
// 产品B实现1
class ProductB1: public IProductB{
    virtual void run(){
        //...
    }
};
// 产品B实现2
class ProductB2: public IProductB{
    virtual void run(){
        //...
    }   
};

// 工厂基类
class IProductFactory{
public:
    virtual IProductA * createProductA() = 0;
    virtual IProductB * createProductB() = 0;
    virtual ~IProductFactory(){}
}; 
// 工厂1:生产产品实现1
class ProductBFactory1 :public IProductFactory{
public:
    virtual IProductA * createProductA() {
    	return new ProductA1();
    }
    virtual IProductB * createProductB() {
    	return new ProductB1();
    }
}; 
// 工厂2:生产产品实现2
class ProductBFactory2 :public IProductFactory{
public:
    virtual IProductA * createProductA() {
    	return new ProductA2();
    }
    virtual IProductB * createProductB() {
    	return new ProductB2();
    }
};

int main(int argc, char const *argv[]){
    // 新建一类产品
    IProductFactory pf1 =  new ProductBFactory1;
    IProductA = pf1.createProductA();
    IProductB = pf1.createProductB();
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75

要点

  • 如果没有应对“多系列对象构建”的需求变化,则没有必要使用Abstract Factory模式,这时候使用简单的工厂完全可以。
  • “系列对象”指的是在某一特定系列下的对象之间有相互依赖、或作用的关系。不同系列的对象之间不能相互依赖。
  • Abstract Factory模式主要在于应对“新系列”的需求变动。其缺点在于难以应对“新对象”的需求变动。

总结

Abstract Factory其实和Factory Method模式类似,只不过他将一系列关联性非常强的对象创建工作封装到了一个类中,这样可以一定程度上避免客户程序创建不匹配的对象进行操作。

原型模式(Prototype)

定义

使用原型实例(仅供克隆)指定创建对象的种类,然后通过拷贝这些原型来创建新的对象。

——《设计模式》GoF

动机

  • 在软件系统中,经常面临着“某些结构复杂的对象”的创建工作;由于需求的变化,这些对象经常面临着剧烈的变化,但是它们却拥有比较稳定一致的接口
  • 如何应对这种变化?如何向“客户程序(使用这些对象的程序)”隔离出“这些易变对象”,从而使得“依赖这些易变对象的客户程序”不随着需求改变而改变?

结构图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-efVMMPAA-1664189552211)(http://qny.cwnest.top/designPattern/原型模式类图.png)]

代码

// 抽象类
class ISplitter{
public:
    virtual ISplitter* clone()=0;//通过克隆自己来创建对象
    virtual ~ISplitter(){}
};

// 具体类
class BinarySplitter : public ISplitter{
public:
    virtual ISplitter* clone(){
        // C++调用拷贝构造函数,其他语言实现方式可能不同
        // 需要实现深拷贝
        return new BinarySplitter(*this);
    }
};

class TxtSplitter : public ISplitter{
public:
    virtual ISplitter* clone(){
        return new TxtSplitter(*this);
    }
};
class MainForm : public Form
{
    ISplitter* prototype;//原型对象
public:
    MainForm(SplitterFactory* prototype){
        this->prototype=prototype;
    }

    void Button1_Click(){
        ISplitter * splitter = prototype->clone();//克隆原型
        splitter->split();//使用克隆对象进行操作
    }
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

要点

  • Prototype模式同样用于隔离对象的使用者和**具体类型(易变类)**之间的耦合关系,它同样要求这些“易变类”拥有稳定的接口。
  • Prototype模式对于“如何创建易变类的实体对象“采用原型克隆的方法来做,它使得我们可以非常灵活地动态创建“拥有某些稳定接口”的新对象——所需工作仅仅是注册一个新类的对象(即原型),然后在任何需要的地方clone。
  • Prototype模式中的Clone方法可以利用某些框架中的序列化来实现深拷贝。(c++用拷贝构造函数即可)

总结

原型模式一般来说使用场景较少,适用于工厂方法创建对象比较复杂繁琐的情况,它利用已经处于某种状态的原型对象克隆出新的对象,从而避免了复杂的创建工作,只需在克隆对象上进行相对少量的操作就能达到目的。

构建器模式(Builder)

定义

将一个复杂对象的构建与其表示相分离,使得同样的构建过程(稳定)可以创建不同的表示(变化)。

——《设计模式》GoF

动机

  • 在软件系统中,有时候面临着一个复杂对象的创建工作,其通常由各个部分的子对象用一定的算法构成;由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将它们组合在一起的算法却相对稳定
  • 如何应对这种变化?如何提供一种“封装机制”来隔离出“复杂对象的各个部分”的变化,从而保持系统中的“稳定构建算法”不随着需求改变而改变?

结构图

builder

代码

// 房子抽象基类
class House{
    //...
};
// 房子构建类抽象基类
class HouseBuilder{
public:
    House *GetResult(){
        return pHouse;
    }
    virtual ~HouseBuilder() {}
protected:
    House *pHouse;
    virtual void BuildPart1() = 0;
    virtual void BuildPart2() = 0;
    virtual void BuildPart3() = 0;
    virtual void BuildPart4() = 0;
    virtual void BuildPart5() = 0;
}

// 负责House类的创建流程(也可以不单独声明成类,放在HouseBuilder中)
class HouseDirector{
    HouseBuilder *pHouseBuilder;
public:
    HouseDirector(HouseBuilder *hb){
        this->pHouseBuilder = hb;
    }

    House *Construct(){
        pHouseBuilder->BuildPart1();
        for (int i = o; i < 4; i++){
            pHouseBuilder->BuildPart2();
        }
        bool flag = pHouseBuilder->BuildPart3();
        if (flag){
            pHouseBuilder->BuildPart4();
        }
        pHouseBuilder->BuildPart5();
        return pHouseBuilder->GetResult();
    }
};

// 具体实现(石头房子)
class StoneHouse : public House {
    // ...
};
class StoneHouseBuilder : public HouseBuilder{
protected:
    virtual void BuildPart1(){
        // pHouse->Part1 = . . ;
    }
    virtuat void BuildPart2() {}
    virtual void BuildPart3() {}
    virtual void BuildPart4() {}
    virtual void BuildPart5() {}
}

int main(){
    HouseBuilder stoneHouseBuilder =  new StoneHouseBuilder();
    HouseDirector houseDirector = new HouseDirector(stoneHouseBuilder);
    House* house = houseDirector.Construct();//得到石头房子
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62

要点

  • Builder模式主要用于“分步骤构建一个复杂的对象”。在这其中“分步骤”是一个稳定的算法,而复杂对象的各个部分则经常变化。
  • 变化点在哪里,封装哪里——Builder模式主要在于应对“复杂对象各个部分”的频繁需求变动。其缺点在于难以应对“分步骤构建算法”的需求变动
  • 在Builder模式中,要注意不同语言中构造器内调用虚函数的差别(C++Vs.C#)。C++不能直接在构造器中调用虚函数,而C#、java等高级语言可以。

tips:为什么C++中父类对象构造函数调用虚函数不能实现多态?

因为在C++中子类初始化时要先调用父类的构造函数,如果在父类构造函数中调用一个虚函数当然无法调用子类对于父类虚函数的重载实现(因为此时子类还没有创建)。

总结

Builder模式和Template Method模式非常类似。特殊点在于Builder模式实在初始化类的时候拥有稳定的构建方式。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/正经夜光杯/article/detail/897275
推荐阅读
相关标签
  

闽ICP备14008679号