30

读书笔记-设计模式-可复用版-抽象工厂-腾讯游戏学院

 5 years ago
source link: http://gad.qq.com/article/detail/289419
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.

终于到了5种创建型设计模式的最后一种,抽象工厂Abstract Factory,书中有提到,Abstract Factory中通常使用工厂方法Factory Method实现,不是通常,都是使用工厂方法Factory Method来实现的:)


先复习一下工厂方法(Factory Method)的概念:


定义”一个“用于创建对象的接口,让子类决定,实例化哪一个类


抽象工厂的概念是:


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


一个和一系列


都是创建产品,工厂方法是针对一个产品的创建,而抽象工厂是针对完成品的完整系列


可以说,抽象工厂,就是一个工厂方法的集合,而这些工厂方法所创建出来的对象之间是相关或是有相互依赖关系的。


两个完全不相关的工厂方法,是不能定义在抽象工厂当中的,不相关更不要提相互依赖。


比如生产汉堡的抽象工厂中,应该生产的和汉堡相关的一些面料,佐料等等,而不应该生产烤鸭,炸酱面等北京菜......


但写到这里发现,比如我要做一个汉堡,通过抽象工厂是可以实现的,但汉堡制作起来工序其实是比较多的,比较复杂,并且DIY多样化,一步一步的搭配创建,其实更符合之前讲到的Builder构建者设计模式


Builder构建者设计模式和Abstract Factory抽象工厂是非常类似的,但Builder通常是用来构建复杂的对象,并且强调是一步一步的构建,而抽象工厂对于简单和复杂的对象构建都可以,它着重于多个系列产品的对象。比如不同的UI显示风格,室内的装修风格,样式style等等


一个抽象工厂创建了一个产品的完整系列,如果我们需要改变风格,只需要替换

具体的抽象工厂派生类,这样整个系列的产品都会被改变,系列中的相关的每一部分,都定义在抽象工厂类中。


Abstract Factory抽象工厂在实现中一些说明:


1.一般每个系列的产品只需要有一个ConcreteFactory,对于这种独一无二的对象,我们可以设置他为Singleton单例


2.抽象工厂中,只声明一系列创建产品的接口,具体的创建是由ConcreteProduct子类实现的,这里注是FactoryMehod 工厂方法的应用。


3.抽象工厂中,不会指明具体的类,你看不到ConcreteProduct,有的只是抽象或是基类Product,具体由抽象工厂的派生类指定哪种产品!


可复用版本中,通过创建不同风格的墙壁,门,房间来演示如何应用Abstract Factory抽象工厂的使用


抽象工厂是工厂方法的集合,所以两个要结合起来使用,但之前在说明工厂方法的

时候,有说过他存在的缺点,当我新增Product时,我就会增加一个新的工厂方法来创建Product,可以通过范型去避免创建更多的子类


在大话设计模式中,认为如果Product不断的增多,就需要创建多个工厂方法,不如回到参数化的工厂方法,但又存在新增或修改,都会涉及到的Factory的改动,要

新增switch case / if else,又要面对如何消除switch case / if else的问题


提到了反射的方案,反射确实是一个很偷懒方法,不需要指定具体的类,只要程序集成功加载,我就可以通过“字符串”去找到我想要创建的类,并完成类的一切操作。


这种方式也是OK的,在移动端,如果不是频繁大量的使用没问题,毕竟反射的性能 比较差,但依赖字符串就是一件很危险的事儿了,这需要我们在具体的开发过程中,根据不同的情况,采取不同的方案,反射+字符串这套方案,不应优先考虑。


下面举例说明抽象工厂Abstract Factory的使用:


我打算开一家Coffee Bar,咖啡馆有不同的设计风格,比如我们简单的划分为

现代和复古两种风格


当确认了风格以后,Coffee Bar内的很多部分都会沿着主风格变化,比如我们大致

的将室内的部分划分为壁画,灯光,音乐这三部分构成,我做为老板,我来

选择我想要的风格


代码:


public class Mural

    public string style { getset; }//风格

    public int budget { getset; }//预算


    public virtual void Painting()

    {

        Debug.Log("your mural is " + style);

    }

定义Mural(壁画)产品基类


并派生三个子类,实现不同风格的Mural


Pet,Flower,Food 宠物,鲜花,食物


public class PetMural : Mural

    public PetMural()

    {

        style = "Pet";

        budget = 1000;

    }


    public override void Painting()

    {

        base.Painting();

        Debug.Log("there cat and two dogs are sleeping on the bed");

    }


