二.工厂方法模式

Posted zaijianba

tags:

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

??工厂方法模式

引言:上一篇写的的是简单工厂模式,简单工厂模式只有三个要素(工厂、抽象产品、具体产品),它没有工厂接口,并且得到产品的方法一般是静态的,所以在工厂实现的扩展性上面较差,可以当作工厂模式的简化版。在简单工厂模式中,当增加一个产品子类的时候,还需要在工厂方法的Switch分支中新增一个判断,只做到了对扩展的开放,并没有做到对修改关闭,而这点在工厂方法模式中得到了一定的克服,作为简单工厂模式的升级版,工厂方法模式更适用于复杂一点的创建方法中。

1.何为工厂方法模式?

介绍

? 工厂方法模式(FACTORY METHOD)是一种常用的类创建型设计模式,此模式的核心精神是封装类中变化的部分,提取其中个性化善变的部分为独立类,通过依赖注入以达到解耦、复用和方便后期维护拓展的目的。它的核心结构有四个角色,分别是抽象工厂、具体工厂、抽象产品、具体产品 。

该模式中包含的角色及其职责:??

  1. 抽象工厂(Creator)角色:

    是工厂方法模式的核心,与应用程序无关。任何在模式中创建的对象的工厂类必须实现这个接口。

  2. 具体工厂(Concrete Creator)角色:

    这是实现抽象工厂接口的具体工厂类,包含与应用程序密切相关的逻辑,并且受到应用程序调用以创建产品对象。在上图中有两个这样的角色:BulbCreator与TubeCreator。

  3. 抽象产品(Product)角色:

    工厂方法模式所创建的对象的超类型,也就是产品对象的共同父类或共同拥有的接口。

  4. 具体产品(Concrete Product)角色:

    这个角色实现了抽象产品角色所定义的接口。某具体产品有专门的具体工厂创建,它们之间往往一一对应。

可以看到,对比简单工厂模式,工厂方法模式多了个抽象工厂(Creator)的角色,这也是工厂方法模式的核心。

2.情景再现?

情景:

? 上一篇的例子中,我们使用了简单工厂模式根据系统的不同从而创建不同的系统实例,这一次我们使用工厂方法模式实现上一篇的需求。

上一篇的需求如下??

说起操作系统,市面上大概可以分为三种:

  1. Windows操作系统
  2. Linux操作系统
  3. Mac OS操作系统,

? 现在老板有个需求,根据用户使用的操作系统的不同,分别调用不同操作系统的 SayHello()方法,和用户打招呼。

编写代码:

工厂方法模式代码:

工厂接口:任何在模式中创建的对象的工厂类必须实现这个接口

    /// <summary>
    /// 工厂抽象接口
    /// </summary>
    interface iosFactory
    {
        /// <summary>
        /// 创建操作系统类
        /// </summary>
        /// <returns></returns>
        OS CreateOS();
    }

具体工厂:实现抽象工厂接口的具体工厂类

    /// <summary>
    /// Windows操作系统工厂
    /// </summary>
    class WindowsOSFactory : IOSFactory
    {
        public OS CreateOS()
        {
            return new WindowsOS();
        }
    }
    /// <summary>
    /// Mac操作系统工厂
    /// </summary>
    class MacOSFactory : IOSFactory
    {
        public OS CreateOS()
        {
            return new MacOS();
        }
    }
    /// <summary>
    /// Linux操作系统工厂
    /// </summary>
    class LinuxOSFactory : IOSFactory
    {
        public OS CreateOS()
        {
            return new LinuxOS();
        }
    }

抽象产品角色:工厂方法模式所创建的超类型,也就是产品对象的共同父类或者共同拥有的接口,在此例中为OS

    //包含SayHello()方法
    interface ISayHelloAble
    {
        string SayHello();
    }
    /// <summary>
    /// 操作系统抽象父类
    /// </summary>
    public abstract class OS:ISayHelloAble
    {
        public abstract string SayHello();
    }

具体产品角色:这个角色实现了抽象产品角色所定义的接口。某具体产品有专门的具体工厂创建,它们之间往往一一对应,此例中为不同的操作系统。

    /// <summary>
    /// 具体的Windows操作系统,继承抽象的OS类
    /// </summary>
    class WindowsOS : OS
    {
        public override string SayHello()
        {
            string result = "你好,我是Windows操作系统,很高兴为您服务~";
            return result;
        }
    }

    /// <summary>
    /// 具体的Linux操作系统
    /// </summary>
    class LinuxOS : OS
    {
        public override string SayHello()
        {
            string result = "你好,我是Linux操作系统,很高兴为您服务~";
            return result;
        }
    }

    /// <summary>
    /// 具体的Mac操作系统
    /// </summary>
    class MacOS : OS
    {
        public override string SayHello()
        {
            string result = "你好,我是Mac OS操作系统,很高兴为您服务~";
            return result;
        }
    }

