简单工厂模式引子(从重构来理解)

思考:New存在问题么?
Sting str=new String() 我们平常都是这样用,怎么会有问题呢?

那我们再来看:
类库---
class Road
{
        
//
}
客户程序---
Road road 
= new Road();   
有问题么?应该没有吧...其实...
new确实存在问题:实现依赖,不能应对“具体实例化类型”的变化。
就是说Road()发生变化,比方说参数发生变化, 不仅要改Road类,客户程序Road road = new Road() 也需要更改

String类几乎不发生什么改变,是稳定的,就不存在这个问题

简单工厂模式又称为静态工厂模式:
封装变化点,哪里变化,封装哪里 没有变化就不用封装
---变化点在“对象创建” 所以封装对象创建, wrapper Road类

 RoadFactory
{
    public static Road CreateRoad()
    {
         
return new Road();
    }
}

class Road
{
   
//【设计模式】之 Simple Factory & Abstract Factory
}

客户程序
---
Road road 
= RoadFactory.CreteRoad();

这种封装其实是把实例化提出到另一处地方,相当于过程式程序设计中的函数的概念。
面对新的需求变化,例如Road构造方法发生变化,简单工厂模式将变化隔离在类库中。
思考
:如果我们增加一种Bridge呢?在类库中增加Bridge类,CreateBridge方法。

简单工厂模式实例

简单工厂模式对程序的复用性,易维护,可扩展带来什么好处呢?看例子(来自cj723"大话设计模式")
假设"TO"公司做了一个Operation的类,封装了常用的计算方法。
//Operation.cs
【设计模式】之 Simple Factory & Abstract Factory  
---未使用工厂模式,我们直接拿来使用:
operation oper = new operationSub();
oper.NumberA = 56;
oper.NumberB = 8;
double c = oper.GetResult();
同样存在“实现依赖”的问题;
---使用简单工厂模式解决:

 OperationFactory
    {
        public static Operation CreateOperator(string opera)
        {
            Operation operation 
= null;

            
switch (opera)
            {
                
case "+":
                    {
                        operation 
= new OperationAdd();
                        
break;
                    }
                
//【设计模式】之 Simple Factory & Abstract Factory.
                default:
                    
break;
            }
            
return operation;
        }
     }

客户程序
                  Console.Write(
"请输入数字A:");
                
string strNumberA = Console.ReadLine();
                Console.Write(
"请选择运算符号(+、-、*、/):");
                
string strOperate = Console.ReadLine();
                Console.Write(
"请输入数字B:");
                
string strNumberB = Console.ReadLine();
                
string strResult = "";

                Operation oper 
= OperationFactory.CreateOperator(strOperate);
                oper.NumberA 
= Convert.ToDouble(strNumberA);
                oper.NumberB 
= Convert.ToDouble(strNumberB);

                strResult 
= oper.GetResult().ToString();


简单工厂模式结构图

【设计模式】之 Simple Factory & Abstract Factory

简单工厂根据提供给它的参数(opera),经过必要的逻辑判断,决定哪个产品被创建,返回产品的公共父类。
产品基类Operation不一定非要是抽象类

---如果要改变Add的规则,只需要去更改OperationAdd()计算子类。(易维护好,只需更改类库中的计算子类!)
---如果要增加别的运算,如sin(), 需要增加运算子类OperationSin(), 还要更改OperationFactory类的switch逻辑判断分支。(可扩展性一般)
注意: 系统中增加业务规则类不是模式所能解决的,无论采用什么设计模式,OperationSin总是少不了的。(即增加了新系列产品)(TerryLee)
我们不能接受的是更改OperationFactory类,解决方法是用抽象工厂模式,用AddFactory生产OperationAdd,用SubFactory生产OperationSub ... 
---如果使用WinForm版的计算器,类库拿来即可使用. (复用性好)

“简单工厂模式实现计算器”代码在这里下载(拿了伍迷兄的改了改,呵呵)


 

