一、概念 工廠方法模式:用來封裝對象的創建。工廠方法模式(Factory Method Pattern)通過讓 子類 決定該創建的對象是什麼,來達到將對象創建的過程封裝的目的。這樣,關於超類的代碼和子類創建對象的代碼之間就解耦了。 角色: 1、抽象工廠(Creator):定義了一個抽象的 ...
一、概念
- 工廠方法模式:用來封裝對象的創建。工廠方法模式(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");
}
}
三、使用抽象工廠模式重構代碼
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();
}
}
抽象工廠模式在使用時,利用對象組合和多態將不同的工廠實現類註入到代碼中。這樣,代碼只會依賴介面,根本不關心具體實現是什麼,以此達到解耦的目的。
演示源代碼:https://github.com/JMCuixy/design-patterns/tree/master/src/main/java/com/example/factory
四、總結
- 依賴倒置原則:要依賴抽象,不要依賴具體類 —— 不能讓高層組件依賴底層組件,而且,不管是高層還是底層組件,都應該依賴於抽象。想要遵循依賴倒置原則,工廠模式並非是唯一的技巧,但卻是最有威力的技巧之一。
- 優點:
1、封裝變化。將創建對象的代碼集中在一個對象或方法中,可以避免代碼的重覆。
2、面向介面編程。在實例化對象時,向客戶隱藏了實例化的細節,用戶只需要關心所需產品對應的工廠,無需關心創建細節,甚至無須知道具體產品類的類名。
- 缺點:
1、系統中類的個數會增加,在一定程度上增加了系統的複雜度,會給系統帶來一些額外的開銷。
2、由於考慮到系統的可擴展性,需要引入抽象層,增加了系統的抽象性和理解難度。 - 工廠方法模式和抽象工廠模式的區別?
1、工廠方法和抽象工廠的任務都是負責實例化對象,但是工廠方法用的方式是繼承,而抽象工廠方式用的方法是對象組合。
2、工廠方法註重於:把對象實例化的代碼從具體類中解耦出來 —— 通過子類繼承實現,或者目前還不知道將來需要實例化哪些具體類時;抽象工廠註重於:當你需要創建產品家族和想讓製造的相關產品集中起來時。