创建型模式指的是 创建对象 或是 获取实例 的方式。
1、工厂模式
平时写一些简单的代码可能会直接用 new 创建出一个对象,但是实际在阅读一些功能比较多、规模比较庞大的工程时,可能会发现有多个类继承于同一个基类的情况,它们拥有同样的接口但是实现了不同的功能。它们可能是可以互相替代的两套系统(例如 Android Media 中的 ACodec 和 CCodec),也有可能是多个不同功能的工具(例如 MediaExtractor 中不同的 extractor),创建这类对象时往往都用到了工厂模式
。
这里的工厂模式我觉得是一个广义的概念,指的是在创建有具有相同特征(相同基类)的对象时不要硬编码(hard code),这样会在工程庞大之后变得难以维护,我们需要一种动态的创建方法,可以根据条件灵活创建具体的类。这里的创建方法并不仅仅指的是如下三种具体的工厂设计模式,也有可能是一个条件判断函数。
工厂模式的实现主要依赖的是多态的特性。
1.1、简单工厂模式
我们可以认为具有相同特征(相同基类)的对象可以由一个工厂生产,根据不同的要求,工厂可以生产不同的对象实例,这就是简单工厂模式。
以简单计算器为例,有加法和减法等等计算器类型,这时候我们就可以创建一个计算器工厂,根据传进的符号创建对应的计算器实例:
cpp
class Calculator {
public:
virtual int calculate(int a, int b) = 0;
};
class AddCalculator : public Calculator {
public:
int calculate(int a, int b) {
return a + b;
}
};
class SubCalculator : public Calculator {
public:
int calculate(int a, int b) {
return a - b;
}
};
class CalculatorFactory {
public:
static Calculator* createCalculator(const char c) {
Calculator* cal = NULL;
switch (c)
{
case '+':
cal = new AddCalculator;
break;
case '-':
cal = new SubCalculator;
break;
default:
break;
}
return cal;
}
};
int main() {
Calculator* addCal = CalculatorFactory::createCalculator('+');
printf("5 + 5 = %d\n", addCal->calculate(5, 5));
Calculator* subCal = CalculatorFactory::createCalculator('-');
printf("5 - 5 = %d\n", subCal->calculate(5, 5));
delete addCal;
delete subCal;
return 0;
}
用简单工厂模式有什么好处呢?
- 只要给工厂指定计算器类型,它就可以为我们创建出对应实例,创建实例的接口被统一,后期如果要修改对象类型,只需要修改传给工厂的参数即可;
- 后期如何要增加新的计算器类型,我们只要修改工厂即可,使用方式仍旧可以保持统一。
1.2、工厂方法模式
简单工厂需要指定创建的实例类型,指定
这个动作也可以看作为hard code的一种,有没有办法让工厂为我们自动选择创建的实例类型呢?工厂方法模式可以帮助我们完成自动选择的动作。
我们对上面简单计算器例子做一些修改:
cpp
class Calculator {
public:
virtual int calculate(int a, int b) = 0;
};
class AddCalculator : public Calculator {
public:
int calculate(int a, int b) {
return a + b;
}
};
class SubCalculator : public Calculator {
public:
int calculate(int a, int b) {
return a - b;
}
};
class CalFactory {
public:
virtual Calculator* createCalculator() = 0;
};
class AddCalFactory : public CalFactory {
public:
Calculator* createCalculator() {
return new AddCalculator;
}
};
class SubCalFactory : public CalFactory {
public:
Calculator* createCalculator() {
return new SubCalculator;
}
};
int main() {
CalFactory *addFactory = new AddCalFactory;
Calculator* addCal = addFactory->createCalculator();
printf("5 + 5 = %d\n", addCal->calculate(5, 5));
CalFactory *subFactory = new SubCalFactory;
Calculator* subCal = subFactory->createCalculator();
printf("5 - 5 = %d\n", subCal->calculate(5, 5));
delete addCal;
delete subCal;
delete addFactory;
delete subFactory;
return 0;
}
例子中每一个 Calculator 类都有其对应的 CalFactory 工厂,想要创建需要的类型只要实例化对应的工厂就可以了。
我们在网上其他博客中看到的工厂方法模式一般到这里就结束了,这里就会有疑问了,自动选择是如何体现的呢?工厂方法模式相比简单工厂优点在哪里呢?
接下来以 Android MediaPlayerFactory 为例看一下工厂方法模式在实战中是如何使用的:
MediaPlayerFactory 中有一个 getPlayerType
方法,它会遍历每一个具体的 IFactory,调用它们的 scoreFactory 方法得到最优分数,得到最优工厂类型;然后再调用MediaPlayerFactory createPlayer
方法,用自动选择的工厂创建出实例(当然分数选择和实例化对象可以放到一个方法中)。
1.3、抽象工厂模式
抽象工厂和工厂方法类似,这里只做简单举例:
cpp
class Pencil {
public:
Pencil(const char* factory) : mFactory(factory) {}
virtual void write() = 0;
protected:
const char* mFactory;
};
class RedPencil : public Pencil {
public:
RedPencil(const char* factory) : Pencil(factory) {}
void write() {
printf("%s", mFactory);
printf(" Red Pencil write\n");
}
};
class BlackPencil : public Pencil {
public:
BlackPencil(const char* factory) : Pencil(factory) {}
void write() {
printf("%s", mFactory);
printf(" Black Pencil write\n");
}
};
class Pen {
public:
Pen(const char* factory) : mFactory(factory) {}
virtual void write() = 0;
protected:
const char* mFactory;
};
class RedPen : public Pen {
public:
RedPen(const char* factory) : Pen(factory) {}
void write() {
printf("%s", mFactory);
printf(" Red Pen write\n");
}
};
class BlackPen : public Pen {
public:
BlackPen(const char* factory) : Pen(factory) {}
void write() {
printf("%s", mFactory);
printf(" Black Pen write\n");
}
};
class Factory {
public:
virtual Pencil* createPencil(int catgory) = 0;
virtual Pen* createPen(int catgory) = 0;
};
class AppleFactory : public Factory {
public:
virtual Pencil* createPencil(int catgory) {
Pencil* p = NULL;
switch (catgory)
{
case 1:
p = new RedPencil("AppleFactory");
break;
case 2:
p = new BlackPencil("AppleFactory");
break;
default:
break;
}
return p;
}
virtual Pen* createPen(int catgory) {
Pen* p = NULL;
switch (catgory)
{
case 1:
p = new RedPen("AppleFactory");
break;
case 2:
p = new BlackPen("AppleFactory");
break;
default:
break;
}
return p;
}
};
class OrangeFactory : public Factory {
public:
virtual Pencil* createPencil(int catgory) {
Pencil* p = NULL;
switch (catgory)
{
case 1:
p = new RedPencil("OrangeFactory");
break;
case 2:
p = new BlackPencil("OrangeFactory");
break;
default:
break;
}
return p;
}
virtual Pen* createPen(int catgory) {
Pen* p = NULL;
switch (catgory)
{
case 1:
p = new RedPen("OrangeFactory");
break;
case 2:
p = new BlackPen("OrangeFactory");
break;
default:
break;
}
return p;
}
};
static Factory* createFactory(const char* factoryName) {
Factory* factory = NULL;
if (!memcmp(factoryName, "Apple", 5))
{
factory = new AppleFactory;
}
else if (!memcmp(factoryName, "Orange", 6)) {
factory = new OrangeFactory;
}
return factory;
}
int main() {
Factory* AppFac = createFactory("Apple");
Pen* AppPen = AppFac->createPen(1);
AppPen->write();
Pencil* AppPencil = AppFac->createPencil(1);
AppPencil->write();
Factory* OraFac = createFactory("Orange");
Pen* OraPen = OraFac->createPen(2);
OraPen->write();
Pencil* OraPencil = OraFac->createPencil(2);
OraPencil->write();
delete AppFac;
delete AppPen;
delete AppPencil;
delete OraFac;
delete OraPen;
delete OraPencil;
}
2、建造者模式
cpp
class Builder;
class Computer {
protected:
friend class Builder;
void setBrand(std::string brand) { mBrand = brand; }
void setCpu(std::string cpu) { mCpu = cpu; }
void setGpu(std::string gpu) { mGpu = gpu; }
void setPrice(int price) { mPrice = price; }
public:
void print() {
printf("%s \t %s \t %s \t %d\n", mBrand.c_str(), mCpu.c_str(), mGpu.c_str(), mPrice);
}
private:
std::string mBrand;
std::string mCpu;
std::string mGpu;
int mPrice;
};
class GrapeComputer : public Computer {
public:
GrapeComputer() {
setBrand("Grape");
setGpu("RTX4080");
}
};
class OrangeComputer : public Computer {
public:
OrangeComputer() {
setBrand("Orange");
}
};
class Director;
class Builder {
protected:
friend class Director;
virtual void buildCpu(std::string cpu) { mComputer->setCpu(cpu); };
virtual void buildGpu(std::string gpu) { mComputer->setGpu(gpu); };
virtual void buildPrice(int price) { mComputer->setPrice(price); };
public:
virtual Computer* build() {
return mComputer;
}
protected:
Computer *mComputer;
};
class GrapeBuilder : public Builder
{
public:
GrapeBuilder() {
mComputer = new GrapeComputer();
}
virtual void buildGpu(std::string gpu) override { };
};
class OrangeBuilder : public Builder
{
public:
OrangeBuilder() {
mComputer = new OrangeComputer();
}
};
class Director {
public:
Director(Builder *builder) { mBuilder = builder; }
Computer * construct(std::string cpu, std::string gpu, int price) {
mBuilder->buildCpu(cpu);
mBuilder->buildGpu(gpu);
mBuilder->buildPrice(price);
return mBuilder->build();
}
private:
Builder *mBuilder;
};
int main() {
Builder *grapeBuilder = new GrapeBuilder;
Director *directorA = new Director(grapeBuilder);
Computer *grapeComputer = directorA->construct("core i7 13700k", "standard", 10999);
grapeComputer->print();
delete grapeBuilder;
delete directorA;
delete grapeComputer;
Builder *orangeBuilder = new OrangeBuilder;
Director *directorB = new Director(orangeBuilder);
Computer *orangeComputer = directorB->construct("core i5 12400f", "RTX3060", 5999);
orangeComputer->print();
delete orangeBuilder;
delete directorB;
delete orangeComputer;
}
以上是我阅读网上内容后自己编写的示例,示例主要由 Director 和 Builder 两部分组成。我的理解 Director 就是调用者,不同的 Builder 用于处理不同事务/创建不同的对象。引用网上的分析:
在客户端代码中,无须关心产品对象的具体组装过程,只需确定具体建造者的类型即可,建造者模式将复杂对象的构建与对象的表现分离开来,这样使得同样的构建过程可以创建出不同的表现。
我没有在实战中看到过完全契合以上示例的建造者模式使用,只在
Android MediaCodecList.cpp中看到有类似的使用方法。
实例化 MediaCodecList 时会先调用 GetBuilders 获取 Codec2InfoBuilder 和 OmxInfoBuilder 实例,接着遍历调用他们的 buildMediaCodecList 方法,将所有的信息写到 MediaCodecListWriter 中。在这个示例中虽然没有具体的 Director,也没有返回具体的对象,只有不同的 Builder,但是我觉得和建造者模式的思想是一致的。
3、单例模式
单例模式比较简单,在我们不想重复构建一个对象的时候可以使用单例模式,例如 DataSourceFactory.cpp 和上面提到的 MediaCodecList.cpp 就是典型的单例模式:
cpp
struct MediaCodecList : public BnMediaCodecList {
static sp<IMediaCodecList> getInstance();
private:
static sp<IMediaCodecList> sCodecList;
MediaCodecList(std::vector<MediaCodecListBuilderBase*> builders);
MediaCodecList(const MediaCodecList&) = delete;
MediaCodecList& operator=(const MediaCodecList&) = delete;
}
目前碰到的单例模式使用场景如下:
- 加载并存储信息可供查询,使用单例模式可以避免多次信息加载(MediaCodecList)
- 提供一系列方法可供使用,使用单例模式可以减少对象创建的开销(DataSourceFactory)。
唯一有一点要注意的,在获取对象实例时需要用锁保护,避免有同时调用getInstance 同时创建到对象的情况出现:
cpp
sp<DataSourceFactory> DataSourceFactory::getInstance() {
Mutex::Autolock l(sInstanceLock);
if (!sInstance) {
sInstance = new DataSourceFactory();
}
return sInstance;
}