首页 > 技术文章 > 设计模式之工厂模式

Tan-sir 2018-01-16 09:25 原文

定义

定义一个创建对象的接口,但由子类决定要实例的类是哪一个。工厂方法让类把实例推迟到子类。

设计原则

要依赖抽象,不要依赖具体的类:不能让高层组件依赖于底层组件,并且两者都应该依赖于抽象。

指导方针

  • 变量不可以持有具体类的引用:如果使用new,就会持有具体类的引用。可以用工厂来避开这样的做法。
  • 不要让类派生自具体类:如果派生自具体类,你就会依赖具体类。派生自一个抽象。
  • 不要覆盖基类中已实现的方法:如果覆盖基类已实现的方法,那么你的基类就不是一个真正适合被继承的抽象。基类中所有实现的方法,应该由所有子类共享。

抽象工厂模式

提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定的具体类。

第一次设计(识别变化)

假设我们有一个披萨店,我们需要写代码来制造一些不同类型的披萨。
下面来看看我们最开始的设计:

        Pizza orderPizza(string type)
        {
            Pizza pizza;

            if (type.Equals("cheese"))
            {
                pizza = new CheesePizza();
            }
            else if (type.Equals("greek"))
            {
                pizza = new GreekPizza();
            }
            else if (type.Equals("pepperoni"))
            {
                pizza = new PepperoniPizza();
            }
            else
            {
                pizza = new Pizza();
            }
            pizza.perpare();
            pizza.bake();
            pizza.cut();
            pizza.box();
            return pizza;
        }

再这里我们根据type传入的类型来创建对应的pizza,但这里有一个问题,当我们需要修改一些pizza种类的时候,我们就必须要修改里面的代码。比如:不在提供GreekPizza的pizza,我们就必须删除创建GreekPizza的代码。这样就不符合我们的开放-关闭原则

封装创建对象代码

下面,我们将创建pizza对象代码封装到一个单独的对象中,由这个对象专职创建pizza。

    class SimplePizzaFactory
    {
        public Pizza createPizza(string type)
        {
            Pizza pizza = null;

            if (type.Equals("cheese"))
            {
                pizza = new CheesePizza();
            }
            else if (type.Equals("greek"))
            {
                pizza = new GreekPizza();
            }
            else if (type.Equals("pepperoni"))
            {
                pizza = new PepperoniPizza();
            }
            else
            {
                pizza = new Pizza();
            }
        }
    }

重做PizzaStore类

class PizzaStore
    {
        private SimplePizzaFactory factory;

        public PizzaStore(SimplePizzaFactory factory)
        {
            this.factory = factory;
        }

        Pizza orderPizza(string type)
        {
            Pizza pizza=null;

            pizza=factory.createPizza(type);
            pizza.perpare();
            pizza.bake();
            pizza.cut();
            pizza.box();
            return pizza;
        }
    }

类图


这种简单的工厂其实并不是一个设计模式,反而比较像是一种编程习惯。

使用框架

下面我们把createPizza的方法放回PizzaStore中,但是要把它设置成"抽象方法",然后为每个区域创建不同的PizzaStore子类。让每个子类各自决定如何制造披萨。

public abstract class PizzaStore
    {
        //private SimplePizzaFactory factory;

        //public PizzaStore(SimplePizzaFactory factory)
        //{
        //    this.factory = factory;
        //}

        Pizza orderPizza(string type)
        {
            Pizza pizza = null;

           // factory.createPizza(type);
           //从工厂对象中移回PizzaStore中
            pizza = createPizza(type);
            pizza.perpare();
            pizza.bake();
            pizza.cut();
            pizza.box();
            return pizza;
        }


        //工厂对象移到这里
        public abstract Pizza createPizza(string type);
    }
    }

在这里,orderPizza会对Pizza对象做许多的事情,但并不知道究竟是哪一种披萨,这里其实就是解耦。

开一家披萨店

        public override Pizza createPizza(string type)
        {
            if (type.Equals("cheese"))
            {
                return new CheesePizza();
            }
            //根据类型不同,创建其他对象
            return null;
        }

工厂模式

所有工厂模式都用来封装对象创建的。

创建者类


Creator类是一个抽象类,它定义了一个抽象的工厂方法,让子类实现此方法制造产品。
子类可以利用createPrizza创建自己的产品。

产品类

对于上面的PizzaStore来说,Pizza就是产品。

定义工厂方法模式

工厂方法模式:定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法让类把实例化推出到子类。

依赖倒置原则

当你直接实例化一个对象时,就是在依赖它的具体类。
在代码里减少对于具体类依赖是件"好事"。这个原则就是依赖倒置原则——要依赖抽象,不要依赖具体类。
不能让高层组件(由其他底层组件定义其行为的类)赖低层组件,不管高层还是底层,两者都应该依赖与抽象。
上面示例应用工厂模式后类图:

可以看到,高层组件(PizzaStore)和底层组件都依赖了Pizza抽象。

几个指导方针帮助你遵循此原则

  • 变量不可以持有具体的引用
  • 不要让类派生自具体类
  • 不要覆盖基类中已经实现的方法

推荐阅读