C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)

时间:2022-09-08 20:48:59

意图

           在《Design Patterns》一书中,ABSTRACT FACTORY(图省事以下用AF代替了)模式是第一个被介绍的模式,个人以为AF模式也是最容易理解的模式之一。 
          《Design Patterns》中,对AF模式的描述是这样的:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。用通俗一点的话说:有一个对象,它可以创建一堆一系列的其他对象,每个系列的对象是从一个基类派生的。个人觉得,这些所谓的意图理解不理解问题都不是很大,知道什么时候应该使用什么模式就够了,这就叫“只能意会,不能言传”。

别名

           Kit ,这个别名用途不大,现在基本都是用AF称呼这个模式,很少有使用Kit的。

实现 

           有一些类表示一个“产品”的“同功能,不同类型的部件”,我们的应用中要使用这些类,由于OOP的多态性为了方便使用,基本是要靠派生的:

 1 C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)// 一系列的“喇叭”,估计这个说吧,最后一个是外星语的
 2 C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂) // 它们都从Speaker派生,这是废话
 3 C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂) public   class  Speaker  {
 4C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)    virtual public string SayHi() {
 5C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)        return "Hello";
 6C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)    }

 7C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)}

 8 C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)
 9 C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂) public   class  ChineseSpeaker : Speaker  {
10C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)    public override string SayHi() {
11C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)        return "你好";
12C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)    }

13C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)}

14 C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)
15 C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂) public   class  FjidjSpeaker : Speaker  {
16C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)    public override string SayHi() {
17C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)        return "%F&*#";    
18C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)}

19C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)}
            光有这样一些类虽然也可以使用AF模式,但是这样似乎没有什么意义,AF模式经常要制作几个系列的对象,那几个系列的对象还要有逻辑上的关系,否则不但不会起到应有的作用还能使程序变得更加恶心。再定义一组“部件”:
 1 C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)// 构造一块屏幕,代码没什么意思
 2 C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂) public   class  Screen  {
 3C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)    virtual public string Display() {
 4C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)        return "This is Screen";
 5C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)    }

 6C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)}

 7 C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)
 8 C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂) public   class  ChineseScreen : Screen  {
 9C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)    public override string Display() {
10C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)        return "这是屏幕";
11C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)    }

12C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)}

13 C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)
14 C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂) public   class  FfdhkScreen : Screen  {
15C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)    public override string Display() {
16C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)        return "SF&*&*#%";
17C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)    }

18C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)}

        现在有了两个“部件”了,一个喇叭,一个屏幕。我们需要把这个东西组成一个“产品”,考虑以下事实:每一中产品中的喇叭和屏幕必须是对应的(其实也可以混合,不过这样......),假设这个产品叫做"xToy“,那就应该有一个给我这种英文盲使用的"ChineseXtoy"和给外星人使用的"DFxToy”,标准的是英文版的"xToy"。
C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)// 一个更加无聊的类,甚至唯一的Play()方法都毫无意义(还是错误的)
C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)
// 但是,能够说明问题就好了
C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)
public   class  xToy  {
C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)    
public xToy(Speaker sp, Screen scr) {
C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)        _speaker 
= sp;
C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)        _screen 
= scr;
C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)    }
   
C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)
C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)    
public void Play() {
C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)        _speaker.SayHi();
C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)        _screen.Display();
C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)    }

C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)
C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)    
private Speaker _speaker;
C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)    
private Screen _screen;
C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)}
   由于OOP的多态性,我们不需要为中文版和外星版创建子类了,只要改变传入的Speaker对象和Scrren对象就好。看起来像这样:
C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)// 标准版
C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)
xToy std  =   new  xToy( new  Speaker(),  new  Screen() );
C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)
C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)
// 中文版
C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)
xToy c  =   new  xToy( new  ChineseSpeaker(),  new  Chinese());
C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)
C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)
// 外星版
C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)
xToy f  =   new  xToy( new  FjidjSpeaker(),  new  FfjhkScreen());
C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)
C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)
// 同时我们可以创建一个“疯狂”版
C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)
xToy cray  =   new  xToy( new  ChineseSpeaker(),  new  FfjhkScreen());

        这样实现,达到了预期的效果,但是也给创建“疯狂版”留下了捷径。要解决疯狂版的问题,我们可以加入验证过程,保证传入的喇叭和屏幕是对应系列的,或者可以派生xToy类,使用新的构造函数。这两种办法都增加了不少工作,尤其前者,如何判断实现无关的两个对象逻辑上十分相关?或者给那两个对象建立关系?
        更麻烦的是,如果以后这个玩具增加更多的部分,那么就会有一个非常恐怖的构造函数了
C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)xToy ex  =   new  xToy( new  Speaker(),  new  Screen(),  new  Keybroad(),  new  Handle(),  new  OtherThing());
        解决的办法之还是可以派生产品的“子类”,让子类完成创建这些新加入的部件的工作。 或者,我们在这里就可以使用AF模式了,首先创造一个新的基类,用于创建标准的Speaker和Screen,然后派生它,用于创建其他“非标准”的部件
C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)public   class  Fac  {
C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)    
virtual public Speaker CreateSpeaker() {
C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)        
return new Speaker();
C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)    }

C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)
C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)    
virtual public Screen CreateScreen() [
C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)        
return new Screen();
C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)    }

C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)}
C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)
C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)
public   class  ChFac() : Fac  {
C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)    
public override Speaker CreateSpeaker() {
C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)        
return new ChineseSpeak();
C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)    }

C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)
C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)    
public override Screen CreateScreen() {
C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)        
return new ChineseScreen();
C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)    }

C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)}
        最后,xToy类的构造函数也是要修改的,接受一个Fac对象,并调用Fac的两个方法创建“产品”
C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)public  xToy(Fac fac)  {
C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)    _speaker 
= fac.CreateSpeaker();
C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)    _screen 
= fac.CreateScreen();
C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)}
 现在要构造不同的产品,只要传入不同的“工厂”就可以了
C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)xToy std  =   new  xToy( new  Fac());
C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)
C#中23个经典设计模式(1)——ABSTRACT FACTORY(抽象工厂)xToy c 
=   new  xToy( new  ChFac());
     实际应用中,Fac类应实现为一个抽象类,本身不进行任何工作,这也是AF模式得名的原因。

C#中的情况     
        在C#中,由于有了接口和抽象类两种用于继承的*,所以AF模式的实现就分为抽象类实现和接口实现两种方式,我个人建议,实现AF模式,一定不要使用接口实现的方式,一个IFactory接口是让人非常困惑的,AF模式的本质是类"is a"工厂”而不是"has a"工厂。
        虽然C#中有完善的垃圾回收机制,但是像上面这样每次构造“产品”都要先构造“工厂”是无法令人接受的,把“工厂”实现为SINGLETON模式是非常好的,或者把“工厂”改变为静态类也是非常好的。

缺陷
        要添加制造不同“产品”的“工厂”很容易,只要派生“部件”类和“工厂”类就可以了。要改变不同组合更容易,只要派生“工厂”类就可以了。但是,如果要给“产品”增加一个新的“部件”就非常痛苦了,要改变“产品”类(不论是否使用AF模式,这都是必须的),要添加一个新的“部件”类(这也是必须的),要改变所有的“工厂”类(包括所有派生类),这样的话整个实现体系都要全部修改,麻烦是很多的,好在所有使用“产品”的应用不需要修改。
        对于上面的问题,还有一种解决办法,就是派生“产品”类来表示“添加了新的部件的产品”,同时派生“工厂”类和“部件”类,这样的一个很大的问题就是,新的“产品”失去了通用的“工厂”接口,必须为其传入新的“工厂”,否则编译时就会出错。同样,所有使用“产品”的应用不需要修改(使用新“产品”的应用应该使用新的接口)。

相关模式
        SINGLETON:AF模式基本使用SINGLETON模式构建。
        FACTORY METHOD: AF模式与其非常相似,但是抽象级别不同,简单说FACTORY METHOD就是使用接口实现的,是"has a“工厂的本质。