抽象工厂模式引子
到底什么时候用抽象工厂模式,是值得思考的。就是说简单工厂模式的局限性在什么地方?
答:简单工厂模式不能应对“不同系列对象”的变化。
这是啥意思呢?
就是说,如果上面的计算器要卖给火星人用,那我们要针对火星数学法则,开发一套火星OperationAdd,火星OperationSub;
游戏场景设施工厂,我们要拓展游戏风格,要产生卡通风格的道路,建筑等等;简单工厂模式的FacilitiesFactory仅仅封装CreateRoad, CreateBuilding, createJungle就不能适用了。
这就是“不同系列对象”的变化。我们需要“隔离”这种变化。
另一个思考:
TerryLee的BonusTax例子,简单工厂模式也可以实现(只有一个工厂Factory;ChineseFactory,AmericanFactory其实只是用来做判断的):

 Factory
{
    public Tax CreateTax()
    {
            Tax currentTax 
= null;
            
if (Constant.STR_FACTORYNAME == "ChineseFactory")
            {
                currentTax 
= new ChineseTax();                
            }
            
else if (Constant.STR_FACTORYNAME == "AmericanFactory")
            {
                currentTax 
= new AmericanTax();
            }
            
return currentTax;
    }

    
public Bonus CreateBonus()
    {
            Bonus currentBonus 
= null;
            
if (Constant.STR_FACTORYNAME == "ChineseFactory")
            {
                currentBonus 
=  new ChineseBonus();
            }
            
else if (Constant.STR_FACTORYNAME == "AmericanFactory")
            {
                currentBonus 
= new AmericanBonus();
            }
            
return currentBonus;
            
    }
}

客户端可以做到不用更改,只要改配置文件就能实现American和Chinese之间的迁移。
Bonus bonus = new Factory().CreateBonus();
double bonusValue  = bonus.Calculate();

可是这样做到底好不好呢?
其实这样做和TerryLee用抽象工厂写的是一样的效果。
因为这个例子,对象之间的“相互依赖”关系不是很明显(bonus和tax之间),所以才造成这样的结果。
如果增加France的,也是只需要增加FranceTax、FranceBonus,在Factory类增加相应的逻辑(改写为反射,逻辑也不用改了)。
反射: currentTax = (Tax)Assembly.Load("FactorySalary").CreateInstance("FactorySalary." + TaxName); 还要Bonus反射,从这点看还是AbstractFactory有利些。
而如果象下面的例子(肉食动物和食草动物之间存在依赖关系)用抽象工厂就十分必要了。
所以GoF给出的概念,每个字都不是白给的!(一系列相互依赖的对象

结论:
简单工厂模式能够实现一组对象的创建工作,但如果对象之间有依赖关系,则要用抽象工厂模式。

抽象工厂模式产品族示意图(来自吕震宇博文)

比较形象的表达了抽象工厂模式
【设计模式】之 Simple Factory & Abstract Factory 
抽象工厂模式Motivation

在软件系统中,经常面临着“一系列相互依赖的对象”的创建工作;同时,由于需求的变化,往往存在更多系列对象的创建工作。(李建忠)
前者(只有一个系列)用简单工厂模式没有问题,后者因为需求的变化,"多系列",简单工厂模式会遇到麻烦。

抽象工厂模式意图

提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。

抽象工厂模式类图
【设计模式】之 Simple Factory & Abstract Factory

客户程序只依赖抽象,AbstractFactoy, AbstractProductA, AbstractProductB, 实现了我们需要的隔离。
因为往往复杂的模块在Client模块,我们不希望为这部分做改动。例如:

 GameManager
    {
        FacilitiesFactory facilitiesFactory;
        Road road;
        Building building;
        Tunnel tunnel;
        Jungle jungle;

        public GameManager( FacilitiesFactory facilitiesFactory )
        {
            
this.facilitiesFactory = facilitiesFactory;
        }

        
public void BuildGameFacilities()
        {
            road 
= facilitiesFactory.CreateRoad();
            building 
= facilitiesFactory.CreateBuilding();
            tunnel 
= facilitiesFactory.CreateTunnel();
            jungle 
= facilitiesFactory.CreateJungle();
        }
         
        
public void Run()
        {
            
//运行逻辑部分 十分复杂 不希望改动
            road.AAA();
            buliding.BBB(road);
            tunnel.CCC(jungle);
        }
    }


  主程序:
  GameManager g 
= new GameManager(new ModernFacilitiesFactory());
  g.BuildGameFacilities();
  g.Run();

 

抽象工厂模式structrual code

 GangOfFour.Abstract.Structural
{
  // MainApp test application 

  
class MainApp
  {
    
public static void Main()
    {
      
// Abstract factory #1 
      AbstractFactory factory1 = new ConcreteFactory1();
      Client c1 
= new Client(factory1);
      c1.Run();

      
// Abstract factory #2 
      AbstractFactory factory2 = new ConcreteFactory2();
      Client c2 
= new Client(factory2);
      c2.Run();

      
// Wait for user input 
      Console.Read();
    }
  }

  
// "AbstractFactory" 

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

  
// "ConcreteFactory1" 

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

  
// "ConcreteFactory2" 

  
class ConcreteFactory2 : AbstractFactory
  {
    
public override AbstractProductA CreateProductA()
    {
      
return new ProductA2();
    }
    
public override AbstractProductB CreateProductB()
    {
      
return new ProductB2();
    }
  }

  
// "AbstractProductA" 

  
abstract class AbstractProductA
  {
  }

  
// "AbstractProductB" 

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

  
// "ProductA1" 

  
class ProductA1 : AbstractProductA
  {
  }

  
// "ProductB1" 

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

  
// "ProductA2" 

  
class ProductA2 : AbstractProductA
  {
  }

  
// "ProductB2" 

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

  
// "Client" - the interaction environment of the products 

  
class Client
  {
    
private AbstractProductA AbstractProductA;
    
private AbstractProductB AbstractProductB;

    
// Constructor 
    public Client(AbstractFactory factory)
    {
      AbstractProductB 
= factory.CreateProductB();
      AbstractProductA 
= factory.CreateProductA();
    }

    
public void Run()
    {
      AbstractProductB.Interact(AbstractProductA);
    }
  }

输出结果:
ProductB1 interacts with ProductA1
ProductB2 interacts with ProductA2

抽象工厂模式实例

Herbivore:草食动物    willebeest: 非洲羚羊  bison:美洲野牛
Carnivore:食肉动物    Lion: 非洲狮          jaguar: 美洲虎
尽管在不同大陆下动物物种是不一样的,但动物间的关系仍然保留了下来。(对象之间存在相互依赖的关系)
类图:
【设计模式】之 Simple Factory & Abstract Factory

代码:

 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 Jaguar(); }
}

