zoukankan      html  css  js  c++  java
  • 工厂模式

    简单工厂模式,工厂方法模式,抽象工厂模式

    本文使用C#和Vuejs对简单工厂模式,工厂方法模式,抽象工厂模式进行了描述

    分类: 创建型模式

    参见

    简单工厂模式

    定义

    • 该模式又被称为静态工厂方法模式;
    • 简单工厂模式是由一个工厂对象决定创建出哪一种产品的实例;
    • 简单工厂模式就是可以根据传入的参数的不同,返回不同的对象;
    • 设计模式中的工厂模式借鉴了现实生活中工厂的概念。

    示例

    // 抽象汽车类, 也可以是接口
    public abstract class Car 
    { 
        public string Type { get; set;}
    }
    
    // 特斯拉
    public class Tesla: Car 
    { 
        public Tesla ()
        {
            this.Type = "电动力车";
            Console.Writeline("生产特斯拉电动汽车");
        }
    }
    
    // 奔驰
    public class Benz: Car 
    { 
        public Benz ()
        {
            this.Type = "汽油动力车";
            Console.WriteLine("生产奔驰汽车");
        }
    }
    
    // 工厂
    public class CarFactory
    {
        // 静态工厂方法
        public static Car Build(string carName)
        {
            Car car = null;
            if ("Tesla" == carName)
            {
                car = new Tesla();
            } else if ("Benz" == carName)
            {
                car = new Benz();
            } else 
            {
                Console.WriteLine("没找到要生产的汽车Name");
            }
            return car;
        }
    }
    
    // 消费者
    public class Customer
    {
        public static void Main(String[] args)
        {
            CarFactory.Build("Benz");
            CarFactory.Build("Tesla");
            CarFactory.Build("Audi");
        }
    }
    
    

    JavaScript 示例

    var CarFactory = (function () {
        var Car = function (carName, carType) {
            this.CarName = carName;
            this.CarType = carType;
        };
        return function (carName, carType) {
            return new Car(carName, carType);
        }
    })();
    
    // 消费
    var audi = new CarFactory("Audi", "汽油车");
    var benz = new CarFactory("Benz", "汽油车");
    var tesla = new CarFactory("Tesla", "电动车");
    
    

    Vuejs 示例

    <html>
    
    <head></head>
    
    <body>
        <div id="app">
        </div>
    
        <div id="test">
        </div>
    
        <script src="https://unpkg.com/vue"></script>
        <script>
            // 工厂
            var buildTemplate = function (name) {
                var template = ''
                if ('app' === name) {
                    template = '<p style="color:red;">Hello ' + name + '</p>'
                } else if ('test' === name) {
                    template = '<p style="color:blue;">Hello ' + name + '</p>'
                }
                return template
            }
            const appName = 'test'
            // 消费者
            var app = new Vue({
                el: '#'+ appName,
                template: buildTemplate(appName)
            })
    
        </script>
    </body>
    
    </html>
    

    优点

    • 将类实例化的操作与“使用”操作分离开,让使用者不用知道具体参数就可以实例化出所需要的“产品”类,从而避免了在客户端(消费者)代码中显式指定,实现了解耦;也就是说,使用者可以直接消费产品而无需知道其生产的细节
    • 把初始化实例时的工作放到工厂里进行,使代码更容易维护
    • 更符合面向对象的原则 -> 面向接口编程

    缺点

    1. 如果需要新的产品,只能加if-else或者Switch加case,也就是硬编码,违反了开闭原则,对维护和扩展不够友好,所以一般使用在条件不多变化不大的情况下。
    2. 工厂类集中了所有实例(产品)的创建逻辑,一旦这个工厂不能正常工作,整个系统都会受到影响;
    3. 简单工厂模式由于使用了静态工厂方法,静态方法不能被继承和重写,会造成工厂角色无法形成基于继承的等级结构;

    使用场景

    1. 工厂类负责创建的对象比较少,不会造成工厂方法中的业务逻辑太过复杂
    2. 客户端只知道传入工厂类的参数,对于如何创建对象并不关心

    工厂方法模式

    定义

    工厂方法模式可以说是简单工厂模式的进一步抽象和拓展,在保留了简单工厂的封装优点的同时,让扩展变得简单,让继承变得可行,增加了多态性的体现

    示例

    // 抽象汽车类, 也可以是接口
    public abstract class Car 
    { 
        public string Type { get; set;}
    }
    
    // 特斯拉
    public class Tesla: Car 
    { 
        public Tesla ()
        {
            this.Type = "电动力车";
            Console.Writeline("生产特斯拉电动汽车");
        }
    }
    
    // 奔驰
    public class Benz: Car 
    { 
        public Benz ()
        {
            this.Type = "汽油动力车";
            Console.WriteLine("生产奔驰汽车");
        }
    }
    
    // 工厂接口
    public interface ICarFactory
    {
        public Car Build();
    }
    
    
    public class BenzFactory: ICarFactory
    {
        public Car Build()
        {
            return new Benz();
        }
    }
    
    public class TeslaFactory: ICarFactory
    {
        public Car Build()
        {
            return new Tesla();
        }
    }
    
    // 消费者
    public class Customer
    {
        public static void Main(String[] args)
        {
            ICarFactory factory = null;
            Car teslaCar = null;
            Car benzCar = null;
    
            // 生产 Tesla
            factory = new TeslaFactory();
            teslaCar = facotry.Build();
    
            // 生产 Benz
            factory = new BenzFactory();
            benzCar = factory.Build();
        }
    }
    
    

    Vuejs 示例

    <html>
    
    <head></head>
    
    <body>
        <div id="app">
        </div>
    
        <script src="https://unpkg.com/vue"></script>
        <script>
            var appTemplateFactory = {
                build: function () {
                    return '<p style="color:red;">Hello app </p>'
                }
            }
    
            var testTemplateFactory = {
                build: function () {
                    return '<p style="color:blue;">Hello test </p>'
                }
            }
    
            var buildApp = function () {
                var factory = testTemplateFactory; // appTemplateFactory
                var template = factory.build()
                return new Vue({
                    el: '#app',
                    template: template
                })
            }
    
            buildApp()
    
        </script>
    </body>
    
    </html>
    

    优点

    1. 符合开闭原则,容易扩展,新增一种产品时,只需要增加相应的具体产品类和相应的工厂子类即可;
    2. 符合单一职责原则,每个具体工厂类只负责创建对应的产品;

    缺点

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

    讨论

    既然是一个工厂对应一个目标对象,那我还要工厂干嘛,我直接 new 我的目标对象不就可以了

    思考以下场景:
    假如我有三个对象

    Car car1 = new Tesla();
    Car car2 = new Tesla();
    Car car3 = new Tesla();
    

    那么此时我不想要 Tesla 了,我想换成Benz,此时是不是需要修改三处(可能分散在系统各个类库里),这个时候工厂方法模式的好处在哪里体现呢?

    CarFactory factory = new TeslaFactory();
    Car car1 = factory.build();
    Car car2 = factory.build();
    Car car3 = factory.build();
    

    这个时候,要换成Benz, 就修改

    CarFactory factory = new BenzFactory();
    

    一处就可以了。

    别忘了,你在加 Teacher 的时候,是符合开闭原则的,而简单工厂模式是不符合的。

    使用场景

    1. 客户端不知道它所需要的对象的类。客户端不需要知道具体产品类的类名,只需要知道所对应的工厂即可,具体的产品对象由具体工厂类创建;
    2. 抽象工厂类通过其子类来指定创建哪个对象。对于抽象工厂类只需要提供一个创建产品的接口,而由其子类来确定具体要创建的对象

    抽象工厂模式

    定义

    提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们的具体类
    抽象工厂模式为创建一组对象提供了一种解决方案。与工厂方法模式相比,抽象工厂模式中的具体工厂不只是创建一种产品,它负责创建一族产品;
    抽象工厂模式主要能实现很多不同的类的修改。

    示例

    // 产品抽象接口
    public interface IProduct {}
    
    // 车 抽象接口
    public interface ICar: IProduct {}
    
    // 自行车 抽象接口
    public interface IBike: IProduct {}
    
    // 具体产品 - 特斯拉电动车
    public class TeslaCar: ICar 
    {
        public TeslaCar ()
        {
            Console.WriteLine("生产 Tesla 电动车");
        }
    }
    
    // 具体产品 - 特斯拉自行车
    public class TeslaBike: IBike 
    {
        public TeslaBike ()
        {
            Console.WriteLine("生产 Tesla 自行车");
        }
    }
    
    // 具体产品 - 奔驰汽车
    public class BenzCar: ICar 
    {
        public BenzCar ()
        {
            Console.WriteLine("生产 奔驰 汽油车");
        }
    }
    
    // 具体产品 - 奔驰自行车
    public class BenzBike: IBike 
    {
        public BenzBike ()
        {
            Console.WriteLine("生产 奔驰 自行车");
        }
    }
    
    // 抽象工厂
    public interface IProductFactory
    {
        ICar BuildCar ();
        IBike BuildBike ();
    }
    
    // 具体工厂类 - 特斯拉工厂
    public class TeslaProductFactory: IProductFactory
    {
        public ICar BuildCar ()
        {
            return new TeslaCar();
        }
    
        public IBike BuildBike ()
        {
            return new TeslaBike();
        }
    }
    
    // 具体工厂类 - 奔驰工厂
    public class BenzProductFactory: IProductFactory
    {
        public ICar BuildCar ()
        {
            return new BenzCar();
        }
    
        public IBike BuildBike ()
        {
            return new BenzBike();
        }    
    }
    
    // 消费者
    public class Customer
    {
        public static void Main()
        {
    
            IProductFactory factory = null;
            factory = new TeslaProductFactory();
            var car = factory.BuildCar();
            var bike = factory.BuildBike();
    
            /*
                当要修改产品,比如我们不要特斯啦的产品了,我要奔驰的,那么就只需要改动一个地方
                factory = new TeslaProductFactory(); => factory = new BenzProductFactory();
                其他地方不需要修改
            */
    
        }
    }
    

    结果

    生产 Tesla 电动车
    生产 Tesla 自行车
    生产 奔驰 汽油车
    生产 奔驰 自行车
    

    Vuejs 示例

    <html>
    
    <head></head>
    
    <body>
        <div id="app">
        </div>
    
        <script src="https://unpkg.com/vue"></script>
        <script>
            var appFactory = {
    
                buildTemplate: function () {
                    return '<div><p style="color:red;">Hello app </p><p> {{ message }} </p></div>'
                },
    
                buildData: function () {
                    return { message: 'app message'}
                }
            }
    
            var testFactory = {
    
                buildTemplate: function () {
                    return '<div><p style="color:red;">Hello test </p><p> {{ message }} </p></div>'
                },
    
                buildData: function () {
                    return { message: 'test message'}
                }
            }
    
            var factory = testFactory; // testFactory
    
            var buildApp = function () {
                var template = factory.buildTemplate()
                var data = factory.buildData()
                console.log(data)
                return new Vue({
                    el: '#app',
                    template: template,
                    data: data
                })
            }
    
            buildApp()
        </script>
    </body>
    
    </html>
    

    讨论

    每一个模式都是针对一定问题的解决方案,工厂方法模式针对的是一个产品等级结构,而抽象工厂模式针对的是多个产品等级结构。有多少个产品等级结构,就会在工厂角色中发现多少个工厂方法。每一个产品等级结构中有多少个具体的产品,就有多少个产品族,也就会在工厂等级结构中发现多少个具体工厂。总结一下我认为可以应用到抽象工厂模式的实际例子:

    1. 两种产品:特斯拉和奔驰,两种产品等级:车和自行车
    2. 两种产品:PC和MAC,两种产品等级:RAM,CPU
    3. 两种产品:水果和蔬菜,两种产品等级:南方特产,北方特产
    4. 两种产品:男人和女人,三种产品等级:黄种人,黑人,白人

    就类似这种结构的对象关系都可以用抽象工厂模式来构造。。。。。

    优点

    1. 分离接口和实现
    2. 切换产品族变的异常方便

    缺点

    1. 如果需要给整个产品族添加一个新的产品,那么就需要修改抽象工厂,这样就会导致修改所有的工厂实现类;比如增加一个滑板车(特斯拉滑板车,奔驰滑板车)

    使用场景

    为创建一组对象提供了一种解决方案
    

    总结

    • 简单工厂模式
      简单工厂类负责了所有产品的创建逻辑,当需要引进一个新的产品时,就不得不修改工厂类的产品创建逻辑,当产品类型较多时有可能造成工厂类的产品创建逻辑过于复杂,不利于系统的维护和扩展
    • 工厂方法模式
      对简单工厂模式的设计优化,简单工厂模式中如果新增一类产品,就需要修改工厂静态方法的产品创建逻辑,而使用工厂方法模式,只需要新扩展出一个新的工厂子类和产品类,使系统具有了良好的扩展性和维护性
    • 抽象工厂模式
      为创建一组(一族)对象提供了一种解决方案,与工厂方法模式相比,抽象工厂模式中的具体工厂不只是创建一种产品,它负责创建一组(一族)产品。

    本文完。

    参考资料:

  • 相关阅读:
    iOS 设计模式-委托模式
    python中时间操作总结
    list、dict、tuple的一些小操作总结
    DataFrame的构建及一些操作
    python连接mysql、oracle小例子
    sqlalchemy 映射的小例子
    crontab定时任务以及其中中文乱码问题
    vs2008试用版的评估期已经结束解决办法
    MongoDB 常用shell命令汇总
    把py文件打成exe
  • 原文地址:https://www.cnblogs.com/zanpen2000/p/7324976.html
Copyright © 2011-2022 走看看