抽象工厂学习总结(全面)

工厂模式介绍

工厂模式分为简单工厂模式工厂方法模式抽象工厂模式三类,其中工厂方法和抽象工厂模式的定义和结构图如下,简单工厂模式在后面的实例中给出

工厂方法模式

工厂方法模式(Factory Method):定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法是一个类的实例化延迟到其子类。
在这里插入图片描述

抽象工厂模式

抽象工厂模式(Abstract Factory):提供一个创建一系列相关相互依赖对象的接口,而无需指定它们具体的类。
在这里插入图片描述

简单工厂 VS 工厂方法

这里通过实现一个计算器例子来了解简单工厂和工厂方法模式

简单工厂模式的计算器实现

在这里插入图片描述

#include <iostream>
using namespace std;
class CalBase
{
public:
	double num1;
	double num2;
public:
	virtual double cal() = 0;
};

class Add : public CalBase
{
public:
	double cal()
	{
		return num1 + num2;
	}
};

class Sub : public CalBase
{
public:
	double cal()
	{
		return num1 - num2;
	}
};

class Mul : public CalBase
{
public:
	double cal()
	{
		return num1 * num2;
	}
};

class Div :public CalBase
{
public:
	double cal()
	{
		return num1 / num2;
	}
};

class Factory
{
public:
	CalBase* createCal(char type)
	{
		switch (type)
		{
		case '+':
			return new Add();
		case '-':
			return new Sub();
		case '*':
			return new Mul();
		case '/':
			return new Div();
		default:
			cout << "运算符输入错误" << endl;
			break;
		}
		return nullptr;
	}
};
int main()
{
	while (true)
	{
		double num1, num2, result;
		char oper;

		cout << "请输入计算式(运算符为#表示退出):" << endl;
		cin >> num1 >> oper >> num2;

		if (oper == '#') break;

		Factory* ftory = nullptr;
		CalBase* cal = ftory->createCal(oper);
		cal->num1 = num1;
		cal->num2 = num2;

		result = cal->cal();

		cout << "计算结果:" << result << endl;
	}
	return 0;
}

请输入计算式(运算符为#表示退出)50 / 2
计算结果:25
请输入计算式(运算符为#表示退出)1 # 1
优点
  • 简单工厂模式的最大优点在于工厂类中包含了必要的逻辑判断,根据客户端的选择条件动态实例化相关的类。对于客户端来说,去除了与具体产品的依赖,就像你的计算器让客户端不用管该用哪个类的实例,只需要把+传给工厂。工厂自动就给出了相应的实例,客户端只要去做运算就可以了,不同的实力会实现不同的运算。
缺点
  • 如果要加一个求M的N次方的功能。我们是一定需要给运算工厂类的方法里case的分支条件。但这样会修改原有的类,就等于说我们不但对扩展开放了,对修改也就开放了,这样就违背了开放-封闭原则

工厂方法模式的计算器实现

在这里插入图片描述

#include <iostream>
using namespace std;
class CalBase
{
public:
	double num1;
	double num2;
public:
	virtual double cal() = 0;
};

class Add : public CalBase
{
public:
	double cal()
	{
		return num1 + num2;
	}
};

class Sub : public CalBase
{
public:
	double cal()
	{
		return num1 - num2;
	}
};

class Mul : public CalBase
{

public:
	double cal()
	{
		return num1 * num2;
	}
};

class Div :public CalBase
{
public:
	double cal()
	{
		return num1 / num2;
	}
};

// 抽象工厂接口
class IFactory
{
public:
	virtual ~IFactory() {};
	virtual CalBase* Cal() = 0;
};

// 加法工厂
class AddFactory : public IFactory
{
public:
	CalBase* Cal()
	{
		return new Add();
	}
};

// 减法工厂
class SubFactory : public IFactory
{
public:
	CalBase* Cal()
	{
		return new Sub();
	}
};

// 乘法工厂
class MulFactory : public IFactory
{
public:
	CalBase* Cal()
	{
		return new Mul();
	}
};

// 除法工厂
class DivFactory : public IFactory
{
public:
	CalBase* Cal()
	{
		return new Div();
	}
};
int main()
{
	//IFactory* fct = new AddFactory(); 
	IFactory* fct = new DivFactory();
	CalBase* oper = fct->Cal();
	oper->num1 = 10;
	oper->num2 = 30;
	double result = oper->cal();
	cout << result << endl;
}

0.333333
优点
  • 更符合开-闭原则,新增一种产品时,只需要增加相应的具体产品类和相应的工厂子类即可

  • 符合单一职责原则,每个具体工厂类只负责创建对应的产品

  • 不使用静态工厂方法,可以形成基于继承的等级结构

缺点
  • 添加新产品时,除了增加新产品类外,还要提供与之对应的具体工厂类,系统类的个数将成对增加,在一定程度上增加了系统的复杂度;同时,有更多的类需要编译和运行,会给系统带来一些额外的开销

  • 由于考虑到系统的可扩展性,需要引入抽象层,在客户端代码中均使用抽象层进行定义,增加了系统的抽象性和理解难度,且在实现时可能需要用到DOM、反射等技术,增加了系统的实现难度

  • 虽然保证了工厂方法内的对修改关闭,但对于使用工厂方法的类,如果要更换另外一种产品,仍然需要修改实例化的具体工厂类,比如当前的+法运算有两种算法,当我们需要采用另一种算法时需要修改原来的+法运算类

  • 一个具体工厂只能创建一种具体产品

应用
  • 当一个类不知道它所需要的对象的类时,在工厂方法模式中,不需要知道具体产品类的类名,只需要知道所对应的工厂即可

  • 当一个类希望通过其子类来指定创建对象时在工厂方法模式中,对于抽象工厂类只需要提供一个创建产品的接口,而由其子类来确定具体要创建的对象,利用面向对象的多态性和里氏代换原则,在程序运行时,子类对象将覆盖父类对象,从而使得系统更容易扩展。

  • 将创建对象的任务委托给多个工厂子类中的某一个,在使用时可以无须关心是哪一个工厂子类创建产品子类,需要时再动态指定,可将具体工厂类的类名存储在配置文件或数据库中。

抽象工厂 VS 工厂方法

这里利用数据库访问程序来了解抽象工厂和工厂方法

工厂方法模式的数据库访问程序

在这里插入图片描述

#include <iostream>
using namespace std;
// 用户表抽象接口
class IUser
{
public:
	virtual void insertData() = 0;
	virtual void deleteData() = 0;
};

// MySql型数用户据库表
class MySqlUser:public IUser
{
public:
	void insertData()
	{
		cout << "MySql中成功插入一条数据" << endl;
	}
	void deleteData()
	{
		cout << "MySql中成功删除一条数据" << endl;
	}
};

// Oracle型用户数据库表
class OracleUser:public IUser
{
public:
	void insertData()
	{
		cout << "Oracle中成功插入一条数据" << endl;
	}
	void deleteData()
	{
		cout << "Oracle中成功删除一条数据" << endl;
	}
};

// 抽象工厂接口
class IFactory
{
public:
	virtual ~IFactory() {}
	virtual IUser* createUser() = 0;
};

// 具体工厂 MySql数据库
class MySqlFactory :public IFactory
{
public:
	IUser* createUser()
	{
		return new MySqlUser();
	}
};

// 具体工厂 Oracle数据库
class OracleFactory :public IFactory
{
public:
	IUser* createUser()
	{
		return new OracleUser();
	}
};
void main01()
{
	//IFactory* fct = new MySqlFactory(); // 如果要换成Oracle直接修改这里了就可以了
	IFactory* fct = new OracleFactory();

	IUser* usertable = fct->createUser(); // 将生产出来的产品交给对应的接口
	usertable->insertData();
	usertable->deleteData();
}

运行结果:

Oracle 的 User 表中成功插入一条数据
Oracle 的 User 表中成功删除一条数据

抽象工厂模式的数据库访问程序

在这里插入图片描述

#include <iostream>
using namespace std;

// 用户表抽象接口
class IUser
{
public:
	virtual void insertData() = 0;
	virtual void deleteData() = 0;
};

// MySql型数用户据库表
class MySqlUser :public IUser
{
public:
	void insertData()
	{
		cout << "MySql 的 User 表中成功插入一条数据" << endl;
	}
	void deleteData()
	{
		cout << "MySql 的 User 表中成功删除一条数据" << endl;
	}
};

// Oracle型用户数据库表
class OracleUser :public IUser
{
public:
	void insertData()
	{
		cout << "Oracle 的 User 表中成功插入一条数据" << endl;
	}
	void deleteData()
	{
		cout << "Oracle 的 User 表中成功删除一条数据" << endl;
	}
};

// ☆增加部门表抽象接口
class IDepartment
{
public:
	virtual void insertData() = 0;
	virtual void deleteData() = 0;
};

// ☆增加MySql型部门数据库表
class MySqlDepartment:public IDepartment
{
public:
	void insertData()
	{
		cout << "MySql 的 Department 表中成功插入一条数据" << endl;
	}
	void deleteData()
	{
		cout << "MySql 的 Department 表中成功删除一条数据" << endl;
	}
};

// ☆增加Oracle型部门数据库表
class OracleDepartment :public IDepartment
{
public:
	void insertData()
	{
		cout << "Oracle 的 Department 表中成功插入一条数据" << endl;
	}
	void deleteData()
	{
		cout << "Oracle 的 Department 表中成功删除一条数据" << endl;
	}
};

// 抽象工厂接口
class IFactory
{
public:
	virtual ~IFactory() {}
	virtual IUser* createUser() = 0;
	
	virtual IDepartment* createDepartment() = 0; // ☆添加接口
};

// 具体工厂 MySql数据库
class MySqlFactory :public IFactory
{
public:
	IUser* createUser()
	{
		return new MySqlUser();
	}

	IDepartment* createDepartment() // ☆创建新表
	{
		return new MySqlDepartment();
	}
};

// 具体工厂 Oracle数据库
class OracleFactory :public IFactory
{
public:
	IUser* createUser()
	{
		return new OracleUser();
	}
	IDepartment* createDepartment() // ☆创建新表
	{
		return new OracleDepartment();
	}
};
void main03()
{
	//IFactory* fct = new MySqlFactory(); 
	IFactory* fct = new OracleFactory(); // 负责数据库的切换 这里是唯一可变处

	IUser* usertable = fct->createUser(); // 将生产出来的产品交给对应的接口
	usertable->insertData();
	usertable->deleteData();

	IDepartment* depart = fct->createDepartment(); 
	depart->insertData();
	depart->deleteData();
}

运行结果:

Oracle 的 User 表中成功插入一条数据
Oracle 的 User 表中成功删除一条数据

Oracle 的 Department 表中成功插入一条数据
Oracle 的 Department 表中成功删除一条数据

拓展:
这里的抽象工厂模式仍然会存在一些不足,比如:如果我们的客户端程序类不止一个,有很多地方都在使用IUser或者IDepartment,而这样的设计,其实每一个类的开始都需要声明IFactory * fct = new MySqlFactory(),但是如果我有100个调用数据库访问的类,那岂不是要改很多地方,那怎么办呢?
答案是我们可以利用简单工厂来改进抽象工厂,单这不是最优的做法,因为简单工厂会破坏开闭原则。最优的做法是增加反射机制

优点:
  • 抽象工厂的最大好处是易于交换产品系列,由于具体工厂类,在一个应用中只需要在初始化的时候出现一次,这就使得改变一个应用的具体工厂变得非常容易。他只需要改变具体工厂即可使用不同的产品配置。
  • 能够从多个产品族的多个产品中,简洁的获取想要的具体产品。解决了工厂模式中的不符合开闭原则的问题(增加新的产品时候,不修改工厂,而是增加工厂)。
  • 他让具体的创建实例过程与客户端分离,客户端是通过他的抽象接口操纵实例,产品的具体类名也被具体工厂的实现分离,不会出现客户端代码中。
缺点
  • 产品族(比如这里的user和department)扩展比较困难,要增加一个系列的某一产品(再增加一个project表),不仅要增加具体的产品类,还要增加对应的工厂类(或者修改对应产品族的工厂类)。
引用场景

解决两个维度的组合产品的构造问题,取其中一个维度作为产品族,另外一个维度作为产品族中具体的多个产品。

抽象工厂和工厂方法的区别

工厂方法:

  • 一个抽象产品类,可以派生出多个具体产品类。

  • 一个抽象工厂类,可以派生出多个具体工厂类。

  • 每个具体工厂类只能创建一个具体产品类的实例。

抽象工厂:

  • 多个抽象产品类,每个抽象产品类可以派生出多个具体产品类。

  • 一个抽象工厂类,可以派生出多个具体工厂类。

  • 每个具体工厂类可以创建多个具体产品类的实例。

总结

工厂方法模式只有一个抽象产品类,而抽象工厂模式有多个。

工厂方法模式的具体工厂类只能创建一个具体产品类的实例,而抽象工厂模式可以创建多个。

文章制作不易,如果本文对你有帮助的话就点个赞呗,多谢啦↓↓↓↓

展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 游动-白 设计师: 上身试试
应支付0元
点击重新获取
扫码支付

支付成功即可阅读