// "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 Jaguar : 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();
  }
}

Output
Lion eats Wildebeest
Jaguar eats Bison

从“OCR”来看抽象工厂模式

"开放-封闭"原则要求系统对扩展开放,对修改封闭。通过扩展达到增强其功能的目的。对于涉及到多个产品族与多个产品等级结构的系统,其功能增强包括两方面
增加产品族(纵坐标):Abstract Factory很好的支持了"开放-封闭"原则。
增加新产品的等级结构(横坐标):需要修改所有的工厂角色,没有很好支持"开放-封闭"原则。
综合起来,抽象工厂模式以一种倾斜的方式支持增加新的产品,它为新产品族的增加提供方便,而不能为新的产品等级结构的增加提供这样的方便。
所以用抽象工厂模式之前,一定要设计好系统用到的所用的产品等级结构,因为后来的改变将是剧烈的,系统对新的对象完全是陌生的!
需求分析的重要性!OO设计中要注意寻找变化点,为以后方便!
(系统有多少元素,building,bridge,road,...; Herbrivore, Carnivore...; Bonus, Tax,... 要先固定下来)

抽象工厂模式实现要点

l         抽象工厂将产品对象的创建延迟到它的具体工厂。

l         如果没有应对“多系列对象创建”的需求变化,则没有必要使用抽象工厂模式,这时候使用简单的静态工厂完全可以。

l         系列对象指的是这些对象之间有相互依赖、或作用的关系。

l         Abstract Factory主要在于应对“新系列”的需求变动,其缺点在于难以应对“新对象”的需求变动。

l         抽象工厂模式经常和工厂方法模式共同组合来应对“对象创建”的需求变化。

l         通常在运行时刻创建一个具体工厂类的实例,这一具体工厂的创建具有特定实现的产品对象,为创建不同的产品对象,客户应使用不同的具体工厂
例如ContnentFactory fac =
new AfricaFactory()。

l         把工厂作为单件,一个应用中一般每个产品系列只需一个具体工厂的实例,因此,工厂通常最好实现为一个单件模式。

l         创建产品,抽象工厂仅声明一个创建产品的接口,真正创建产品是由具体产品类创建的,最通常的一个办法是为每一个产品定义一个工厂方法,一个具体的工厂将为每个产品重定义该工厂方法以指定产品,虽然这样的实现很简单,但它确要求每个产品系列都要有一个新的具体工厂子类,即使这些产品系列的差别很小。

相关文章: