工廠模式詳解


1.1工廠模式的由來

現實生活中,原始社會(沒有工廠)--> 農耕小作坊(簡單工廠)--> 工業革命(工廠方法)--> 代工廠(抽象工廠),我們的項目代碼同樣也是由簡至繁一步一步迭代而來的,但對於調用者,卻越來越簡單。在日常開發中,凡是需要生成復雜對象的地方,都可以嘗試考慮使用工廠模式來代替。其中復雜對象指的是類的構造函數參數過多等對類的構造有影響的情況下,我們將創建對象的過程抽離出來,減少代碼中的耦合以及后期我們的維護成本。

1.2工廠模式的分類

工廠模式按照實際業務場景進行划分,有3種不同的實現方式分別是簡單工廠模式、工廠方法模式和抽象工廠模式。

2.1簡單工廠模式

2.1.1 簡單工廠模式的定義

簡單工廠模式(Simple Factory Pattern)又叫做靜態工廠方法(Static Factory Method Pattern)模式,簡單來說,簡單工廠模式有一個具體的工廠類,可以生成多個不同的產品,屬於創建型設計模式。

2.1.2 簡單工廠模式的應用場景

對於一個產品種類相對較少,考慮使用簡單工廠模式可以很方便的創建所需要的產品。使用簡單工廠模式的客戶端是需要傳入工廠類的參數,由工廠完成創建對象的邏輯。

2.1.3 簡單工廠模式的通用寫法

類圖

public interface IProduct {
    void doSomething();
}
public class ProductA implements IProduct{
    @Override
    public void doSomething() {
        System.out.println("Product A");
    }
}
public class ProductB implements IProduct{
    @Override
    public void doSomething() {
        System.out.println("Product B");
    }
}
public class ProductC implements IProduct{
    @Override
    public void doSomething() {
        System.out.println("Product C");
    }
}
public class SimpleFactory {
    public SimpleFactory() {
    }

    public static IProduct makeProduct(String type){
        switch (type){
            case "A":
                return new ProductA();
            case "B":
                return new ProductB();
            case "C":
                return new ProductC();
            default:
                System.out.println("不支持當前類型");
                break;
        }
        return null;
    }
}

2.1.4 簡單工廠模式封裝產品的創建細節

類圖

public interface Animal {
    void eatFood();
}
public class Cat implements Animal {
    @Override
    public void eatFood() {
        System.out.println("貓吃貓糧");
    }
}
public class Dog implements Animal {
    @Override
    public void eatFood() {
        System.out.println("狗吃狗糧");
    }
}
public class AnimalFactory {
    public static Animal create(String name){
        if ("cat".equals(name)){
            return new Cat();
        }else if ("dog".equals(name)){
            return new Dog();
        }else {
            return null;
        }
    }
}

思考分析:這樣雖然簡化了調用者的調用方式,但如果增加更多的動物種類,每次都需要取維護工廠類的create方法,這不符合開閉原則,因此我們采用反射繼續對代碼優化。

public static Animal create(String classname) {
     if (!(null == classname || "".equals(classname))) {
         try {
             return (Animal) Class.forName(classname).newInstance();
         } catch (Exception e) {
             e.printStackTrace();
         }
     }
     return null;
 }
public class SimpleFactoryTest {
    public static void main(String[] args) {
        AnimalFactory.create("org.example.simplefactory2.cat");
    }
}

這樣的話,即使動物種類增加,工廠類代碼也不需要變化,只是代碼中的入參為類名路徑,可控性差,繼續優化。

  public static Animal create(Class<? extends Animal> clazz){
        try{
            if (null != clazz){
             return clazz.newInstance();
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }
public class SimpleFactoryTest {
    public static void main(String[] args) {
        AnimalFactory.create(Cat.class);
    }
}

2.1.5 簡單工廠模式在框架源碼中的應用

  • 簡單工廠在JDK源碼中舉例(Calendar)
 private static Calendar createCalendar(TimeZone zone,
                                           Locale aLocale)
    {
        CalendarProvider provider =
            LocaleProviderAdapter.getAdapter(CalendarProvider.class, aLocale)
                                 .getCalendarProvider();
        if (provider != null) {
            try {
                return provider.getInstance(zone, aLocale);
            } catch (IllegalArgumentException iae) {
                // fall back to the default instantiation
            }
        }

        Calendar cal = null;

        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;
                }
            }
        }
        if (cal == null) {
            // If no known calendar type is explicitly specified,
            // perform the traditional way to create a Calendar:
            // create a BuddhistCalendar for th_TH locale,
            // a JapaneseImperialCalendar for ja_JP_JP locale, or
            // a GregorianCalendar for any other locales.
            // NOTE: The language, country and variant strings are interned.
            if (aLocale.getLanguage() == "th" && aLocale.getCountry() == "TH") {
                cal = new BuddhistCalendar(zone, aLocale);
            } else if (aLocale.getVariant() == "JP" && aLocale.getLanguage() == "ja"
                       && aLocale.getCountry() == "JP") {
                cal = new JapaneseImperialCalendar(zone, aLocale);
            } else {
                cal = new GregorianCalendar(zone, aLocale);
            }
        }
        return cal;
    }
  • 簡單工廠在Logback源碼中的應用
    Logback中的LoggerFactory,根據入參不同存在多個重載的getLogger方法
