简单工厂
模式动机
意图
:定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。
主要解决
:主要解决接口选择的问题。
简单工厂模式的要点在于:当你需要什么,只需要传入一个正确的参数,就可以获取你所需要的对象,而无须知道其创建细节。简单工厂模式结构比较简单,其核心是工厂类的设计:
简单工厂模式中包含以下的几个角色:
-
Factory(工厂角色)
:工厂类,它是简单工厂的核心,工厂类可以被外部直接调用,创建所需的产品对象,在工厂类中提供了静态的工厂方法factoryMethod();它的返回类型为抽象产品类型Product。
-
Product(抽象产品角色)
:是工厂类所创建所有对象的父类,封装了各种产品对象的公有方法,它的引入提高了灵活性,使得在工厂类中只需要定义一个通用的工厂方法,因为所有创建的具体产品对象都是其他子类对象。
-
ConcreateProduct(具体产品角色)
:简单工厂的创建目标,所有被创建的对象都充当这个角色的某个具体类的实例,每一个具体产品角色都继承了抽象产品角色,需要实现在抽象产品中声明的抽象方法。
代码示例:
// 系列产品
class Car
{
public:
Car(string name) :_name(name) {}
virtual void show() = 0;
protected:
string _name;
};
class Bmw:public Car
{
public:
Bmw(string name) :Car(name) {}
void show()
{
cout << "获取了一辆宝马汽车:" << _name << endl;
}
};
class Audi :public Car
{
public:
Audi(string name) :Car(name) {}
void show()
{
cout << "获取了一辆奥迪汽车:" << _name << endl;
}
};
// 产品枚举
enum Cartype
{
BMW, AUDI
};
// 简单工厂类
class SimpleFactory
{
public:
// 用户想要创建一个对象,只需要知道名称就可以了
Car* createCar(Cartype ct)
{
switch (ct)
{
case BMW:
return new Bmw("x6");
case AUDI:
return new Audi("a8");
default:
cerr << "传入参数错误:" << ct << endl;
}
return nullptr;
}
};
SimpleFactory* fac = new SimpleFactory();
Car* p1 = fac->createCar(BMW);
Car* p2 = fac->createCar(AUDI);
p1->show();
p2->show();
delete fac;
delete p1;
delete p2;
}
- 在具体产品类中实现了抽象产品类中声明的抽象方法,不同的具产品类提供不同的实现
-
简单工厂模式的核心是工厂类,在没有工厂类之前,客户端一般会使用new来直接创建产品对象
,而引入了工厂类之后,客户端可以通过工厂类来创建产品,在简单工厂模式中,工厂类提供了一个静态方法供客户端使用
当然上述代码也可用智能指针封装,更好的释放资源:
int main()
{
unique_ptr<SimpleFactory> fac(new SimpleFactory());
unique_ptr<Car> p1(fac->createCar(BMW));
unique_ptr<Car> p2(fac->createCar(AUDI));
p1->show();
p2->show();
return 0;
}
简单工厂模式的简化
为了简化简单工厂模式,我们可以将抽象产品类和工厂类合并,将静态工厂方法移动到抽象产品类中,如图所示:
总结
简单工厂优点
简单工厂可以做到,让用户创建对象的时候只需要知道对象的名称就好,而不需要关心创建对象的细节
(BMW是如何建造的、型号是什么等等细节)。
- 一个调用者想创建一个对象,只要知道其名称就可以了;
- 扩展性高,如果想增加一个产品,只要扩展一个工厂类就可以;
- 屏蔽产品的具体实现,调用者只关心产品的接口。
缺点
- 每次增加一个产品时,都需要增加一个具体类,并且修改对象实现工厂,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。
- 每当我们想要扩展对象的时候(增加其他类型的对象)就需要在SimpleFactory类中添加代码,增加switch后面的case选项。这样一来,就需要修改源代码。灵活性非常的差,违反了开闭原则!
基于上面的问题,就有了工厂方法
工厂方法
- 工厂方法的思想就是定义一个
Factory
基类,基类中定义了一个纯虚函数(创建产品);
- 之后
定义派生类
(具体产品的工厂
)负责创建对应的产品
。
- 可以做到
不同的产品在不同的工厂里面创建
,能够对现有工厂以及产品的修改关闭。
具体产品和抽象产品代码不变,接下里是工厂方法的实现:
// 基类(包含纯虚函数,不能实例化对象)
class Factory
{
public:
virtual Car* createCar(string name) = 0;
};
// 宝马汽车工厂,负责生产宝马汽车
class BmwFac: public Factory
{
public:
Car* createCar(string name)
{
return new Bmw(name);
}
};
// 奥迪汽车工厂,负责生产奥迪汽车
class AudiFac :public Factory
{
public:
Car* createCar(string name)
{
return new Audi(name);
}
};
int main()
{
unique_ptr<Factory> bmwfty(new BmwFac());
unique_ptr<Factory> audifty(new AudiFac());
unique_ptr<Car> p1 (bmwfty->createCar("X6"));
unique_ptr<Car> p2 (audifty->createCar("A8"));
p1->show();
p2->show();
return 0;
}
注意:
可以看到这一次的代码和之前简单工厂的不同:
简单工厂:unique_ptr<SimpleFactory> fac(new SimpleFactory())
;
工厂方法:unique_ptr<Factory> bmwfty(new BmwFac())
;
也就是说,工厂方法是用基类指针实例化派生类对象,这也是动态多态发生的条件。这样的话,就能够实现多态了!
之后的unique_ptr<Car> p1 (bmwfty->createCar("X6"))
有没有更贴近生活呢?
我去买车,进到一家宝马4S店(对应具体的bmwfty对象),然后看上了心仪的车型bmw X6,然后告诉店员:“我想要一辆宝马X6”。之后办理完手续,就可以开心地提车了!
总结
工厂方法解决了简单工厂的问题
(无法对修改关闭),但是它也有自己的局限:
试想一下,宝马工厂里面只是售卖成品汽车吗?
应该不是吧,作为一家成熟的工厂,除了汽车之外,还有一系列配套的零件、产品:比如说:轮胎、车灯、真皮豪华座椅等等。也就是说,跟汽车相关联的有一整个产品簇。
但是我们的宝马工厂BmwFac
里面只有一个createCar
方法,如果想要添加产品的话,就需要增加新的类。但是这些产品其实都应该在一个BmwFac
工厂里面。这才是现实的逻辑,另外,工厂类太多,会不好维护。
于是,抽象工厂 应运而生。
抽象工厂
把有关联关系的,属于一个产品簇的所有产品
创建的接口函数,放在一个抽象工厂里面AbstractFactory
,派生类(具体产品的工厂)应该负责创建该产品簇里面所有的产品。
实现: 这一次,除了汽车产品外,我们再添加一个车灯产品,系列产品的实现和继承关系代码如下:
// 系列产品1:汽车
class Car
{
public:
Car(string name) :_name(name) {}
virtual void show() = 0;
protected:
string _name;
};
class Bmw:public Car
{
public:
Bmw(string name) :Car(name) {}
void show()
{
cout << "获取了一辆宝马汽车:" << _name << endl;
}
};
class Audi :public Car
{
public:
Audi(string name) :Car(name) {}
void show()
{
cout << "获取了一辆奥迪汽车:" << _name << endl;
}
};
// 系列产品2:车灯
class Light
{
public:
virtual void show() = 0;
};
class BmwLight : public Light
{
public:
void show() { cout << "BMW light!" << endl; }
};
class AudiLight : public Light
{
public:
void show() { cout << "Audi light!" << endl; }
};
抽象工厂实现:
// 工厂方法 升级=> 抽象工厂(对有一组关联关系的产品簇提供产品对象的统一创建)
class AbstractFactory
{
public:
virtual Car* createCar(string name) = 0; // 工厂方法 创建汽车
virtual Light* createCarLight() = 0; // 工厂方法 创建汽车关联的产品,车灯
};
// 宝马工厂
class BMWFactory : public AbstractFactory
{
public:
Car* createCar(string name)
{
return new Bmw(name);
}
Light* createCarLight()
{
return new BmwLight();
}
};
// 奥迪工厂
class AudiFactory : public AbstractFactory
{
public:
Car* createCar(string name)
{
return new Audi(name);
}
Light* createCarLight()
{
return new AudiLight();
}
};
使用示例:
int main()
{
unique_ptr<AbstractFactory> bmwfty(new BMWFactory());
unique_ptr<AbstractFactory> audifty(new AudiFactory());
unique_ptr<Car> p1(bmwfty->createCar("X6"));
unique_ptr<Car> p2(audifty->createCar("A8"));
unique_ptr<Light> l1(bmwfty->createCarLight());
unique_ptr<Light> l2(audifty->createCarLight());
p1->show();
l1->show();
p2->show();
l2->show();
return 0;
}
其实抽象工厂跟工厂方法比起来,改动就是增加了相关的系列产品后,把产品的创建都放在工厂里,提供多个产品创建的抽象接口,在一个工厂内可以生产多个产品,而非一个产品一个工厂,这样也更加贴近现实社会的逻辑。
抽象工厂其实还存在一个问题:
当一个汽车厂商的一条业务是其他工厂没有的,那么在抽象工厂中添加了这条业务后,其他厂商的工厂方法都必须重写这个业务方法,否则就会变成抽象类,无法实例化。