43

读书笔记-设计模式-可复用版-简单工厂VS工厂方法-腾讯游戏学院

 5 years ago
source link: http://gad.qq.com/article/detail/289414
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

在设计模式可复用版中,并没有提到简单工厂,我是在大话设计模式中看到这个说法


实际上,简单工厂在工厂方法(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.


再贴下结构图:


5c81dff4e7447.png

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的使用,然后会对创建型的五种设计模式,做一次总结。


就到这里,周末快到了,周末愉快!


感谢您的阅读, 如文中有误,欢迎指正,共同提高 


欢迎关注我的技术分享的微信公众号,Paddtoning帕丁顿熊,期待和您的交流


5c18e453e7d86.jpg


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK