工廠方法和抽象工廠模式.


一、概念

  • 工廠方法模式:用來封裝對象的創建。工廠方法模式(Factory Method Pattern)通過讓子類決定該創建的對象是什么,來達到將對象創建的過程封裝的目的。這樣,關於超類的代碼和子類創建對象的代碼之間就解耦了。
  • 抽象工廠模式(Abstract Factory Pattern):提供一個接口,用來創建相關或依賴對象的家族,而不需要明確指定具體類。這樣,產品創建的過程只會依賴於接口,而不關心具體的實現是什么,從而達到解耦的目的。
  • 角色 - 工廠方法模式:
     1、抽象工廠(Creator):定義了一個抽象的工廠方法,讓子類實現此方法制造產品。
     2、具體工廠(ConcreteCreator):實現抽象工廠方法,包含具體生產產品的實現代碼,返回一個產品的實例。
     3、抽象產品(Product):定義一個抽象的產品,為了工廠中創建產品類型的匹配。
     4、具體產品(ConcreteProduct):實現抽象產品,包含各自產品的特色代碼。
  • 角色 - 抽象工廠模式:
     1、抽象工廠(Creator):一般是接口或抽象類,定義了一系列的產品家族。
     2、具體工廠(ConcreteCreator):實現抽象工廠方法,包含一系列產品家族的實現代碼。
     3、抽象產品(Product):定義一個抽象的產品,為了工廠中創建產品類型的匹配。
     4、具體產品(ConcreteProduct):實現抽象產品,包含各自產品的特色代碼。

二、Demo 實現 - 工廠方法模式

TOPIC:我們要定義一個披薩店,並允許個人或機構加盟,而且個人或機構可以根據當地不同的口味生產不同的披薩。

1、抽象工廠 - PizzaStore.java

public abstract class PizzaStore {

    public Pizza orderPizza(String type) {
        Pizza pizza = createPizza(type);
        System.out.println(pizza.name);
        pizza.bake();
        pizza.cut();
        pizza.box();
        return pizza;
    }
    
    protected abstract Pizza createPizza(String type);
}

這里,我們定義了一個抽象工廠角色,並定義了一個抽象工廠方法。這樣產品(Pizza)的實例化過程會交由抽象工廠的子類 —— 具體工廠去創建。

2、抽象產品 - Pizza.java

public abstract class Pizza {
    /**
     * 披薩的名字
     */
    protected String name;

    protected void bake() {
        System.out.println("烘烤25分鍾...");
    }

    protected void cut() {
        System.out.println("把匹薩沿着對角線切開...");
    }

    protected void box() {
        System.out.println("包裝用官方標准的盒子...");
    }
}

這里,我們定義了一個抽象產品角色,之所以把產品定義成抽象的,有兩個原因:一是為了在具體工廠中生產的產品可以用統一的抽象父類來接收;二是為了讓加盟商可以任意的擴展自己的產品。這種設計符合我們的設計原則 —— 面向接口(或抽象)編程,不面向實現編程。

3、具體產品

現在有加盟商要加盟我們的商店,他希望能夠按照他們的地方特色,自定義他們要出售的披薩...那么,只要擴展 Pizza 類就可以了!

public class CheesePizza extends Pizza {
    public CheesePizza() {
        name = "這是一個奶酪披薩";
    }
    @Override
    protected void bake() {
        System.out.println("烘烤30分鍾...");
    }
    @Override
    protected void cut() {
        System.out.println("把匹薩按四等分切開...");
    }
    @Override
    protected void box() {
        System.out.println("包裝用奶酪披薩特制的盒子...");
    }
}
public class DurianPizza extends Pizza {
    public DurianPizza() {
        name = "這是一個榴蓮披薩";
    }
    @Override
    protected void bake() {
        System.out.println("烘烤45分鍾...");
    }
    @Override
    protected void cut() {
        System.out.println("把匹薩按三等分切開...");
    }
    @Override
    protected void box() {
        System.out.println("包裝用榴蓮披薩特制的盒子...");
    }
}

4、具體工廠 - PizzaFactory.java

public class PizzaFactory extends PizzaStore {
    
    @Override
    protected Pizza createPizza(String type) {
        switch (type) {
            case "cheese":
                return new CheesePizza();
            case "durian":
                return new DurianPizza();
            default:
                break;
        }
        return null;
    }
}

這里,我們定義了一個具體工廠角色,繼承自抽象工廠,產品(Pizza)的具體實例化代碼在這里實現。

5、測試

public class Test {