客户端:

        static void Main(string[] args)
        {
            //之后可以利用反射原理优化代码
            IOSFactory oSFactory = new WindowsOSFactory();
            OS concreteOS = oSFactory.CreateOS();
            string greetings = concreteOS.SayHello();
            Console.WriteLine(greetings);
            Console.ReadKey();
        }

结果

技术图片

? ?使用工厂方法模式之后,一个简单工厂模式的工厂类,变成了一个工厂抽象接口和多个具体生成对象的工厂,如果我们要对其它的操作系统进行支持,那么我们就不需要更改原有的工厂类了

? 例如我们需要增加Netware操作系统,我们只需要增加此功能的操作系统类和对应的工厂就行了。??

    /// <summary>
    /// 具体的Netware操作系统
    /// </summary>
    class NetwareOS : OS
    {
        public override string SayHello()
        {
            string result = "你好,我是Netware操作系统,很高兴为您服务~";
            return result;
        }
    }
    /// <summary>
    /// Netware操作系统工厂
    /// </summary>
    class NetwareOSFactory : IOSFactory
    {
        public OS CreateOS()
        {
            return new NetwareOS();
        }
    }

?

但是仔细观察发现,工厂方法模式实现时,客户端需要决定实例化哪一个工厂来实现操作系统类,选择判断的问题还是存在的,也就是说,工厂方法把简单工厂的内部逻辑判断移到了客户端代码来进行,你想要增加功能,本来是改工厂类的,而现在是修改客户端!

? 那么有什么办法,让选择问题抛在客户端之外解决呢?我们可以使用“反射”。。

关于反射与工厂方法模式的结合将会在下一篇博客《抽象工厂模式》实现。

3.工厂方法模式"骨架"?

工厂方法模式UML图:

技术图片

创建步骤:

步骤1: 创建抽象工厂类,定义具体工厂的公共接口;
步骤2: 创建抽象产品类 ,定义具体产品的公共接口;
步骤3: 创建具体产品类(继承抽象产品类) & 定义生产的具体产品;
步骤4:创建具体工厂类(继承抽象工厂类),定义创建对应具体产品实例的方法;
步骤5:外界通过调用具体工厂类的方法,从而创建不同具体产品类的实例

4.优缺点及使用场景??

优点

  • 工厂方法克服了简单工厂违背开放-封闭原则的缺点,又保持了封装对象创建过程的优点,和简单工厂模式一样,他们都是集中封装了对象的创建,使得要更换对象时,不需要做大的改动就可实现,降低了客户端程序和产品对象的耦合。
  • 工厂模式可以说是简单工厂模式的进一步抽象和拓展,在保留了简单工厂的封装优点的同时,让扩展变得简单,让继承变得可行,增加了多态性的体现。

缺点

  • 添加新产品时,除了增加新产品类外,还要提供与之对应的具体工厂类,系统类的个数将成对增加,在一定程度上增加了系统的复杂度;同时,有更多的类需要编译和运行,会给系统带来一些额外的开销;
  • 由于考虑到系统的可扩展性,需要引入抽象层,在客户端代码中均使用抽象层进行定义,增加了系统的抽象性和理解难度,且在实现时可能需要用到DOM、反射等技术,增加了系统的实现难度。
  • 虽然保证了工厂方法内的对修改关闭,但对于使用工厂方法的类,如果要更换另外一种产品,仍然需要修改实例化的具体工厂类;
  • 一个具体工厂只能创建一种具体产品

使用场景

工厂方法经常用在以下两种情况中:

  1. 第一种情况是对于某个产品,调用者清楚地知道应该使用哪个具体工厂服务,实例化该具体工厂,生产出具体的产品来。Java Collection中的iterator() 方法即属于这种情况。
  2. 第二种情况,只是需要一种产品,而不想知道也不需要知道究竟是哪个工厂为生产的,即最终选用哪个具体工厂的决定权在生产者一方,它们根据当前系统的情况来实例化一个具体的工厂返回给使用者,而这个决策过程这对于使用者来说是透明的。

————————————————————————————————————————————

版权声明:本文为吴恺的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
为获得更好的阅读体验,推荐至我的个人博客阅读:https://www.wukailiving.cn/er-gong-han-fang-fa-mo-shi.html
原文链接:https://www.cnblogs.com/zaijianba/p/11519672.html

如有不足之处,欢迎指正!

以上是关于二.工厂方法模式的主要内容,如果未能解决你的问题,请参考以下文章

二.工厂方法模式

设计模式:工厂方法模式

设计模式:工厂方法

工厂方法模式

设计模式二:工厂模式

简单工厂模式\工厂方法模式