前言
设计模式算是一本非常基础的书籍,在无数程序员先驱的开发过程中总结了很多经验,提炼出了这20+的设计范式供大家参考。本系列文章是一次重新学习的过程,也是一次分享的过程,java版本的模式讲解已经非常多了,c++方面的资料确实要少一些,在原理上来讲设计模式无论是c++还是Java都是相通的,在这里我将会用c++来将设计模式实现一遍。实践出真知!
一、工厂模式简介
程序设计存在的问题:
1)为了提高内聚(Cohesion)和松耦合(Coupling),我们经常会抽象出一些类的公共
接口以形成抽象基类或者接口。这样我们可以通过声明一个指向基类的指针来指向实际的子
类实现,达到了多态的目的。这里很容易出现的一个问题 n 多的子类继承自抽象基类,我们
不得不在每次要用到子类的地方就编写诸如 new ×××;的代码。这里带来两个问题 1)客
户程序员必须知道实际子类的名称(当系统复杂后,命名将是一个很不好处理的问题,为了
处理可能的名字冲突,有的命名可能并不是具有很好的可读性和可记忆性,就姑且不论不同
程序员千奇百怪的个人偏好了。),2)程序的扩展性和维护变得越来越困难。
2)还有一种情况就是在父类中并不知道具体要实例化哪一个具体的子类。这里的意思
为:假设我们在类 A 中要使用到类 B,B 是一个抽象父类,在 A 中并不知道具体要实例化
那一个 B 的子类,但是在类 A 的子类 D 中是可以知道的。在 A 中我们没有办法直接使用类
似于 new ×××的语句,因为根本就不知道×××是什么。
以上两个问题也就引出了 Factory 模式的两个最重要的功能:
1)定义创建对象的接口,封装了对象的创建;
2)使得具体化类的工作延迟到了子类中。
二、简单工厂模式(Simple Factory)
- 说明
我们通常使用 Factory 模式来解决上面给出的两个问题。在第一个问题中,我们经常声明一个创建对象的接口,并封装对象的创建过程。Factory 这里类似于一个真正意义上的工厂(生产对象)。在第二个问题中,我们需要提供一个对象创建对象的接口,并在子类中提供其具体实现(因为只有在子类中可以决定到底实例化哪一个类)。
第一中情况的 Factory 的结构示意图为:
下面我们使用手机生产来讲解该模式:
我们现在拥有两个品牌的手机需要生产,很显然两个品牌的手机拥有同样的基类,为了生产两种品牌的手机(生成对象),我们需要定义一个工厂类来专门负责生产手机的工作。
- |__ phone.h:
#pragma once
#ifndef _Phone_H_
#define _Phone_H_
//Phone类:手机标准规范类(AbstractProduct)
class Phone
{
public:
virtual ~Phone() = 0;
protected:
Phone();
private:
};
// HuaweiPhone类:制造华为手机(Product1)
class HuaweiPhone :public Phone
{
public:
~HuaweiPhone();
HuaweiPhone();
void MakeHuaweiPhone();
protected:
private:
};
//IPhone类:制造苹果手机(Product2)
class IPhone :public Phone
{
public:
~IPhone();
IPhone();
void MakeIPhone();
protected:
private:
};
#endif //~_Phone_H_
- |__ phone.cpp:
#include "Product.h"
#include <iostream>
using namespace std;
Phone::Phone()
{
}
Phone::~Phone()
{
}
HuaweiPhone::HuaweiPhone()
{
this->MakeHuaweiPhone();
}
HuaweiPhone::~HuaweiPhone()
{
}
void HuaweiPhone::MakeHuaweiPhone()
{
cout << "Make HuaweiPhone!"<<endl;
}
IPhone::IPhone()
{
this->MakeIPhone();
}
IPhone::~IPhone()
{
}
void IPhone::MakeIPhone()
{
cout << "Make IPhone!"<<endl;
}
- |__ phoneFactory.h:
#pragma once
#ifndef _FACTORY_H_
#define _FACTORY_H_
#include <string>
class Phone;
//PhoneFactory类:手机代工厂(Factory)
class PhoneFactory
{
public:
virtual ~PhoneFactory();
PhoneFactory();
Phone* CreatePhone(std::string type);
protected:
private:
};
- |__ phoneFactory.cpp:
#include "Factory.h"
#include "Product.h"
#include <iostream>
using namespace std;
PhoneFactory::PhoneFactory()
{
}
PhoneFactory::~PhoneFactory()
{
}
Phone* PhoneFactory::CreatePhone(string type)
{
if (type == "HuaweiPhone") {
return new HuaweiPhone();
}
else if (type == "IPhone") {
return new IPhone();
}
return NULL;
}
- |__ main.cpp:
#include "Factory.h"
#include "Product.h"
#include <iostream>
using namespace std;
int main(int argc, char* argv[])
{
PhoneFactory *factory = new PhoneFactory();
Phone *huaweiPhone= factory->CreatePhone("HuaweiPhone"); // make HuaweiPhone!
IPhone *iPhone = (IPhone*)factory->CreatePhone("IPhone"); // make iphone!
system("pause");
return 0;
}
如上所示就实现了一个简单生产模式。
三、工厂方法模式(Factory Method)
- 说明
上图所以的 Factory 模式经常在系统开发中用到,但是这并不是 Factory 模式的最大威力所在(因为这可以通过其他方式解决这个问题)。Factory 模式不单是提供了创建对象的接口,其最重要的是延迟了子类的实例化(第二个问题),以下是这种情况的一个 Factory 的结构示意图:
图 2 中关键中 Factory 模式的应用并不是只是为了封装对象的创建,而是要把对象的创建放到子类中实现:Factory 中只是提供了对象创建的接口,其实现将放在 Factory 的子类ConcreteFactory 中进行。这是图 2 和图 1 的区别所在。
接下来继续使用生产手机的例子来讲解该模式其中和产品相关的Phone类、HuaweiPhone类和IPhone类的定义不变。代码演示仅展示增量。
- |__ phoneFactory.h:
#pragma once
#ifndef _FACTORY_H_
#define _FACTORY_H_
#include <string>
class Phone;
class PhoneFactory
{
public:
virtual ~PhoneFactory();
PhoneFactory();
virtual Phone* CreatePhone() = 0;
protected:
private:
};
class HuaweiFactory :public PhoneFactory
{
public:
~HuaweiFactory();
HuaweiFactory();
Phone* CreatePhone();
protected:
private:
};
class AppleFactory :public PhoneFactory
{
public:
~AppleFactory();
AppleFactory();
Phone* CreatePhone();
protected:
private:
};
#endif //~_FACTORY_H_
- |__ phoneFactory.cpp:
#include "Factory.h"
#include "Product.h"
#include <iostream>
using namespace std;
PhoneFactory::PhoneFactory()
{
}
PhoneFactory::~PhoneFactory()
{
}
HuaweiFactory::HuaweiFactory()
{
}
HuaweiFactory::~HuaweiFactory()
{
}
Phone* HuaweiFactory::CreatePhone()
{
return new HuaweiPhone();
}
AppleFactory::AppleFactory()
{
}
AppleFactory::~AppleFactory()
{
}
Phone* AppleFactory::CreatePhone()
{
return new IPhone();
}
- |__ main.cpp:
#include "Factory.h"
#include "Product.h"
#include <iostream>
using namespace std;
int main(int argc, char* argv[])
{
PhoneFactory *huaweiFactory = new HuaweiFactory();
PhoneFactory *appleFactory = new AppleFactory();
Phone *HuaweiPhone = huaweiFactory->CreatePhone();
IPhone *iPhone = (IPhone*)appleFactory->CreatePhone();
system("pause");
return 0;
}
四、抽象工厂模式(Abstract Factory)
上面两种模式不管工厂怎么拆分抽象,都只是针对一类产品Phone(AbstractProduct),如果要生成另一种产品PC,应该怎么表示呢?
最简单的方式是把2中介绍的工厂方法模式完全复制一份,不过这次生产的是PC。但同时也就意味着我们要完全复制和修改Phone生产管理的所有代码,显然这是一个笨办法,并不利于扩展和维护。
抽象工厂模式通过在AbstarctFactory中增加创建产品的接口,并在具体子工厂中实现新加产品的创建,当然前提是子工厂支持生产该产品。否则继承的这个接口可以什么也不干。
其UML类图如下:
抽象工厂模式的实现也较为简单,此处就略去不表了。
五、模式的利弊
-
工厂模式是为了解耦。可以将对象的创建和使用分离,如果不分离,不但违反了设计模式的开闭原则,需要需要使用另一个子类的话,需要修改源代码 ,把对象的创建和使用的过程分开。就是Class A 想调用 Class B ,那么A只是调用B的方法,而至于B的实例化,就交给工厂类。
-
工厂模式可以降低代码重复。如果创建对象B的过程都很复杂,需要一定的代码量,而且很多地方都要用到,那么就会有很多的重复代码。我们可以这些创建对象B的代码放到工厂里统一管理。既减少了重复代码,也方便以后对B的创建过程的修改维护。(当然,我个人觉得也可以把这些创建过程的代码放到类的构造函数里,同样可以降低重复率,而且构造函数本身的作用也是初始化对象。不过,这样也会导致构造函数过于复杂,做的事太多,不符合java 的设计原则。)
由于创建过程都由工厂统一管理,所以发生业务逻辑变化,不需要找到所有需要创建B的地方去逐个修正,只需要在工厂里修改即可,降低维护成本。同理,想把所有调用B的地方改成B的子类B1,只需要在对应生产B的工厂中或者工厂的方法中修改其生产的对象为B1即可,而不需要找到所有的new B()改为new B1()。 -
因为工厂管理了对象的创建逻辑,使用者并不需要知道具体的创建过程,只管使用即可,减少了使用者因为创建逻辑导致的错误。
-
可以通过参数设置,返回不同的构造函数,不需要修改使用类的地方。如果一个类有多个构造方法(构造的重写),我们也可以将它抽出来,放到工厂中,一个构造方法对应一个工厂方法并命名一个友好的名字,这样我们就不再只是根据参数的不同来判断,而是可以根据工厂的方法名来直观判断将要创建的对象的特点。这对于使用者来说,体验比较好。
举个例子
一个数据库工厂:可以返回一个数据库实例,可以是mysql,oracle等。
这个工厂就可以把数据库连接需要的用户名,地址,密码等封装好,直接返回对应的数据库对象就好。不需要调用者自己初始化,减少了写错密码等等这些错误。调用者只负责使用,不需要管怎么去创建、初始化对象。
❤️❤️❤️ 如果本文对你有所帮助,请不要忘了点赞、关注、收藏哦!灰常感谢! ❤️❤️❤️