    public static void main(String[] args) {
        PizzaStore store = new PizzaFactory();
        store.orderPizza("cheese");
        store.orderPizza("durian");
    }
}

avatar

三、使用抽象工廠模式重構代碼

1、抽象工廠 - AbstractFactory.java

public interface AbstractFactory {
    /**
     * @Description 奶酪披薩制造接口
     */
    Pizza createCheese();

    /**
     * @Description 榴蓮披薩制造接口
     */
    Pizza createDurian();
}

這里,我們定義了一個抽象工廠角色,我們用接口定義了一系列的產品家族。

2、具體工廠

public class AFactory implements AbstractFactory {

    @Override
    public Pizza createCheese() {
        System.out.println("A工廠制造的奶酪披薩");
        return new CheesePizza();
    }

    @Override
    public Pizza createDurian() {
        System.out.println("A工廠制造的榴蓮披薩");
        return new DurianPizza();
    }
}
public class BFactory implements AbstractFactory {

    @Override
    public Pizza createCheese() {
        System.out.println("B工廠制造的奶酪披薩");
        return new CheesePizza();
    }

    @Override
    public Pizza createDurian() {
        System.out.println("B工廠制造的榴蓮披薩");
        return new DurianPizza();
    }
}

這是兩個具體的工廠類,實現了抽象工廠接口,包含了一系列產品家族的實現代碼。

3、抽象產品和具體產品

抽象產品和具體產品的代碼和工廠方法模式一致,這里就不加贅述了,來看看抽象工廠的使用吧!

4、測試

public class PizzaStore {

    private AbstractFactory abstractFactory;

    public PizzaStore(AbstractFactory abstractFactory) {
        this.abstractFactory = abstractFactory;
    }

    public void prepare() {
        this.abstractFactory.createCheese();
        this.abstractFactory.createDurian();
    }
}
public class Test {

    public static void main(String[] args) {
        AbstractFactory abstractFactory = new AFactory();
        PizzaStore pizzaStore = new PizzaStore(abstractFactory);
        pizzaStore.prepare();
        AbstractFactory abstractFactory2 = new BFactory();
        PizzaStore pizzaStore2 = new PizzaStore(abstractFactory2);
        pizzaStore2.prepare();
    }
}

抽象工廠模式在使用時,利用對象組合和多態將不同的工廠實現類注入到代碼中。這樣,代碼只會依賴接口,根本不關心具體實現是什么,以此達到解耦的目的。

avatar

演示源代碼:https://github.com/JMCuixy/design-patterns/tree/master/src/main/java/com/example/factory

四、總結

  • 依賴倒置原則:要依賴抽象,不要依賴具體類 —— 不能讓高層組件依賴底層組件,而且,不管是高層還是底層組件,都應該依賴於抽象。想要遵循依賴倒置原則,工廠模式並非是唯一的技巧,但卻是最有威力的技巧之一。
  • 編寫模塊的實現依賴於抽象,在運行時傳入具體的實現細節, 這就是依賴倒置的工作原理。依賴倒置原則教我們盡量避免使用具體類,而多使用抽象。
  • 依賴倒置原則的目的是讓程序員脫離底層粘合代碼,編寫上層業務邏輯代碼。這就讓上層代碼依賴於底層細節的抽象,從而可以重用上層代碼。這種模塊化和重用方式是雙向的:既可以替換不同的細節重用上層代碼,也可以替換不同的業務邏輯重用細節的實現。
  • 優點:
     1、封裝變化。將創建對象的代碼集中在一個對象或方法中,可以避免代碼的重復。
     2、面向接口編程。在實例化對象時,向客戶隱藏了實例化的細節,用戶只需要關心所需產品對應的工廠,無需關心創建細節,甚至無須知道具體產品類的類名。
  • 缺點:
     1、系統中類的個數會增加,在一定程度上增加了系統的復雜度,會給系統帶來一些額外的開銷。
     2、由於考慮到系統的可擴展性,需要引入抽象層,增加了系統的抽象性和理解難度。
  • 工廠方法模式和抽象工廠模式的區別?
     1、工廠方法和抽象工廠的任務都是負責實例化對象,但是工廠方法用的方式是繼承,而抽象工廠方式用的方法是對象組合。
     2、工廠方法注重於:把對象實例化的代碼從具體類中解耦出來 —— 通過子類繼承實現,或者目前還不知道將來需要實例化哪些具體類時;抽象工廠注重於:當你需要創建產品家族和想讓制造的相關產品集中起來時。


免責聲明!

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



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