public class FlowerMural : Mural

    public FlowerMural()

    {

        style = "Flower";

        budget = 2000;

    }


    public override void Painting()

    {

        base.Painting();


        Debug.Log("There are some roses in the lovely vase");

    }


public class FoodMural : Mural

    public FoodMural()

    {

        style = "Food";

        budget = 3000;

    }


    public override void Painting()

    {

        base.Painting();

        Debug.Log("There are some pizza and cakes......");

    }


这里为了方便演示,直接在构造函数中指定style和budget,并通过Painting()打印出每个具体的Mural.


下一步,通过工厂方法,来创建不同的Mural.

为了避免创建更多的子类,我们使用泛型


public interface IMuralFactory

    Mural CreateMural();


public class MuralFactory<T> : IMuralFactory where T : Muralnew()

    public Mural CreateMural()

    {

        return new T();

    }


接下来是灯光和音乐,代码上基本上和Mural是一样的,保持队形完整,也贴上来,就不做过多的解释了


public class Lighting

    public Color mainColor { getset; }

    public int budget { getset; }


    public virtual void On()

    {

        Debug.Log("your lighting main color is " + mainColor.ToString());

    }


//白色光

public class WhiteLighting : Lighting

    public WhiteLighting()

    {

        mainColor = Color.white;

        budget = 500;

    }


    public override void On()

    {

        base.On();

    }


//黄色,淡黄色

public class PopcoinLighting:Lighting

    public PopcoinLighting()

    {

        mainColor = Color.yellow;

        budget = 800;

    }


    public override void On()

    {

        base.On();

    }


public interface ILightingFactory

    Lighting CreateLighting();


public class LightingFactory<T> : ILightingFactory where T : Lightingnew()

    public Lighting CreateLighting()

    {

        return new T();

    }


public class Music

    public string style { getset; }

    public string songName { getset; }


    public int budget { getset; }


    public virtual void Play()

    {

        Debug.Log("your music style is " + style);

        Debug.Log(songName + " is playing");

    }


//流行音乐

public class PopMusic:Music

    public PopMusic()

    {

        style = "Pop";

        songName = "In the Midst Of Rain";

        budget = 2000;

    }


    public override void Play()

    {

        base.Play();

    }



//爵士乐

public class JazzMusic : Music

    public JazzMusic()

    {

        style = "Jazz";

        songName = "Fletcher's Song In Club";

        budget = 1000;

    }


    public override void Play()

    {

        base.Play();

    }



public interface IMusicFactory

    Music CreateMusic();


public class MusicFactory<T> : IMusicFactory where T : Musicnew()

    public Music CreateMusic()

    {

        return new T();

    }


下面是创建咖啡馆的抽象工厂Abstract Factory


public interface ICoffeeBarFactory

    Mural CreateMural();//创建壁画


    Lighting CreateLighting();//创建灯光


    Music CreateMusic();//创建音乐


    int Budget(Mural mural, Lighting lighting, Music music);


Budget用于计算总预算


这里我定义成了interface,这不是强制,抽象工厂也不一定是抽象类,我在初版本实现的时候,使用的是非抽象类,即普通的基类,但我改成了interface的原因是,在上面提到的关于抽象工厂说明中,通常一个系列产品只需要一个具体的抽象工厂类即可,所以一般定义成Singleton


所以需要使用泛型,但因为使用了范型,那么在定义抽象工厂对象的时候,就需要指定具体的派生类,为了不指定,因为这样做并不合理,我们可以面向接口,接口是非泛型的


抽象工厂基类:


public class CoffeeBarFactory<T> : ICoffeeBarFactory where T:class,new()

    private static readonly T instance = new T();

    public static T Instance

    {

        get

        {

            return instance;

        }

    }


    public int budget { getset; }


    public virtual Mural CreateMural() { return null; }//创建壁画


    public virtual Lighting CreateLighting() { return null; }//创建灯光


    public virtual Music CreateMusic() { return null; }//创建音乐


    public int Budget(Mural mural, Lighting lighting, Music music)

    {

        budget = mural.budget + lighting.budget + music.budget;

        return budget;

    }



这是核心的地方,需要注意以下几点:


1.CoffeeBarFactory<T>

要使用泛型,因为要支持Singleton单例


 private static readonly T instance = new T();

    public static T Instance

    {

        get

        {

            return instance;

        }

    }


那么如果我想定义一个CoffeeBarFactory的引用,就必须指定T,指定类型

CoffeeBarFactory<xxx> factory;


