工厂模式(Factory Pattern)

简单工厂

又叫做静态工厂方法(Static Factory Method)模式,但不属于 23 种设计模式之一。 简单工厂模式的实质是由一个工厂类根据传入的参数,动态决定应该创建哪一个产品类。

例子

张三准备自主创业,想开一个餐厅,由于前期资金有限只能请一个厨师,假定只出售红烧肉和土豆丝。代码如下

package com.itxiaoer.simple;

/**
 * 食物
 *
 * @author : liuyk
 */
public interface Food {
    /**
     * 名称
     */
    void name();

}

package com.itxiaoer.simple;

/**
 * 土豆丝
 *
 * @author : liuyk
 */
public class PotatoSilk implements Food {
    @Override
    public void name() {
        System.out.println("您点了土豆丝");
    }
}

package com.itxiaoer.simple;

/**
 * 红烧肉
 *
 * @author : liuyk
 */
public class RedCookedPork implements Food {

    @Override
    public void name() {
        System.out.println("您点了红烧肉");
    }
}

package com.itxiaoer.simple;

/**
 * 食物工程类
 *
 * @author : liuyk
 */
public class FoodFactory {

    public static Food create(String type) {
        switch (type) {
            case "土豆丝":
                return new PotatoSilk();
            case "红烧肉":
                return new RedCookedPork();
            default:
                throw new IllegalArgumentException(" type error");
        }
    }
}

package com.itxiaoer.simple;

import org.junit.Test;

public class TestFoodFactory {

    @Test
    public void create() {
        // 顾客想吃土豆丝
        FoodFactory.create("土豆丝").name();
        // 顾客想吃红烧肉
        FoodFactory.create("红烧肉").name();
    }
}

缺点

  • 由于工厂类(厨师)集中了所有产品的创建逻辑,一旦不能正常工作(厨师生病),将影响整个系统(餐厅)。
  • 系统(增加菜品)扩展困难,一旦添加新产品就必须要修改工厂逻辑(厨师需要进修),在产品类型比较多的时候,工厂逻辑比较复杂,不利于系统的扩展和维护(学习成本高)。
  • 简单工厂会一定程度上增加系统的类的数量,一定程度上增加系统的复杂度和理解难度。

使用场景

  • 工厂创建的类比较少,由于创建的类比较少,不会造成工厂业务过于复杂。
  • 客服端只知道工厂类型的参数,对于如何创建对象并不关心,不需要关心创建细节,甚至类名都不需要记住,只需知道类型参数。

工厂方法

工厂方法是针对每一种产品提供一个工厂类, 通过不同的工厂实例来创建不同的产品实例。

例子

张三经营了一段时间,顾客慢慢增多,一个厨师忙不过来了,于是又请了一个厨师,张三决定让一个厨师炒土豆丝,一个厨师炒红烧肉。


package com.itxiaoer.method;

/**
 * 食物
 *
 * @author : liuyk
 */
public interface Food {
    /**
     * 名称
     */
    void name();

}

package com.itxiaoer.method;

/**
 * 土豆丝
 *
 * @author : liuyk
 */
public class PotatoSilk implements Food {
    @Override
    public void name() {
        System.out.println("您点了土豆丝");
    }
}

package com.itxiaoer.method;

/**
 * 红烧肉
 *
 * @author : liuyk
 */
public class RedCookedPork implements Food {

    @Override
    public void name() {
        System.out.println("您点了红烧肉");
    }
}

package com.itxiaoer.method;

/**
 * 食物工厂类(厨师)
 *
 * @author : liuyk
 */
public interface FoodFactory {
    /**
     * 创建
     *
     * @return 食物
     */
    Food create();

}

package com.itxiaoer.method;

/**
 * 土豆丝工厂类
 *
 * @author : liuyk
 */
public class PotatoSilkFactory implements FoodFactory {
    @Override
    public Food create() {
        return new PotatoSilk();
    }
}

package com.itxiaoer.method;

/**
 * 红烧肉工厂类
 *
 * @author : liuyk
 */
public class RedCookedPorkFactory implements FoodFactory {
    @Override
    public Food create() {
        return new RedCookedPork();
    }
}

package com.itxiaoer.method;

import org.junit.Test;

public class TestFoodFactory {

    @Test
    public void create() {
       // 顾客想吃土豆丝
       new PotatoSilkFactory().create().name();
       // 顾客想吃红烧肉
       new RedCookedPorkFactory().create().name();
    }
}

缺点

  • 添加新产品时,除了增加新产品类外,还要提供与之对应的具体工厂类,系统类的个数将成对增加,在一定程度上增加了系统的复杂度;同时,有更多的类需要编译和运行,会给系统带来一些额外的开销;
  • 一个具体工厂只能创建一种具体产品
  • 虽然保证了工厂方法内的对修改关闭,但对于使用工厂方法的类,如果要更换另外一种产品,仍然需要修改实例化的具体工厂类;

优点

  • 扩展容易,新增一种新产品时,只需要增加对应的产品和工厂类即可,无需修改原有代码
  • 职责单一,每个工厂只负责一个产品的创建

使用场景

  • 当一个类不知道它所需要的对象的类时
    在工厂方法模式中,客户端不需要知道具体产品类的类名,只需要知道所对应的工厂即可; *当一个类希望通过其子类来指定创建对象时
    在工厂方法模式中,对于抽象工厂类只需要提供一个创建产品的接口,而由其子类来确定具体要创建的对象,利用面向对象的多态性和里氏代换原则,在程序运行时,子类对象将覆盖父类对象,从而使得系统更容易扩展。