一. 抽象工厂&工厂方法&简单工厂方法

Posted Tiigoo

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一. 抽象工厂&工厂方法&简单工厂方法相关的知识,希望对你有一定的参考价值。

1. 抽象工厂模式

参考《大话设计模式》第十五章

1.1 问题

  用户可能用两种类型的数据库,这两种数据库有类似的方法,但如果一开始声明对象时,固定了它使用 SQL Server 数据库:SqlserverUser su=new SqlserverUser(),那么要是后面需要改成Access数据库,它调用的各种方法都需要改变,需要修改的代码量很大。比如添加用户操作:su.Insert(User),两种类型的数据库都是执行这个操作,但具体实现的代码不同,这样需要全部改变。
​  此问题的特点:有多种模式可以用,但不同模式又有相似操作。虽然不同模式实现相似操作的具体代码不同,但对用户来说达到的效果是一样的。

1.2 UML图和代码

  • 定义:抽象工厂模式——提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类

  • UML图
    对上述问题的解决方法:
      对用户来说:他只看得到抽象接口IUser,不用知道具体实现类(SqlserverUser和AccessUser)。这意味着他知道有哪些操作(IUer告诉他的),但不用考虑是在Sqlserver还是Access的环境中使用操作。那么当要从Sqlserver数据库改到Access数据库时,只要改变传给工厂的指令,但用户端的操作代码都不用变(操作是固定的,IUser中定义了固定的操作)。
      对工厂来说:首先告诉工厂是在哪个环境,工厂根据指定的环境创造具体工厂(不同的具体工厂会创造不同的操作组合)。然后具体工厂将这些操作组合在一起,返回给用户。这样用户不用去挑选环境对应的各个操作,直接从工厂那里拿到一整套的操作

  • 代码

  • IUser和IDocument

    interface IUser//定义了关于User的操作
    {
    	void Insert(User user);
    	User GetUser(int id);
    }
    
    interface IDepartment//定义了关于department的操作
    {
        void Insert(Department department);
    	Department GetDepartment(int id);
    }
    
  • IUser和IDepartment的具体实现类:

      //SqlserverUser,用于访问 SQL Server 的 User
      class SqlserverUser implements Iuser
      {
          public void Insert(User user)
          {
              ...
          }
          public void GetUser(int id)
          {
              ...
          }
      }
      //AccessUser,用于访问Access 的 User
      class AccessUser implements Iuser
      {
          public void Insert(User user)
          {
              ...
          }
          public void GetUser(int id)
          {
              ...
          }
          
      }
      //SqlserverDepartment,用于访问 SQL Server 的 Department
      class SqIserverDepartment implements IDepartment
      {
          ...
      }
      //AccessDepartment,用于访问Access 的 Department
      class AccessrDepartment implements IDepartment
      {
          ...
      }
    
  • IFactory接口

    interface IFactory
    {
    	IUser CreateUser();
    }
    
  • IFactory的具体实现类

    //SqlServerFactory类
    
    class SqlServerFactory implements IFactory
    {
    	public IUser CreateUser()
    	{
    		return new SqlserverUser();
    	}
        public IDocument CreateDocument()
    	{
    		return new SqlserverDocument();
    	}
    }
    
    //AccessFactory类
    
    class AccessFactory implements IFactory
    {
    	public IUser CreateUser()
    	{
    		return new AccessUser();
    	}
        public IDocument CreateDocument()
    	{
    		return new AccessDcocument();
    	}
    }
    
    
  • 客户端代码

static void Main(string[] args)
{
	IFactory factory=new SqlServerFactory();//选择创造具体产品组合的具体工厂
	IUser iu = factory.CreateUser();//通过具体子类创造产品
	IDocument idoc = factory.CreateDocument();
	
    User user=new User();
    Document document=new Document();
	iu.Insert(user);
	iu.GetUser(1);
	idoc.Insert(document);
	idoc.GetDocument(1);
}
  • 结构图

1.3 优点和缺点

  • 优点

    1. 易于交换产品系列。由于选择具体工厂类(如 IFactory factory=new AccessFactory() )只需要在初始化的时候出现—次,这就使得改变—个应用的操作环境很容易。只用改变这一条指令,指定不同的具体工厂就可以改变操作环境。

    2. 让具体的创建实例过程与客户端分离。客户端只知道有抽象类 IUser 和IDepartment,抽象类告诉用户有哪些操作,而具体产品类的代码不会出现在客户端代码中。

  • 缺点:

    1. 如果后期要增加产品——项目表Proiect(Project和User、Department同级)需要做以下改动:

      (1)增加抽象接口IProject,以及增加IProject的具体实现类 SqlserverProject、AccessProject

      (2)还需要更改抽象工厂接口IFactory,以及更改具体工厂SqlserverFactory和 AccessFactory
        这也比较繁琐,这个问题可以用简单工厂解决,将抽象工厂和具体工厂统一成一个工厂DateAccess(如下图),这样就只用增加(1),以及在DataAccess里增加一个方法:CreateProject()。简单工厂在后文详述。

    2. 如果客户端程序有很多,每个客户端都有IFactory factory=new SqlserverFactory,当要换成Access数据库时,每个客户端都需要改变:IFactory factory=new AccessFactory()。这并没有实现我们的需求:只改一处,而所有的客户端都能变。但是这个问题可以通过反射解决。

1.4 反射+抽象工厂

还没仔细了解反射,这部分请跳过,复习javase后补充。

常规抽象工厂的写法
:IUser result = new SqlserverUser();

反射的写法:using System.Reflection;先引用System.Reflection 的命名空间
IUser result=(IUser)Assembly.Load(“抽 象 工厂模式”).CreateInstance(“抽 象 工.厂模式. SqlserverUser”);
当前"命名空间"名称要实例化的"类名"
当前"程序集"的名称

  • 反射+配置文件

2. 简单工厂模式

参考:《大话设计模式》第一章

2.1 问题

  从固定的印刷到活字印刷体现了面向对象的好处:通过封装、继承、多态把程序的耦合性降低

  第一,要改印刷模板,只需更改对应的字,而不是重新印刷整个产品,此为可维护

  第二,印刷用到的模板字并非用完这次就无用,完全可以在后来的印刷中重复使用,此乃可复用

  第三,印刷模板若要加字,只需另刻字加入即可,这是可扩展

  第四字的排列其实可能是睡排可能是横排,此时只需将活字移动就可,做到满足排列需求,此是灵活性好

  此章面对的问题:计算器实现+-*/,这几个运算有相似和不同的部分,如果没有分离开,当我们要增加一个运算,或者给这几个类都增加一个功能,改动很大。所以采用封装实现低耦合,具体见下。

2.2 业务的封装

  封装:就是让业务逻辑与界面逻辑分开,让它们之间的耦合度下降。业务逻辑是工厂和运算类来完成(比如工厂可以选择实现哪个具体运算,运算类可以进行具体操作),界面逻辑是客户端代码完成。只有分离开,才可以达到容易维护或扩展。

  “+”、“-”、“*”、"/"算法也要分开,用一个抽象类(下面UML图中的“运算类”)来实现抽象步骤,而具体类(下面的加法类、乘法类…)来选择具体用哪个算法。

  到底要实例化谁,将来会不会增加实例化的对象,比如增加开根运算——这是很容易变化的地方,应该考虑用一个単独的类(简单工厂类)来做这个创造实例的过程,这就是工厂。

  这里工厂不是抽象的,它通过输入创造了类。

2.3 UML图和代码

  • 简单工厂类代码
 public class SimpleFacory//简单工厂类
 {
 	public static Operation createOprate(String Operate)
 	{
 		Operation opr=null;
 		switch(opr)
 		{
 			case "+":
 				opr=new OperationAdd();
 				break;
 			case "-":
 				opr=new OperationSub();
 				break;
 			case "*":
 				opr=new OperationMul();
 				break;
 			case "/":
 				opr=new OperationDiv();
 				break;
 		}
 		return opr;
 	}
 }
  • 客户端代码:
Operation opr;
//例如想进行加法运算
opr=SimpleFactory.createOperate("+");//利用了多态性
//下面的属性和方法是抽象类固定的,具体选择哪个子类是由上面SimpleFactory决定的。
opr.NumberA=1;
opr.NumberB=2;
double result=opr.GetResult();

3.工厂方法模式

参考《大话设计模式》第八章

3.1 定义

  工厂方法模式是定义一个用于创建对象的接口(IFactory),让子类(这里指的是工厂接口的子类,即具体工厂)决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。

3.2 UML图和代码

  和 3.简单工厂模式 面对的问题一样,即对+ - * / 类的分离和封装。但这里,具体选择哪个类不是在工厂内决定,而是用户端决定。即switch case不是在工厂内部了,而是在客户端代码,这样更能体现封闭性,后面会具体分析。

  • 抽象工厂代码:

    interface IFactory//抽象工厂是一个接口,需要子类实现接口,而不能直接调用方法
    {
    	Operation CreateOperation();//所有具体工厂都有这个方法。
    }
    
  • 具体工厂的代码:

    class AddFactory implements IFactory
    {
    	public Operation CrateOperation()
    	{
    		return new OperationAdd();//OperationAdd是Operation的子类,利用了多态性
    	}
    }
    
    class SubFactory implements IFactory
    {
    	public Operation CrateOperation()
    	{
    		return new OperationAdd();//OperationSub是Operation的子类,利用了多态性
    	}
    }
    
    class MulFactory implements IFactory
    {
    	public Operation CrateOperation()
    	{
    		return new OperationAdd();//OperationMul是Operation的子类,利用了多态性
    	}
    }
    
    
    class DivFactory implements IFactory
    {
    	public Operation CrateOperation()
    	{
    		return new OperationAdd();//OperationDiv是Operation的子类,利用了多态性
    	}
    }
    
  • 客户端代码:

    IFactory oprFactory=new AddFactory();//例如想要实现加法类,这里是在客户端选择具体工厂,也可以用case,根据一个变量来选择具体工厂
    Operation opr=oprFactory.CreateOperation();//这里得到的就是加法具体类了
    opr.NumberA=1;
    opr.NumberB=2;
    double result=opr.GetResult();
    

3.3 工厂方法和简单工厂的比较

  • 工厂方法的好处

   对比二者的UML图,发现工厂方法多出来的是:四个具体工厂的分支去和具体运算类耦合,而简单工厂是一个工厂直接和四个具体运算类耦合。那么工厂方法的好处是什么呢?

   考虑我们要增加一个运算:求 M N M^N MN

   在简单工厂类中,我们需要改变简单工厂:如图,在它 createOprate()方法内部增加一个case "^",这样才可以选择乘方运算。但这种对类的改变会破坏封闭原则,我们希望封装好的类不再做改变。

   在工厂方法中,我们只用再增加一个具体工厂:

class PowerFactory implements IFactory
{
	public Operation CrateOperation()
	{
		return new OperationPower();
	}
}

   如图:

   这样既扩展了抽象工厂,又没有改变抽象工厂内部代码。其实这本质上是把switch case交给了客户端代码,这样就会增加客户端代码的改动。

  • 简单工厂的好处
       简单工厂好处是对于客户端更友好。如上所说,简单工厂的客户端只需要传递一个字符给工厂,而switch case(选择实例化哪个具体运算类)是工厂来做的。所以,对于客户端来说,去除了与具体产品的依赖,客户不用做逻辑判断。客户不用知道有哪些具体工厂,他只知道“+”这个字符。
       我们可以对比一下二者客户端的代码:
 //简单工厂:只用给简单工厂(SimpleFactory)一个字符“+”,就能得到想要的具体加法类。
Operation opr;
opr=SimpleFactory.createOperate("+");//给简单工厂一个字符“+”,返回了具体的加法类
opr.NumberA=1;//然后利用加法类得到结果
opr.NumberB=2;
double result=opr.GetResult();

//工厂方法:客户端需要选择具体工厂AddFactory,
//			如果想要选择不同的具体工厂,可以加上switch case。
//			所以这里是把逻辑判断(选择哪个工厂)交给了用户,
//			而简单工厂中是SimpleFactory来进行逻辑判断的
  IFactory oprFactory=new AddFactory();//客户端选择具体工厂
  Operation opr=oprFactory.CreateOperation();//再利用具体工厂得到想要的加法类
  opr.NumberA=1;//然后利用加法类得到结果
  opr.NumberB=2;
  double result=opr.GetResult()
  • 二者共同的好处:
        二者都保持了封装对象创建过程的优点:易维护,易扩展,可复用,灵活性(相对于不用对象封装)
       只是工厂方法更加体现封闭原则,更易扩展

3.4 工厂方法和抽象工厂的比较

  • 结构图对比

    抽象工厂:

    工厂方法:

      可见二者都有抽象工厂、具体工厂,抽象产品、具体产品,并且是具体工厂和具体产品耦合。
      但是工厂方法中只有一个抽象产品,每个具体工厂和一个抽象产品的具体产品耦合;
      抽象工厂有多个抽象产品,每个具体工厂和多个抽象产品的具体产品耦合,并且将不同具体产品进行组合。抽象工厂是工厂方法的扩展。

以上是关于一. 抽象工厂&工厂方法&简单工厂方法的主要内容,如果未能解决你的问题,请参考以下文章

一抽象工厂模式&&简单工厂+反射改进抽象工厂

简单工厂模式&工厂方法模式&抽象工厂模式

工厂模式&抽象工厂——HeadFirst设计模式学习笔记

工厂模式

浅谈简单工厂,工厂方法,抽象工厂的区别和使用

工厂模式:简单工厂工厂方法抽象工厂