设计模式 - 工厂模式
工厂模式是创建型模式之一,其主要功能都是帮助我们把对象的实例化部分分离出来,降低系统中代码耦合度,增强了系统的扩展性,并将对象的创建过程延迟到子类进行
工厂模式
分类:
- 简单工厂模式(Simple Factory):只有一个工厂,通过向工厂传参来选择工厂所要生产的产品
- 工厂方法模式(Factory Method):允许多个工厂,但一个工厂只生产一种产品,通过不同的工厂类型来选择所要生产的产品
- 抽象工厂模式(Abstract Factory):允许多个工厂,一个工厂允许生产多种产品,通过工厂实例的不同方法来选择所要生产的产品
Github源码:
简单工厂模式
含义:只有一个工厂类,客户端向工厂传递所需产品名,工厂生产产品交予客户
实现过程:
- 理论性:创建产品的抽象类做为产品基类,产品类(派生类)继承该基类并实现各自方法。工厂类 负责接收客户端传入的参数(产品名),根据参数选择对应产品类进行实例化,最终返回给客户端
- 比喻:客户去印刷厂印刷书籍,客户只需要告诉印刷厂其需求(书名、材质 ..),印刷厂根据该需求选择印刷对应的书籍,最终交予客户
优缺点:
- 优点:
- 客户端无须知道所创建的具体产品类的创建细节,只需将产品名参数传递到工厂类即可实现创建并使用
- 添加新类即可实现新产品的类,无需对客户端进行大改动
- 缺点:
- 随着产品的增加,对应产品类数量增加,增加了系统复杂度
- 新增加产品类都会修改工厂类,违背 “开闭原则”
- 由于工厂类集中了所有产品创建逻辑,一旦不能正常工作,整个系统都要受到影响
项目示例:
简单工厂模式实现加减乘除运算,工厂类OperationFactory 根据传入的运算符字符("+"、"-" ..),自动选择实例化的运算类并返回,实现计算:
// 运算基类(抽象产品类):
// --------------------
public class Operation
{
public double NumberA { get; set; }
public double NumberB { get; set; }
public virtual double GetResult()
{
double result = 0;
return result;
}
}
// 运算符派生类(具体产品类):
// ------------------------
class OperationAdd : Operation
{
public override double GetResult()
{
double result = 0;
result = NumberA + NumberB;
return result;
}
}
class OperationSub : Operation
{
public override double GetResult()
{
double result = 0;
result = NumberA - NumberB;
return result;
}
}
class OperationMul : Operation
{
public override double GetResult()
{
double result = 0;
result = NumberA * NumberB;
return result;
}
}
class OperationDiv : Operation
{
public override double GetResult()
{
double result = 0;
if (NumberB == 0)
throw new Exception("除数不可为 0");
result = NumberA / NumberB;
return result;
}
}
// 工厂类:
// ------
public class OperationFactory
{
public static Operation CreateOperate(string operate)
{
//根据传入的参数operate选择实例化的类
Operation oper = null;
switch (operate)
{
case "+":
oper = new OperationAdd();
break;
case "-":
oper = new OperationSub();
break;
case "*":
oper = new OperationMul();
break;
case "/":
oper = new OperationDiv();
break;
}
return oper;
}
}
// 客户端:
// ------
class Test
{
static void Main(string[] args)
{
//创建运算基类的对象,传入字符串到工厂类,返回对应实类的实例对象
Operation oper;
oper = OperationFactory.CreateOperate("+");
oper.NumberA = 4; oper.NumberB = 2;
double result = oper.GetResult();
Console.WriteLine(result); // 6
oper = OperationFactory.CreateOperate("-");
oper.NumberA = 4; oper.NumberB = 2;
result = oper.GetResult();
Console.WriteLine(result); // 2
oper = OperationFactory.CreateOperate("/");
oper.NumberA = 4; oper.NumberB = 2;
result = oper.GetResult();
Console.WriteLine(result); // 2
}
}
UML图
工厂方法模式
含义:允许多个工厂,但一个工厂只生产一种产品,通过不同的工厂类型来选择所要生产的产品。也可以说:定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,即使其创建过程延迟到子类进行
实现过程:
- 理论性:创建抽象工厂接口,创建各类产品的工厂类继承该接口并实现各自方法。客户端选择所需产品的工厂,对应工厂实现生产并返还
- 比喻:客户去印刷全彩书刊,需要找到对应的全彩书刊印刷厂,该工厂拥有印刷该类书籍的方法,因此可执行印刷功能并将产品交予客户
优缺点:
- 优点:
- 用户只需关心产品对应工厂而无需知道创建细节、产品名
- 添加新产品只需添加具体工厂类、产品类即可,可拓展性好,符合 “开放封闭原则”
- 由于一个工厂只生产一种产品,因此也符合“单一职责原则”
- 缺点:
- 随着产品的增加,对应工厂类、产品类数量增加,增加了系统复杂度
项目示例:
对上节简单工厂方法的项目示例做修改,实现工厂方法模式:抽象产品类、各运算类保持不变,添加抽象工厂接口,并由该接口派生出各类计算方法的工厂类,客户端通过工厂类即可实现计算功能
#region 上述的运算基类(抽象产品类) + 运算符派生类(具体产品类)
//...
#endregion
// 抽象工厂接口:
// -----------
interface IFactory
{
Operation CreateOperation();
}
// 各运算工厂类:
// -----------
// 加法工厂
class AddFactory : IFactory
{
public Operation CreateOperation()
{
return new OperationAdd();
}
}
// 减法工厂
class SubFactory : IFactory
{
public Operation CreateOperation()
{
return new OperationSub();
}
}
// 乘法工厂
class MulFactory : IFactory
{
public Operation CreateOperation()
{
return new OperationMul();
}
}
// 除法工厂
class DivFactory : IFactory
{
public Operation CreateOperation()
{
return new OperationDiv();
}
}
// 客户端:
// ------
class Test
{
static void Main(string[] args)
{
//用户选择产品对应的工厂进行生产
IFactory operFactory = new AddFactory();
Operation oper = operFactory.CreateOperation();
oper.NumberA = 4; oper.NumberB = 2;
double result = oper.GetResult(); //6
}
}
UML图
抽象工厂模式
含义:允许多个工厂,一个工厂允许生产多种产品,通过工厂实例的不同方法来选择所要生产的产品。也可以说:提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类
实现过程:
- 理论性:抽象产品接口->具体产品类,抽象工厂接口->具体工厂类,客户端创建工厂、选择生产产品
- 比喻:显示器(抽象产品接口)的产品有苹果显示器、戴尔显示器(具体产品类),主机的产品也有苹果和戴尔;生产电脑设备的工厂(抽象工厂接口)有苹果工厂和戴尔工厂(具体工厂类),客户可以选择苹果或者戴尔电脑工厂,再选择要生产该品牌的显示器或是主机
扩展:
- 产品族:具有相同属性的同类型产品,如戴尔显示器、戴尔主机都属于戴尔
- 产品等级结构:产品的继承结构,如产品抽象类(显示器),其具体产品类有戴尔显示器、苹果显示器,抽象类与产品类之间构成了产品等级结构
优缺点:
- 优点:
- 将一个系列的产品族(戴尔的显示器、主机..)统一到对应工厂进行创建,便于客户端直接使用同一产品族的工厂进行生产操作
- 较比与工厂方法模式,减少了工厂类和具体产品的类添加
- 缺点:
- 产品族扩展困难(例如添加苹果ipad,则需要对产品的抽象、具体类修改,工厂的抽象、具体类修改)
项目示例:
采用抽象工厂模式实现苹果、戴尔电脑的生产工厂:创建两个抽象产品接口(显示器、主机),根据该接口实现具体产品类(苹果显示器、苹果主机、戴尔..);创建抽象工厂接口(电脑工厂),根据该接口实现具体工厂类(苹果电脑工厂、戴尔电脑工厂);最终在客户端进行工厂创建、生产产品
// 抽象产品类:
// ----------
// 抽象产品 - 显示器
interface IDisplay
{
void Show();
}
// 抽象产品 - 主机
interface IMainFrame
{
void Open();
}
// 具体产品类:
// ----------
// 具体产品 - 苹果显示器
class AppleDisplay : IDisplay
{
public void Show()
{
Console.WriteLine("苹果显示器正常工作!");
}
}
//具体产品 - 戴尔显示器
class DellDisplay : IDisplay
{
public void Show()
{
Console.WriteLine("戴尔显示器正常工作!");
}
}
// 具体产品 - 苹果主机
class AppleMainFrame : IMainFrame
{
public void Open()
{
Console.WriteLine("苹果主机启动成功!");
}
}
// 具体产品 - 戴尔主机
class DellMainFrame : IMainFrame
{
public void Open()
{
Console.WriteLine("戴尔主机启动成功!");
}
}
// 抽象工厂类:
// ----------
// 抽象电脑工厂
interface IComputerFactory
{
// 生产一个显示器
IDisplay ProduceADisplay();
// 生产一个主机
IMainFrame ProduceAMainFrame();
}
// 具体工厂类:
// ----------
// 具体工厂 - 苹果电脑工厂
class AppleComputerFactory:IComputerFactory
{
public IDisplay ProduceADisplay()
{
return new AppleDisplay();
}
public IMainFrame ProduceAMainFrame()
{
return new AppleMainFrame();
}
}
// 具体工厂 - 戴尔电脑工厂
class DellComputerFactory : IComputerFactory
{
public IDisplay ProduceADisplay()
{
return new DellDisplay();
}
public IMainFrame ProduceAMainFrame()
{
return new DellMainFrame();
}
}
// 客户端:
// ------
class Client
{
static void Main()
{
//创建苹果电脑工厂
IComputerFactory MyAppleFactory = new AppleComputerFactory();
//生产苹果显示器
IDisplay AppleDisplay = MyAppleFactory.ProduceADisplay();
AppleDisplay.Show(); //苹果显示器正常工作!
//创建戴尔电脑工厂
IComputerFactory MyDellFactory = new DellComputerFactory();
//生产戴尔主机
IMainFrame DellMainFrame = MyDellFactory.ProduceAMainFrame();
DellMainFrame.Open(); //戴尔主机启动成功!
}
}