public static Logger getLogger(String name) {
        ILoggerFactory iLoggerFactory = getILoggerFactory();
        return iLoggerFactory.getLogger(name);
    }

    public static Logger getLogger(Class<?> clazz) {
        Logger logger = getLogger(clazz.getName());
        if (DETECT_LOGGER_NAME_MISMATCH) {
            Class<?> autoComputedCallingClass = Util.getCallingClass();
            if (autoComputedCallingClass != null && nonMatchingClasses(clazz, autoComputedCallingClass)) {
                Util.report(String.format("Detected logger name mismatch. Given name: \"%s\"; computed name: \"%s\".", logger.getName(), autoComputedCallingClass.getName()));
                Util.report("See http://www.slf4j.org/codes.html#loggerNameMismatch for an explanation");
            }
        }

        return logger;
    }

2.2 工廠方法模式

2.2.1 工廠方法模式的定義

工廠方法模式(Factory Method Pattern)又叫作多態性工廠模式,指定義一個創建對象的接口,但由實現這個接口的類來決定實例化哪個類,工廠方法把類的實例化推遲到子類中進行。
也就是說,工廠方法模式中,不在由工廠類生產對應的產品,而是由工廠類的子類實現具體產品的生產邏輯,解決簡單工廠中switch case的臃腫,也符合開閉原則。

2.2.2 工廠方法模式的應用場景

  • 創建對象需要大量重復代碼
  • 應用層不依賴產品實例的創建細節
  • 一個類通過起子類指定創建哪個對象實例

2.2.3 工廠模式的通用寫法

public interface IFactory {
    IProduct makeProduct();
}
public class FactoryB implements IFactory{
    @Override
    public IProduct makeProduct() {
        return new ProductB();
    }
}
public class FactoryA implements IFactory{

    @Override
    public IProduct makeProduct() {
        return new ProductA();
    }
}

工廠方法模式主要是解決產品的擴展問題,當需要增加新產品時,我們只需要新增類去實現工廠接口,做到工廠職責的單一化,符合單一職責原則。

2.2.4 工廠方法模式在框架中的應用

  • 工廠方法模式在Logback中的應用
public interface ILoggerFactory {
    Logger getLogger(String var1);
}
public class NOPLoggerFactory implements ILoggerFactory {
    public NOPLoggerFactory() {
    }

    public Logger getLogger(String name) {
        return NOPLogger.NOP_LOGGER;
    }
}
.....

2.3 抽象工廠模式

2.3.1 抽象工廠模式概述

抽象工廠模式(Abstract Factory Pattern)指提供一個創建一系列相關或相互依賴對象的接口,無需指定他們具體的類。客戶端不需要指定產品的具體類型,創建多個產品族中的產品對象。

2.3.2 抽象工廠的應用場景

抽象工廠模式適用於需要生成產品族的情景。抽象產品類內部提供了多個其他抽象產品,抽象工廠提供了多個具體的工廠子類,可以生產相應的產品族對象。

2.3.3 抽象工廠的通用寫法

類圖

public interface IAbstractFactory {
    IProductA makeProductA();

    IProductB makeProductB();
}
public interface IProductA {
    void doA();
}
public interface IProductB {
    void doB();
}
public class ProductA1 implements IProductA {
    @Override
    public void doA() {
        System.out.println("A1 屬於 A");
    }
}
public class ProductA2 implements IProductA{
    @Override
    public void doA() {
        System.out.println("A2 屬於 A");
    }
}
public class ProductB1 implements IProductB{
    @Override
    public void doB() {
        System.out.println("B1 屬於 B");
    }
}
public class ProductB2 implements IProductB{
    @Override
    public void doB() {
        System.out.println("B2 屬於 B");
    }
}
public class Factory1 implements IAbstractFactory{
    @Override
    public IProductA makeProductA() {
        return new ProductA1();
    }

    @Override
    public IProductB makeProductB() {
        return new ProductB1();
    }
}
public class Factory2 implements IAbstractFactory{
    @Override
    public IProductA makeProductA() {
        return new ProductA2();
    }

    @Override
    public IProductB makeProductB() {
        return new ProductB2();
    }
}

2.3.4 抽象工廠在框架中的使用

  • 抽象工廠在Spring中的使用(BeanFactory)
public interface BeanFactory {
    String FACTORY_BEAN_PREFIX = "&";

    Object getBean(String var1) throws BeansException;

    <T> T getBean(String var1, Class<T> var2) throws BeansException;

    <T> T getBean(Class<T> var1) throws BeansException;

    Object getBean(String var1, Object... var2) throws BeansException;

    <T> T getBean(Class<T> var1, Object... var2) throws BeansException;

    boolean containsBean(String var1);

    boolean isSingleton(String var1) throws NoSuchBeanDefinitionException;

    boolean isPrototype(String var1) throws NoSuchBeanDefinitionException;

    boolean isTypeMatch(String var1, ResolvableType var2) throws NoSuchBeanDefinitionException;

    boolean isTypeMatch(String var1, Class<?> var2) throws NoSuchBeanDefinitionException;

    Class<?> getType(String var1) throws NoSuchBeanDefinitionException;

    String[] getAliases(String var1);
}

在Spring中,所有工廠都是BeanFactory的子類,BeanFactory根據不同的策略調用getBean,獲取不同的對象。

2.3.5 優缺點分析

優點:

  • 當需要擴展產品族時,只需要增加新的具體工廠的實現類,不需要對已有代碼進行修改,符合開閉原則。
  • 對於客戶端,只需要調用同一個產品的產品族。
    缺點:
  • 產品族中擴展新產品困難,需修改抽象工廠接口。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM