c#设计模式-工厂模式

引入人、工厂、和斧子的问题:
  (1),原始社会时,劳动社会基本没有分工,需要斧子的人(调用者)只好自己去磨一把斧子,每个人拥有自己的斧子,如果把大家的石斧改为铁斧,需要每个人都要学会磨铁斧的本领,工作效率极低。
对应Java里的情形是:java程序里的调用者new一个被调用者的实例。类耦合度极高,修改维护烦琐,效率极低。
  (2),工业社会时,工厂出现,斧子不再由普通人完成,而由工厂生产,当人们需要斧子的时候,可以到工厂购买斧子,无需关心斧子是怎么制造出来的,如果废弃铁斧为钢斧,只需改变工厂的制造工艺即可,制作工艺是工厂决定的,工厂生产什么斧子,工人们就得用什么斧子。
      对应的Java里的情形是:Java程序的调用者可以以来简单工厂创建被调用者,变化点被隔离到了简单工厂里,虽然耦合度降低,但是调用者会和工厂耦合,而且需要定位自己的工厂。
(3)近代工业社会,工厂蓬勃发展,人们需要什么斧子,只需要提供一个斧子图形,商家会按照你提供的图形将你的斧子订做好,送上门。

      工厂模式就相当于创建实例对象的new,我们经常要根据类Class生成实例对象,如A a=new A(). 工厂模式也是用来创建实例对象的,可能多做一些工作,但会给你系统带来更大的可扩展性和尽量少的修改量。

工厂方式封装:

Sample有个继承如MySample

public class Factory{  
  public static Sample creator(int which){  
     //getClass 产生Sample 一般可使用动态类装载装入类。  
if (which==1)  
    return new SampleA(); 

  else if (which==2) 

return new SampleB(); 
  } 

在程序中,如果要实例化Sample时.就使用

Sample sampleA=Factory.creator(1);

在列举个例子:

比如你写了个应用,里面用到了数据库的封装,你的应用可以今后需要在不同的数据库环境下运行,可能是oracle,db2,sql server等,那么连接数据库的代码是不一样的,你用传统的方法,就不得不进行代码修改来适应不同的环境,非常麻烦,但是如果你采用工厂类的话,将各种可能的数据库连接全部实现在工厂类里面,通过你配置文件的修改来达到连接的是不同的数据库,那么你今后做迁移的时候代码就不用进行修改了。

一、 抽象工厂(Abstract Factory)模式

抽象工厂模式是所有形态的工厂模式中最为抽象和最具一般性的一种形态。

为了方便引进抽象工厂模式,引进一个新概念:产品族(Product Family)。所谓产品族,是指位于不同产品等级结构,功能相关联的产品组成的家族。如图:

 

图中一共有四个产品族,分布于三个不同的产品等级结构中。只要指明一个产品所处的产品族以及它所属的等级结构,就可以唯一的确定这个产品。

引进抽象工厂模式

所谓的抽象工厂是指一个工厂等级结构可以创建出分属于不同产品等级结构的一个产品族中的所有对象。如果用图来描述的话,如下图:

 

二、 Abstract Factory模式的结构:

 

图中描述的东西用产品族描述如下:

 


抽象工厂(Abstract Factory)角色:担任这个角色的是工厂方法模式的核心,它是与应用系统商业逻辑无关的。

具体工厂(Concrete Factory)角色:这个角色直接在客户端的调用下创建产品的实例。这个角色含有选择合适的产品对象的逻辑,而这个逻辑是与应用系统的商业逻辑紧密相关的。

抽象产品(Abstract Product)角色:担任这个角色的类是工厂方法模式所创建的对象的父类,或它们共同拥有的接口。

具体产品(Concrete Product)角色:抽象工厂模式所创建的任何产品对象都是某一个具体产品类的实例。这是客户端最终需要的东西,其内部一定充满了应用系统的商业逻辑。


三、 程序举例:

该程序演示了抽象工厂的结构,本身不具有任何实际价值。

// Abstract Factory pattern -- Structural example  
using System;

// "AbstractFactory"
abstract class AbstractFactory
{
  // Methods
  abstract public AbstractProductA CreateProductA();
  abstract public AbstractProductB CreateProductB();
}

// "ConcreteFactory1"
class ConcreteFactory1 : AbstractFactory
{
  // Methods
  override public AbstractProductA CreateProductA()
  {
    return new ProductA1();
  }
  override public AbstractProductB CreateProductB()
  {
    return new ProductB1();
  }
}

// "ConcreteFactory2"
class ConcreteFactory2 : AbstractFactory
{
  // Methods
  override public AbstractProductA CreateProductA()
  {
    return new ProductA2();
  }

  override public AbstractProductB CreateProductB()
  {
    return new ProductB2();
  }
}

// "AbstractProductA"
abstract class AbstractProductA
{
}

// "AbstractProductB"
abstract class AbstractProductB
{
  // Methods
  abstract public void Interact( AbstractProductA a );
}

// "ProductA1"
class ProductA1 : AbstractProductA
{
}

// "ProductB1"
class ProductB1 : AbstractProductB
{
  // Methods
  override public void Interact( AbstractProductA a )
  {
    Console.WriteLine( this + " interacts with " + a );
  }
}

// "ProductA2"
class ProductA2 : AbstractProductA
{
}

// "ProductB2"
class ProductB2 : AbstractProductB
{
  // Methods
  override public void Interact( AbstractProductA a )
  {
    Console.WriteLine( this + " interacts with " + a );
  }
}

// "Client" - the interaction environment of the products
class Environment
{
  // Fields
  private AbstractProductA AbstractProductA;
  private AbstractProductB AbstractProductB;

  // Constructors
  public Environment( AbstractFactory factory )
  {
    AbstractProductB = factory.CreateProductB();
    AbstractProductA = factory.CreateProductA();
  }
 
  // Methods
  public void Run()
  {
    AbstractProductB.Interact( AbstractProductA );
  }
}

/// <summary>
/// ClientApp test environment
/// </summary>
class ClientApp
{
  public static void Main(string[] args)
  {
    AbstractFactory factory1 = new ConcreteFactory1();
    Environment e1 = new Environment( factory1 );
    e1.Run();

    AbstractFactory factory2 = new ConcreteFactory2();
    Environment e2 = new Environment( factory2 );
    e2.Run();
  }
}


四、 在什么情形下使用抽象工厂模式:

在以下情况下应当考虑使用抽象工厂模式:

  • 一个系统不应当依赖于产品类实例如何被创建、组合和表达的细节,这对于所有形态的工厂模式都是重要的。
  • 这个系统有多于一个的产品族,而系统只消费其中某一产品族。
  • 同属于同一个产品族的产品是在一起使用的,这一约束必须在系统的设计中体现出来。
  • 系统提供一个产品类的库,所有的产品以同样的接口出现,从而使客户端不依赖于实现。

五、 抽象工厂的起源

据说最早的应用是用来创建在不同操作系统的视窗环境下都能够运行的系统。比如在Windows与Unix系统下都有视窗环境的构件,在每一个操作系统中,都有一个视窗构件组成的构件家族。我们可以通过一个抽象角色给出功能描述,而由具体子类给出不同操作系统下的具体实现,如图:

 

可以发现上面产品类图有两个产品等级结构,分别是Button与Text;同时有两个产品族:Unix产品族与Windows产品族。

 

系统对产品对象的创建要求由一个工厂的等级结构满足。其中有两个具体工厂角色,即UnixFactory和WinFactory。UnixFactory对象负责创建Unix产品族中的产品,而WinFactory负责创建Windows产品族中的产品。

 

显然一个系统只能够在某一个操作系统的视窗环境下运行,而不能同时在不同的操作系统上运行。所以,系统实际上只能消费属于同一个产品族的产品。

在现代的应用中,抽象工厂模式的使用范围已经大大扩大了,不再要求系统只能消费某一个产品族了。


六、 Abstract Factory模式在实际系统中的实现

Herbivore:草食动物
Carnivore:食肉动物
Bison:['baisn],美洲或欧洲的野牛

下面实际代码演示了一个电脑游戏中创建不同动物的抽象工厂。尽管在不同大陆下动物物种是不一样的,但动物间的关系仍然保留了下来。

// Abstract Factory pattern -- Real World example  
using System;

// "AbstractFactory"
abstract class ContinentFactory
{
  // Methods
  abstract public Herbivore CreateHerbivore();
  abstract public Carnivore CreateCarnivore();
}

// "ConcreteFactory1"
class AfricaFactory : ContinentFactory
{
  // Methods
  override public Herbivore CreateHerbivore()
  { return new Wildebeest(); }

  override public Carnivore CreateCarnivore()
  { return new Lion(); }
}

// "ConcreteFactory2"
class AmericaFactory : ContinentFactory
{
  // Methods
  override public Herbivore CreateHerbivore()
  { return new Bison(); }

  override public Carnivore CreateCarnivore()
  { return new Wolf(); }
}

// "AbstractProductA"
abstract class Herbivore
{
}

// "AbstractProductB"
abstract class Carnivore
{
  // Methods
  abstract public void Eat( Herbivore h );
}

// "ProductA1"
class Wildebeest : Herbivore
{
}

// "ProductB1"
class Lion : Carnivore
{
  // Methods
  override public void Eat( Herbivore h )
  {
    // eat wildebeest
    Console.WriteLine( this + " eats " + h );
  }
}

// "ProductA2"
class Bison : Herbivore
{
}

// "ProductB2"
class Wolf : Carnivore
{
  // Methods
  override public void Eat( Herbivore h )
  {
    // Eat bison
    Console.WriteLine( this + " eats " + h );
  }
}

// "Client"
class AnimalWorld
{
  // Fields
  private Herbivore herbivore;
  private Carnivore carnivore;

  // Constructors
  public AnimalWorld( ContinentFactory factory )
  {
    carnivore = factory.CreateCarnivore();
    herbivore = factory.CreateHerbivore();
  }

  // Methods
  public void RunFoodChain()
  { carnivore.Eat(herbivore); }
}

/// <summary>
///  GameApp test class
/// </summary>
class GameApp
{
  public static void Main( string[] args )
  {
    // Create and run the Africa animal world
    ContinentFactory africa = new AfricaFactory();
    AnimalWorld world = new AnimalWorld( africa );
    world.RunFoodChain();

    // Create and run the America animal world
    ContinentFactory america = new AmericaFactory();
    world = new AnimalWorld( america );
    world.RunFoodChain();
  }
}

抽象工厂的另外一个例子:

如何设计抽象类工厂留作思考。


七、 "开放-封闭"原则

"开放-封闭"原则要求系统对扩展开放,对修改封闭。通过扩展达到增强其功能的目的。对于涉及到多个产品族与多个产品等级结构的系统,其功能增强包括两方面:

增加产品族:Abstract Factory很好的支持了"开放-封闭"原则。

增加新产品的等级结构:需要修改所有的工厂角色,没有很好支持"开放-封闭"原则。

综合起来,抽象工厂模式以一种倾斜的方式支持增加新的产品,它为新产品族的增加提供方便,而不能为新的产品等级结构的增加提供这样的方便。

 
分类: c#
标签: 设计模式
 
 
 

工厂方法模式简介:

工厂方法(Factory Method)模式的意义是定义一个创建产品对象的工厂接口,将实际创建工作推迟到子类当中。核心工厂类不再负责产品的创建,这样核心类成为一个抽象工厂角色,仅负责具体工厂子类必须实现的接口,这样进一步抽象化的好处是使得工厂方法模式可以使系统在不修改具体工厂角色的情况下引进新的产品。

过程方法模式结构:

抽象工厂角色:是工厂方法模式的核心,与应用程序无关。任何在模式中创建的对象的工厂类必须实现这个接口。
具体工厂角色:这是实现抽象工厂接口的具体工厂类,包含与应用程序密切相关的逻辑,并且受到应用程序调用以创建产品对象
抽象产品角色:工厂方法模式所创建的对象的超类型,也就是产品对象的共同父类或共同拥有的接口。在上图中,这个角色是Light。
具体产品角色:这个角色实现了抽象产品角色所定义的接口。某具体产品有专门的具体工厂创建,它们之间往往一一对应。

在上一篇博文【C#设计模式-简单工厂模式】中,我们使用简单工厂模式实现了:

如果有一个住户管理系统,里面的住户类型是可变的,每一种租户类型的租金计算公式都存在差异
例如:A类型的住户租金额=天数*单价+绩效*0.005;B类型的住户租金额=月份*(每月价格+performance*0.001)
这里我们虽然实现了客户的需求,但是如果客户后期需要增加了C类型商店和D类型商店,而它们的算法要求又不一样,
这个时候我们就需要进行C,D类型商店的创建,并继承Ishop接口,实现里面的方法,
同时还得继续修改工厂类在switc中增加case进行捕捉创建相应的商店对象,一旦出现这样的情况,是不利于程序的扩展性和项目后期的维护性的。
现在使用工厂方法模式可以很好的解决这一问题。不多说上代码。

1.分析:商店有共同的行为特征,都要进行店铺租金计算行为,我们抽象了Ishop ,里面有待实现的计算商店租金方法行为。

[csharp] view plain copy
 
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Linq;  
  4. using System.Text;  
  5.   
  6. namespace FactoryEntiy  
  7. {  
  8.     public interface Ishop  
  9.     {  
  10.         double Getrent(int days, double dayprice, double performance);  
  11.     }  
  12. }  

2.我们实现Isho接口里面的方法,创建A,B类型店铺。

[csharp] view plain copy
 
  1. using FactoryEntiy;  
  2. using System;  
  3. using System.Collections.Generic;  
  4. using System.Linq;  
  5. using System.Text;  
  6.   
  7. namespace ProductEnity  
  8. {  
  9.     /// <summary>  
  10.     /// 继承商店接口,实现里面的行为方法,即算法  
  11.     /// </summary>  
  12.     public class Ashop:Ishop  
  13.     {  
  14.         /// <summary>  
  15.         /// /// A类型商店租金额,天数*单价+绩效*0.005  
  16.         /// </summary>  
  17.         /// <param name="days">天数</param>  
  18.         /// <param name="dayprice">每天单价</param>  
  19.         /// <param name="performance">日平均绩效</param>  
  20.         /// <returns></returns>  
  21.         public double Getrent(int days, double dayprice, double performance)  
  22.         {  
  23.             Console.WriteLine("A商店的租金算法");  
  24.             return days * dayprice + performance * 0.01;  
  25.         }  
  26.     }  
  27. }  
[csharp] view plain copy
 
  1. using FactoryEntiy;  
  2. using System;  
  3. using System.Collections.Generic;  
  4. using System.Linq;  
  5. using System.Text;  
  6.   
  7. namespace ProductEnity  
  8. {  
  9.     /// <summary>  
  10.     /// 继承商店接口,实现里面的行为方法,即算法  
  11.     /// </summary>  
  12.     public class Bshop:Ishop  
  13.     {  
  14.         /// <summary>  
  15.         /// B类型商店的租金=月份*(每月价格+performance*0.001)  
  16.         /// </summary>  
  17.         /// <param name="month">月数</param>  
  18.         /// <param name="monthprice">月单价</param>  
  19.         /// <param name="performance">月平均绩效</param>  
  20.         /// <returns></returns>  
  21.         public double Getrent(int month, double monthprice, double performance)  
  22.         {  
  23.             Console.WriteLine("B商店的租金算法");  
  24.             return month * (monthprice + performance * 0.001);  
  25.         }  
  26.     }  
  27. }  

3.现在考虑在什么情况下创建商店的实体,对不同的商店进行租金计算,并且方便以后的增加和修改。

于是我们创建IFactroy接口,里面有待实现的创建商店对象的方法。

[csharp] view plain copy
 
  1. using FactoryEntiy;  
  2. using System;  
  3. using System.Collections.Generic;  
  4. using System.Linq;  
  5. using System.Text;  
  6.   
  7. namespace FactoryMethod  
  8. {  
  9.     /// <summary>  
  10.     /// 工厂类,创建商店类型实体  
  11.     /// </summary>  
  12.     public interface IFactory  
  13.     {  
  14.         Ishop CreateShop();  
  15.     }  
  16. }  
4.现在我们就可以继承自IFactory,实现里面创建对应的商店对象了。
[csharp] view plain copy
 
  1. using FactoryEntiy;  
  2. using FactoryMethod;  
  3. using ProductEnity;  
  4. using System;  
  5. using System.Collections.Generic;  
  6. using System.Linq;  
  7. using System.Text;  
  8.   
  9. namespace ProductEnity  
  10. {  
  11.     /// <summary>  
  12.     /// 继承工厂类,创建A类型商店实体  
  13.     /// </summary>  
  14.     public class CreateBshop : IFactory  
  15.     {  
  16.         public Ishop CreateShop()  
  17.         {  
  18.             return new Ashop();  
  19.         }  
  20.     }  
  21. }  
  22.   
  23. using FactoryEntiy;  
  24. using FactoryMethod;  
  25. using ProductEnity;  
  26. using System;  
  27. using System.Collections.Generic;  
  28. using System.Linq;  
  29. using System.Text;  
  30.   
  31. namespace ProductEnity  
  32. {  
  33.     /// <summary>  
  34.     /// 继承工厂类,创建B类型商店实体  
  35.     /// </summary>  
  36.     public class CreateAshop : IFactory  
  37.     {  
  38.         public Ishop CreateShop()  
  39.         {  
  40.             return new Bshop();  
  41.         }  
  42.     }  
  43. }  

5.之后根据当前的商店类型进行判断,该类型的商店应该进行哪一种算法:



[csharp] view plain copy
 
  1. using FactoryEntiy;  
  2. using System;  
  3. using System.Collections.Generic;  
  4. using System.Configuration;  
  5. using System.Linq;  
  6. using System.Reflection;  
  7. using System.Text;  
  8.   
  9. namespace FactoryMethod.App  
  10. {  
  11.     class Program  
  12.     {  
  13.         static void Main(string[] args)  
  14.         {  
  15.             string shopname = ConfigurationManager.AppSettings["CreateShopClassName"];  
  16.             //shopname为创建商店类名称,此处=CreateAshop  
  17.             IFactory af = (IFactory)Assembly.Load("ProductEnity").CreateInstance("ProductEnity." + shopname);  
  18.             //第一个ProductEnity是dll的名称,第二个ProductEnity是项目的命名空间。  
  19.             Ishop As = af.CreateShop(); double total = As.Getrent(30, 300, 2000); //30 天/100元 日平均绩效为2000   
  20.             Console.WriteLine("该A类型商店的租金为:" + total);   
  21.               
  22.             Console.WriteLine("=============");  
  23.             IFactory bf = (IFactory)Assembly.Load("ProductEnity").CreateInstance("ProductEnity." + "CreateBshop");  
  24.             //CreateBshop可以保存为配置或者存在数据库中,  
  25.             //注意该保存字符串应该与项目中创建的类名一样,  
  26.             //否则反射的方式会找不到该项目下的类。  
  27.             Ishop Bs = bf.CreateShop(); total = Bs.Getrent(30, 300, 2000); //30 天/100元 日平均绩效为2000  
  28.             Console.WriteLine("该A类型商店的租金为:" + total);  
  29.         }  
  30.     }  
  31. }  

这里我们使用反射的方式创建对象,替换了之前的工厂类通过switch创建对象的方式,有利于后面的新类型商店增加和算法修改增加和维护

以后在项目需求在有变革,我们只需要重新在项目中增加C,D类型商店,继承Ishop实现里面的方法,同时,添加继承IFactroy接口,创建对应的商店对象编译该项目的

ProductEnity.dll,以后再进行计算该C,D类型的商店算法就可以通过反射的方式进行计算,不需要修改原来的工程类代码。

整个项目的结构图如下:

原文地址:https://www.cnblogs.com/kdp0213/p/8547658.html