在设计模式可复用版中,并没有提到简单工厂,我是在大话设计模式中看到这个说法
实际上,简单工厂在工厂方法(Factory Method)一章节中,叫做参数化的工厂方法,接受一个标识对象种类的参数,使得工厂方法可以创建多种产品。
“工厂“即是用于"生产”产品的,简单工厂是通过一个统一的接口,通常是静态的,接受一个对象种类的参数,通过if else或是switch case 条件判断,返回不同种类的对象。
工厂方法(Factory Method) 概念:
定义一个用于创建对象的接口,让子类决定,实例化哪一个类。Factory Method 使一个类的实例化,延迟到子类。
在大话设计模式中,通过采用计算器,+、-、*、/等不同的运算,来应用工厂方法,也是不错的例子,我这里稍后会采用一个"货币兑换”来进行阐述工厂方法(Factory Method)的使用,但先还原一下运算器的例子吧
public class Operation
{
public float num1 { get; set; }
public float num2 { get; set; }
public virtual float GetResult() { return 0; }
}
public class Add : Operation
{
public override float GetResult()
{
return num1 + num2;
}
}
public class Sub : Operation
{
public override float GetResult()
{
return num1 - num2;
}
}
public class Mul : Operation
{
public override float GetResult()
{
return num1 * num2;
}
}
public class Div : Operation
{
public override float GetResult()
{
if (num2 == 0)
{
Debug.LogError("num2 is zero!");
return 0;
}
return num1 / num2;
}
}
public class OperationFactory
{
public static Operation CreateOperation(string type)
{
Operation operation = null;
switch (type)
{
case "+":
operation = new Add();
break;
case "-":
operation = new Sub();
break;
case "*":
operation = new Mul();
break;
case "/":
operation = new Div();
break;
}
return operation;
}
}
测试代码:
Operation operation = OperationFactory.CreateOperation("*");
operation.num1 = 10;
operation.num2 = 10;
Debug.Log(operation.GetResult());
所有的运算符派生成基类Operation(Product) ,并通过OperationFactory参数化工厂方法,根据type,返回指定的运算符对象。
但上面的代码有个弊端,如果我新增一个运算符,我就需要修改一次OperationFactory,
加一个case或是if else,这样也可能会影响其它代码,但你新增一个二手手机号码拍卖运算符或是对某一个运算符做修改,影响到其它已经稳定在使用中的运算符,显然是不合理的,这其实违背了开放-封闭原则,设计模式一共有6大原则,这会在后面着重介绍
所以,我们需要解决这种情况,基于接口的的工厂方法可以消除switch case 或if else,从而
新增或是修改运算符,不影响其它部分。
添加一个IOperation接口,接口只提供了一个CreateOperation工厂方法,用于创建运算符:
public interface IOperation{
Operation CreateOperation();
}
并分别新建四个运算符类,实现IOperation接口:
public class AddOperation : IOperation
{
public Operation CreateOperation()
{
return new Add();
}
}
public class SubOperation : IOperation
{
public Operation CreateOperation()
{
return new Sub();
}
}
public class MulOperation : IOperation
{
public Operation CreateOperation()
{
return new Mul();
}
}
public class DivOperation : IOperation
{
public Operation CreateOperation()
{
return new Div();
}
}
测试代码:
IOperation operation = new AddOperation();
Operation op = operation.CreateOperation();
op.num1 = 10;
op.num2 = 5;
Debug.Log(op.GetResult());
通过修改,已经消除了switch case / if else带来的弊端,我新增一个运算符,或是修改已有的运算符,对其它运算符都不会有影响,而且也不需要单独的构建一个OperationFactory类,结构要比参数化工厂方法更好
但工厂方法也是有缺点的,比如我新增一个运算后,我就需要新增一个实现IOperation接口的工厂类,用于创建具体的产品(Product),但我个人并不认为这是什么大的缺点,有必要的时候是一定要添加的,但要解决也是有办法的
我们看上面实现了IOperation接口的这些工厂类:
AddOperation
SubOperation
MulOperation
DivOperation
除了名字,所有的实现都是相同的,这里就可以使用泛型或模板,来减少重复的代码,提高代码的重用性
只需要定义一个泛型类:
public class OperationGeneric<T> : IOperation where T : Operation, new()
{
public Operation CreateOperation()
{
return new T();
}
}
测试代码:
IOperation operation = new OperationGeneric<Mul>();
Operation op = operation.CreateOperation();
op.num1 = 10;
op.num2 = 5;
Debug.Log(op.GetResult());
IOperation operation = new OperationGeneric<Mul>();
需要什么运算符,只需要在<>中指定即中。
这样,上面新增的:
AddOperation
SubOperation
MulOperation
DivOperation
就全部可以删除掉了,新增的运算符,也不需要再新增一个实现IOperation的工厂类了。
使用模板,可以避免创建子类。
还有一点需要提到,之前在Singleton章节讲到过,Lazy Initalization 懒汉式或是延迟初始化,只有在使用的时候,如果不存在,我才会去创建
工厂方法,目前就是采用的Lazy Initalization.
再贴下结构图:
Product 产品的基类,我们通常要派生实现,因为尽量的避免面向具体的类(ConcreteClass)编程,要面向抽象编程。
ConcreteProduct 派生自Product
在上面的例子中,Operation是Product,是基类
Add,Sub,Mul,Div分别派生自Operation基类,他们是ConcreteProduct
Creator 是工厂方法的接口
ConcreteCreator是实现了Creator接口的具体工厂类,创建不同运算符的类。
AddOperation,SubOperation...这些(但我们通过泛型来避免创建更多的子类,不要忘记这一点)
在抽象工厂Abstract Factory中,经常用工厂方法Factory Method来实现,下一章,我们会来介绍创建型的最后一个设计模式,抽象工厂Abstract Factory.
最后,提供货币兑换的例子,以巩固工厂方法(Factory Method)的练习 :
假设,我现在有100块钱人民币,我想要兑换成美元,英镑,欧元和卢布,如何通过工厂方法来实现?
这里的美元,英镑,欧元和卢布,就是我们Product,我们先需要定义一个基类Product,这是为了避免向面具体的产品(ConcreteProduct)进行编程,将公共的接口抽象出来。然后创建相应的子类派生实现它。
public class Exchange
{
public float amount{get;set;}//现金数
public virtual float Translation() { return 0; }//返回兑换数
}
public class USDExchange : Exchange
{
public override float Translation()
{
return amount * 0.1490f;
}
}
public class GBPExchange : Exchange
{
public override float Translation()
{
return amount * 0.1131f;
}
}
public class EURExchange : Exchange
{
public override float Translation()
{
return amount * 0.1318f;
}
}
public class RUBExchange : Exchange
{
public override float Translation()
{
return amount * 9.8227f;
}
}
下面定义工厂方法的接口:
public interface IExchange
{
Exchange CreateExchange();
}
为了避免创建子类,我们使用泛型:
public class ExchangeGeneric<T> : IExchange where T : Exchange, new()
{
public Exchange CreateExchange()
{
return new T();
}
}
测试代码:
IExchange exchangeFactory = new ExchangeGeneric<RUBExchange>();
Exchange exchange = exchangeFactory.CreateExchange();
exchange.amount = 100;
Debug.Log("amount:"+exchange.Translation());
输出结果:
amount:982.27
(我对俄罗斯是有情怀的:)
工厂方法就介绍到这里,接下来会介绍抽象工厂Abstract Factory的使用,然后会对创建型的五种设计模式,做一次总结。