设计模式
创建型模式
创建型模式(Creational Pattern)主要关注对象创建过程的灵活性和可复用性
工厂方法
- 工厂方法模式是一种创建型设计模式, 其在父类中提供一个创建对象的方法, 允许子类决定实例化对象的类型
- 如果无法预知对象确切类别及其依赖关系时,可使用工厂方法
-
工厂方法模式建议使用特殊的工厂 方法代替对于对象构造函数的直接调用 (即使用
new
运算符)。 不用担心, 对象仍将通过new
运算符创建, 只是该运算符改在工厂方法中调用罢了。 工厂方法返回的对象通常被称作 "产品"。 -
工厂方法(Factory Method)模式适用于将对象的创建和使用分离,并希望尽可能多地复用现有代码来处理新型对象的情景。
c++class Network { public: virtual ~Network() { cout << "Network destroyed" << endl; } virtual string getNetworkType() const = 0; }; class CNN : public Network { public: string getNetworkType() const override { return "CNN"; } }; class GAN : public Network { public: string getNetworkType() const override { return "GAN"; } };
- 纯虚函数
getNetworkType()
用来返回网络的类型
- 纯虚函数
抽象工厂
- 抽象工厂(Abstract Factory)适用于需要创建许多种一系列相互依赖的对象的情景。
- 如果代码需要与多个不同系列的相关产品交互,但是由于无法提前获取相关信息,或者出于对未来扩展性的考虑,你不希望代码基于产品的具体类进行构建,在这种情况下,你可以使用抽象工厂
- 工厂方法与抽象工厂的根本区别在于工厂方法是"方法",抽象工厂是"对象"。具体地,工厂方法的目的在于用基类的不同子类来表示不同的对象;抽象工厂只需要关心如何创建一系列相互依赖的对象,可以理解为一种包含很多工厂方法的对象。
抽象工厂模式建议为系列中的每件产品明确声明接口 (例如椅子、 沙发或咖啡桌)。 然后, 确保所有产品变体都继承这些接口。 例如, 所有风格的椅子都实现 椅子
接口; 所有风格的咖啡桌都实现 咖啡桌
接口, 以此类推
例如, 现代家具工厂
ModernFurnitureFactory只能创建 现代椅子
ModernChair 、 现代沙发
ModernSofa和 现代咖啡桌
ModernCoffeeTable对象。
-
在两个
Network
的子类里,我们调用Dataset
接口的方式是相同的:dataset.getDatasetName()
,区别在于我们需要在CNN
的实现当中只关心CNN
的数据预处理,在GAN
当中只关心GAN
的数据预处理pythonclass Dataset { public: virtual ~Dataset() { cout << "Dataset destroyed" << endl; } virtual string getDatasetName() const = 0; }; class ImageNet : public Dataset { public: string getDatasetName() const override { return "ImageNet"; } }; class CelebA : public Dataset { public: string getDatasetName() const override { return "CelebA"; } }; class Network { public: virtual ~Network() { cout << "Network destroyed" << endl; } virtual string getNetworkType() const = 0; virtual string giveMeADataset(const Dataset& dataset) const = 0; }; class CNN : public Network { public: string getNetworkType() const override { return "CNN"; } string giveMeADataset(const Dataset& dataset) const override { const string dataset_name = dataset.getDatasetName(); return "I am training a CNN with " + dataset_name; } }; class GAN : public Network { public: string getNetworkType() const override { return "GAN"; } string giveMeADataset(const Dataset& dataset) const override { const string dataset_name = dataset.getDatasetName(); return "I am training a GAN with " + dataset_name; } };
-
抽象工厂 是一种创建型设计模式, 它能创建一系列相关或相互依赖的对象, 而无需指定其具体类。什么是 "系列对象"? 例如有这样一组的对象:
运输工具
+引擎
+控制器
。 它可能会有几个变体:汽车
+内燃机
+方向盘
飞机
+喷气式发动机
+操纵杆
如果你的程序中并不涉及产品系列的话, 那就不需要抽象工厂。
生成器
生成器(Builder)模式适用于需要分很多步骤创建不同对象的情景。也就是说我所创建的对象可以由许多重复性的基本组件构成。
-
基于神经网络的分类模型,具体的结构可以有很多种,例如只有1个全连通层的线性模型、包含多个全连通层的MLP、以及同时包含多个卷积层和全连通层的CNN等。这些不同的模型都是由"全连通层"和"卷积层"这两个基本单元构成的
pythonclass Builder { public: virtual ~Builder() { cout << "Builder destroyed" << endl; } virtual void createLinearLayer() const = 0; virtual void createConvLayer() const = 0; virtual void createLossLayer() const = 0; };
-
定义模块化组件进行组装
原型
原型(Prototype)模式适用于需要复制复杂的对象,并希望新复制的对象独立于原来的代码的情景。
clone()
接口就是原型模式的关键所在
单例
单例(Singleton)模式适用于一个类只需要一个实例的情景。抽象工厂模式、生成器模式和原型模式都可以用单例模式实现
-
我们一方面要避免在代码中显示地对模型进行复制(否则内存会爆掉),另一方面在设计模型的接口时也要保证其他人在调用我们的接口时不会随意地复制模型
c++class Model { private: Model() {} ~Model() {} protected: Model(const string id) : model_id(id) {} static Model* model; string model_id; public: Model(const Model&) = delete; Model(Model&&) = delete; void operator=(const Model&) = delete; void operator=(Model&&) = delete; void train() { cout << "Training done with " + model_id + "\n"; } static Model* getInstance(const string& id); static Model& getInstanceSafe(const string& id); }; Model* Model::model = nullptr; Model* Model::getInstance(const string& id) { if (model == nullptr) model = new Model(id); return model; }
-
我们只希望
Model
自己有权对模型进行创建和删除,所以把constructor
和destructor
声明为private
。另外,我们不希望Model
的调用者对模型进行复制,所以需要禁用其copy constructor
,move constructor
,copy assignment operator
和move assignment operator
-
所有单例的实现都包含以下两个相同的步骤:
-
将默认构造函数设为私有, 防止其他对象使用单例类的
new
运算符。 -
新建一个静态构建方法作为构造函数。 该函数会 "偷偷" 调用私有构造函数来创建对象, 并将其保存在一个静态成员变量中。 此后所有对于该函数的调用都将返回这一缓存对象。
-