创建型模式之工厂模式

in HandbookDesign Patterns with 15 comments, viewed 205528 times

1 概述

创建型模式,提供了一种创建对象的最佳实践。工厂方法模式(Factory Method Pattern)的核心思想,是通过统一的工厂类来获取对象,而不需要调用类的构造方法。

2 优点

  1. 可以将类的实例化过程延缓到子类。调用者无需知道接口/抽象类的具体实现是什么,利用工厂方法即可获取类的实例,降低与调用者的耦合度。
  2. 隐藏类的构造细节,降低类创建的复杂度,提高程序可读性。
  3. 可以根据不同环境/参数,从工厂构造不同的方法。

3 案例

有一个饭店的接口,饭店里有厨师和服务员。我们定义KFCPizzaHut两个饭店:

interface Restaurant {
    void getCook();
    void getWaiter();
}

public class KFC implements Restaurant {

    @Override
    public void getCook() {
        System.out.println("I'm KFC cook.");
    }

    @Override
    public void getWaiter() {
        System.out.println("I'm KFC waiter.");
    }
}

class PizzaHut implements Restaurant {

    @Override
    public void getCook() {
        System.out.println("I'm PizzaHut cook.");
    }

    @Override
    public void getWaiter() {
        System.out.println("I'm PizzaHut waiter.");
    }
}

3.1 工厂类型1

再定义一个工厂方法RestaurantFactory,从工厂中,很容易就能根据类型获取对应的饭店:

public class Test {
    public static void main(String[] args) {
        RestaurantFactory factory = new RestaurantFactory();
        Restaurant kfc = factory.createRestaurant(RestaurantFactory.RestaurantType.KFC);
        Restaurant pizzaHut = factory.createRestaurant(RestaurantFactory.RestaurantType.PizzaHut);
        kfc.getCook();
        pizzaHut.getWaiter();
    }
}

class RestaurantFactory {
    enum RestaurantType {
        KFC, PizzaHut
    }

    Restaurant createRestaurant(RestaurantType type) {
        switch (type) {
            case KFC: return new KFC();
            case PizzaHut: return new PizzaHut();
            default: System.out.format("Invalid restaurant %s", type); return null;
        }
    }
}

输出:

I'm KFC cook.
I'm PizzaHut waiter.

UML:
UML for factory type1

比如Spring中的BeanFactory使用的就是这种模式:getBean方法用Bean类型/名字作为参数,返回对应的Bean
JDK中的Calendar类,也是用的这种模式:

public static Calendar getInstance(TimeZone zone, Locale aLocale) {
    ...
    // 根据参数创建不同的Calendar实例
    if (aLocale.hasExtensions()) {
        String caltype = aLocale.getUnicodeLocaleType("ca");
        if (caltype != null) {
            switch (caltype) {
            case "buddhist":
            cal = new BuddhistCalendar(zone, aLocale);
                break;
            case "japanese":
                cal = new JapaneseImperialCalendar(zone, aLocale);
                break;
            case "gregory":
                cal = new GregorianCalendar(zone, aLocale);
                break;
            }
        }
    }
    ...
}

如果实例类型相对固定,那么上述模式能很好的满足需求。但是如果实例类型不确定,那么每当需要新增类型的时候,都需要改动原先的方法,对开闭原则遵循得不好。于是有了第二种类型。

3.2 工厂类型2

依然是饭店的例子,我们通过如下方式定义工厂:

public class Test {
    public static void main(String[] args) {
        KFCFactory kfcFactory = new KFCFactory();
        PizzaHutFactory pizzaHutFactory = new PizzaHutFactory();
        Restaurant kfc = kfcFactory.createRestaurant();
        Restaurant pizzaHut = pizzaHutFactory.createRestaurant();
        kfc.getCook();
        pizzaHut.getWaiter();
    }
}

class KFCFactory {
    Restaurant createRestaurant() {
        return new KFC();
    }
}

class PizzaHutFactory {
    Restaurant createRestaurant() {
        return new PizzaHut();
    }
}

输出:

I'm KFC cook.
I'm PizzaHut waiter.

UML:
UML for factory type2

上述方式,每新增一个类别,只需要新增一个对应的工厂即可,原先的工厂方法以及实例无需做任何修改。如LoggerFactory)就是这种类型的工厂模式:

public static Logger getLogger(String name) {
    // getLogger最终是委托给ILoggerFactory去做的。
    // 新增Logger获取方式,只需新增ILoggerFactory的实现类,扩展性很强。
    ILoggerFactory iLoggerFactory = getILoggerFactory();
    return iLoggerFactory.getLogger(name);
}

但是这种方式,对每一种类别,都要有一个工厂类,复杂度相对较高。实际中,还是第一种类型使用比较频繁。

4 总结

工厂方法模式是使用很广泛的一种创建型模式,几乎能在所有的开源框架中见到。很典型的特点就是,工厂类以Factory字样结尾:-)。

文中例子的github地址

Responses
  1. Game LIFE 遊戲情報

    https://gamelife.tw/portal.php

    Reply
  2. 日本片商合法授權 極道光碟屋 台中AV女優光碟店 日本片商合法授權 – AV女優光碟.名器專賣(未滿18歲禁止)

    http://avgood-store.com/

    Reply
  3. 點子數位科技有限公司

    https://spot-digital.com.tw/

    Reply
  4. XYZ專業光碟教學網站

    http://xyz.net.tw/

    Reply
  5. ロレックス デイトナ レプリカn 級 品 おすすめ サイトロレックス デイトナ レプリカs 級 スーパー コピーrolex 偽物 通販

    Reply
  6. アメ横 ロレックス コピーアメ横 ロレックス コピーロレックス 激安 怪しいロレックス スーパー コピー 買っ て みた

    Reply
  7. XYZ 國中基測

    http://xyz.net.tw/

    Reply
  8. ロレックス M116515LN レプリカロレックス M116506 偽物 116519ln スーパーコピーサブマリーナ コピー 激安

    Reply
  9. gucci風 ベルトgucciベルトコピー通販 ヴィトン ベルト コピールイ ヴィトン ベルト 激安

    Reply
  10. ロレックス 新作rolex デイトナコピーコスモグラフデイトナコピーコスモグラフデイトナコピー

    Reply
  11. エルメス iphone ケース 偽物gucci iphone ケース 偽物iphone ケース ブランド コピー

    Reply
  12. n 級 品 時計 ロレックス 偽物 買っ て み

    Reply
  13. 腕時計 レプリカ ロレックス コスモ グラフ デイトナ コピー 新品仕上げ OH済 ROLEX ロレックス DATEJUST デイトジャスト 16233G X番 10Pダイヤ SS 自動巻

    Reply
  14. outlets ロレックス 偽物 ロレックス 偽物 通販 url:https://www.iy304.com/111.html

    Reply
  15. 歐客佬精品咖啡

    https://blog.oklaocoffee.tw/

    Reply