工厂模式

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

本文使用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. 如果需要给整个产品族添加一个新的产品,那么就需要修改抽象工厂,这样就会导致修改所有的工厂实现类;比如增加一个滑板车(特斯拉滑板车,奔驰滑板车)

使用场景

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

总结

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

本文完。

参考资料:

posted @ 2017-08-09 14:05  DebugLife  阅读(416)  评论(0编辑  收藏  举报