但这种就等同于面向具体的的编程了,所以不能这样定义,因为我们只需要调用抽象工厂的方法,所以可以将这些行为定义成为接口,我们面向接口就好了


不用再定义CoffeeBarFactory<xxx> factory这样,直接定义接口的引用:

ICoffeeBarFactory factory;


2.CoffeeBarFactory<T>实现了接口ICoffeeBarFactory,所以,必须要实现ICoffeeBarFactory中所有的方法,但实际上,大部分的方法,我们需要通过

派生类来实现,所以需要为每一个要派生类实现的接口方法,均加上virtual

这样派生类override即可,如果不这么做,派生类通过new 只是隐藏了基类

的实现,但在实际调用中,会发现,通过ICoffeeBarFactory引用调用的依然

是基类的版本


抽象工厂已经定义好了,接下来就要实现现代和复古两个风格版本


public class ModernCofferBarFactory:CoffeeBarFactory<ModernCofferBarFactory>

    public override Mural CreateMural()

    {

        return new MuralFactory<FoodMural>().CreateMural();

    }


    public override Lighting CreateLighting()

    {

        return new LightingFactory<WhiteLighting>().CreateLighting();

    }


    public override Music CreateMusic()

    {

        return new MusicFactory<PopMusic>().CreateMusic();

    }



T指定为ModernCofferBarFactory,这样我可以直接通过Singleton的形式来调用


CoffeeBarFactory<ModernCofferBarFactory>.Instance


CreateMural,CreateLighting,CreateMusic三个接口,分别针对了不同的风格进行了实现


下面是复古版本的实现:


public class AncientCofferBarFactory : CoffeeBarFactory<AncientCofferBarFactory>

    public override Mural CreateMural()

    {

        return new MuralFactory<FlowerMural>().CreateMural();

    }


    public override Lighting CreateLighting()

    {

        return new LightingFactory<PopcoinLighting>().CreateLighting();

    }


    public override Music CreateMusic()

    {

        return new MusicFactory<JazzMusic>().CreateMusic();

    }


最后,定义Client,即调用的代码,通过抽象工厂来创建我想要的风格


public class MyCoffeBar

    private Mural mural { getset; }

    private Lighting lighting { getset; }

    private Music music { getset; }


    private ICoffeeBarFactory factory;


    public void MakeCoffeeBar(ICoffeeBarFactory factory)

    {

        this.factory = factory;


        mural = factory.CreateMural();

        lighting = factory.CreateLighting();

        music = factory.CreateMusic();


        mural.Painting();

        lighting.On();

        music.Play();


    }


    public int GetBudget()

    {

        return factory.Budget(mural, lighting, music);

    }



MakeCoffeeBar接受一个ICoffeeBarFactory接口,上面有说到为什么要使用接口,

然后定义了三个属性


private Mural mural { getset; }

private Lighting lighting { getset; }

private Music music { getset; }


通过MakeCoffeeBar方法,传进来的接口进行创建,并调用输出内容,GetBudget会输出,当前选择的风格的总预算


调用代码:


MyCoffeBar coffeeBar = new MyCoffeBar();

        coffeeBar.MakeCoffeeBar(CoffeeBarFactory<ModernCofferBarFactory>.Instance);

Debug.Log("<<<<<<>>>>>");

Debug.Log("your budget is "+coffeeBar.GetBudget());


输出结果:


5c84dd7580b72.png


如果我想要将风格修改为ModernCofferBarFactory,只需要修改一行代码:


coffeeBar.MakeCoffeeBar(CoffeeBarFactory<ModernCofferBarFactory>.Instance);


输出结果如下:

5c84dd89aaf0c.png

最后,贴上抽象工厂的结构图:
5c84dd9c184cf.png

AbstractFactory

CreateProductA()

CreateProductB()


在上面的代码中,AbstractFactory=ICoffeeBarFactory

CreateProductA()

CreateProductB()

即是ICoffeeBarFactory中声明的接有创建对象的工厂方法


ConcreteFactory1&ConcreteFactory2

分别对应ModernCofferBarFactory&AncientCofferBarFactory


AbstractProductA&AbstractProductB均是Product的基类,

对应着Mural,Lighting,Music


ProductA1,ProcutA2,ProductB1,ProductB2

则对应着上面所有Mural,Lighting,Music的派生类


抽象工厂模式到此为止,接下来会对五种创建型的设计模式进行一次全面的总结,当然,迭代是不可避免的:)


周末愉快!2019.3.9 雕刻时光咖啡馆(北苑)


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


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

5c18e453e7d86.jpg

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK