設計模式:12.工廠模式及其3中實現方式

1.披薩訂購問題的普通解決方案

(1) 問題

披薩店的工作:

  • 披薩的種類很多,比如GreekPizza(希臘披薩)、CheessPizza(奶酪披薩)
  • 披薩的製作過程:prepare(準備)、bake(烘烤)、cut(切割)、box(打包)
  • 披薩店要有訂購功能
  • 要便於披薩種類的拓展和維護
(2) 普通解決方法

在這裏插入圖片描述

/** 披薩抽象類 **/
public abstract class Pizza{
	private String type;	// 披薩的類型

	//準備 抽象方法,不需要實現
	public abstract void prepare();
	//烘烤
	public void bake(){ sout(type + "烘烤中..."); }
	//切割
	public void cut(){ sout(type + "切割中..."); }
	//打包
	public void box(){ sout(type + "打包中...");}
	
	public void setType(String type){ this.type = type; }
}

/** 奶酪披薩 **/
public class CheessPizza extends Pizza{
	@Override
	public void prepare(){ sout("奶酪披薩準備中..."); }
}

/** 希臘披薩 **/
public class GreekPizza extends Pizza{
	@Override
	public void prepare(){ sout("希臘披薩準備中..."); }
}

/** 訂購披薩類 **/
public class OrderPizza{
	// 構造器
	public OrderPizza(){
		Pizza pizza = null;
		String pizzaType;	// 訂購披薩的類型
		while(true){
			pizzaType = getType();
			if("greek".equals(pizzaType)){
				pizza = new GreekPizza();
				pizza.setType("greek");
			}else if("cheess".equals(pizzaType)){
				pizza = new CheessPizza();
				pizza.setType("cheess");
			}else{
				break;
			}

			// 製作
			pizza.prepare();
			pizza.bake();
			pizza.cut();
			pizza.box();
		}
	}
	
	// 從控制檯輸入披薩種類
	public getType(){
		try{
			BufferedReader str = new BufferedReader(new InputStreamReader(System.in));
			String pizzaType = str.readLine();
			return pizzaType;
		}catch(IOException e){
			return "";
		}
	}
}

/** 使用 **/
public class Do{
	psvm(){
		// 產生訂購訂單
		new OrderPizza();
	}
}
(3) 普通解決方法的優缺點
  • 優點: 簡單易操作、容易理解
  • 缺點:違反設計模式的ocp原則:對拓展開放,對修改關閉。即當我們新增功能時,要儘量不修改代碼或少修改代碼。
    當我們新增一個披薩種類,Pepper胡椒披薩時,需要修改:
    在這裏插入圖片描述
    這裏僅僅是隻有一個披薩店的訂購功能,如果有多個披薩店的訂購功能,OrderPizza2、PrderPizza3這些類都需要修改加入判斷胡椒披薩的邏輯:
    在這裏插入圖片描述
(4) 使用設計模式優化的思路

把創建Pizza對象封裝到一個類中,這樣我們有新的Pizza種類時,只需要修改改類就可,其他用到Pizza的對象的代碼就不需要修改了。


2.簡單工廠模式

(1)基本介紹
  • 簡單工廠模式是屬於創建型模式,是工廠模式的一種。簡單工廠模式是由一個工廠對象決定創建出哪一種產品類的實例。簡單工廠模式是工廠模式中最簡單使用的模式。
  • 定義:定義了一個創建對象的類,這個類的功能是:實例化各種對象。
  • 在軟件開發中,當我們會用到大量的創建某種、某類或者某批對象時,就會使用工廠模式。
(2)改進披薩訂購功能
/** 簡單工廠模式:披薩訂單工廠類 **/
public class OrderFactory{
	// 根據type創建Pizza對象
	public Pizza createPizza(String type){
		Pizza pizza = null;
		if("greek".equals(type)){
			pizza = new GreekPizza();
			pizza.setType("greek");
		}else if("cheess".equals(type)){
			pizza = new CheessPizza();
			pizza.setType("cheess");
		}
		return pizza ;
	}
}

/** 修改訂購披薩類,把披薩訂單工廠類聚合進來 **/
public class OrderPizza{
	// 定義一個簡單工廠對象
	private OrderFactory orderFactory;
	
	// 因爲這裏是聚合關係,所以把簡單工廠模式傳進來,當然,也可以使用組合關係,直接定義簡單工廠對象的時候就new OrderFactory(),更簡單
	public void OrderPizza(OrderFactory orderFactory){
		this.orderFactory = orderFactory;
		
		while(true){
			String type = getType();
			Pizza pizza = this.orderFactory.createPizza(type);
			if(null == pizza){ break; }
			
			// 製作
			pizza.prepare();
			pizza.bake();
			pizza.cut();
			pizza.box();
		}
	}
	
	// 從控制檯輸入披薩種類
	public getType(){
		try{
			BufferedReader str = new BufferedReader(new InputStreamReader(System.in));
			String pizzaType = str.readLine();
			return pizzaType;
		}catch(IOException e){
			return "";
		}
	}
}

/** 使用 **/
public class Do{
	psvm(){
		// 產生訂購訂單
		new OrderPizza(new OrderFactory());
	}
}

在這裏插入圖片描述

(3)簡單工廠模式的另一種寫法:靜態工廠模式

就是將靜態工廠類的獲取對象的方法變成靜態方法,使用時直接調用即可,更加簡單。

/** 靜態工廠模式 **/
public class OrderFactory{
	// 根據type創建Pizza對象
	public static Pizza createPizza(String type){
		Pizza pizza = null;
		...
		return pizza ;
	}
}

/** 修改訂購披薩類,把披薩訂單工廠類聚合進來 **/
public class OrderPizza{
	// 直接調用工廠類的靜態方法獲取披薩對象即可
	public void OrderPizza(){
		...
		while(true){
			String type = getType();
			// 直接調用
			Pizza pizza = OrderFactory.createPizza(type);
			...
		}
	}
}

3.工廠方法模式

(1)披薩問題的新增功能

客戶在下單披薩時,可以選擇發貨地區,比如:北京奶酪披薩(BjCheessPizza)、北京希臘披薩(BjGreekPizza)、倫敦奶酪披薩(LdCheessPizza)、倫敦希臘披薩(LdGreekPizza)等。

(2)思路
  • 使用簡單工廠模式,創建不同的簡單工廠類:BjOrderFactory、LdOrderFactory。但是這種方案,軟件的可維護性、可拓展性並不是很好,以後還會增加新的地區。
  • 使用工廠方法模式:將披薩項目的實例化功能抽象成抽象方法,在不同地區點餐子類中具體實現。
(3)基本介紹
  • 定義了一個創建對象的抽象方法,由子類決定要實例化的類。
  • 工廠方法模式將對象的實例化推遲到子類。
  • 簡單工廠模式在工廠類中直接實例化了:OrderFactory.createPizza()中直接Pizza pizza = new GreekPizza();
(4)實現

在這裏插入圖片描述

/** 披薩抽象類 **/
public abstract class Pizza{
	private String type;	// 披薩的類型

	//準備 抽象方法,不需要實現
	public abstract void prepare();
	
	public void bake(){ sout(type + "烘烤中..."); }
	public void cut(){ sout(type + "切割中..."); }
	public void box(){ sout(type + "打包中...");}
	
	public void setType(String type){ this.type = type; }
}

/** 北京奶酪披薩 **/
public class BjCheessPizza extends Pizza{
	@Override
	public void prepare(){
		setType("BJ_Cheess");
		sout("北京_奶酪披薩準備中..."); 
	}
}

/** 北京希臘披薩 **/
public class BjGreekPizza extends Pizza{
	@Override
	public void prepare(){ 
		setType("BJ_Greek");
		sout("北京_希臘披薩準備中...");
	}
}

/** 倫敦奶酪披薩 **/
public class LdCheessPizza extends Pizza{
	@Override
	public void prepare(){
		setType("LD_Cheess");
		sout("倫敦_奶酪披薩準備中..."); 
	}
}

/** 倫敦希臘披薩 **/
public class LdGreekPizza extends Pizza{
	@Override
	public void prepare(){ 
		setType("LD_Greek");
		sout("倫敦_希臘披薩準備中...");
	}
}

/** 披薩訂購抽象類 **/
public abstract class OrderPizza{
	// 抽象方法返回Pizza對象,讓各個工廠子類自己實現
	abstract Pizza createPizza(String orderType);
	
	// 訂購
	public OrderPizza(){
		Pizza pizza = null;
		String orderPizza;
		while(true){
			orderType = getType();
			// 獲取Pizza對象,createPizza會由各個工廠子類實現
			pizza = createPizza(orderType);
			pizza.prepare();
			pizza.bake();
			pizza.cut();
			pizza.box();
		}
	}
}

/** 北京訂購 **/
public class BjOrderPizza extends OrderPizza{
	@Override
	Pizza createPizza(String orderType){
		Pizza pizza = null;
		if(orderType.equals("cheese")){
			pizza = new BjCheessPizza();
		}else if(orderType.equals("greek")){
			pizza = new BjGreekPizza();
		}
		return pizza;
	}
}

/** 倫敦訂購 **/
public class LdOrderPizza extends OrderPizza{
	@Override
	Pizza createPizza(String orderType){
		Pizza pizza = null;
		if(orderType.equals("cheese")){
			pizza = new LdCheessPizza();
		}else if(orderType.equals("greek")){
			pizza = new LdGreekPizza();
		}
		return pizza;
	}
}

/** 使用 **/
public class Do{
	psvm(){
		// 產生訂購訂單
		new BjOrderPizza();
		new LdOrderPizza();
	}
}

4.抽象工廠模式

(1)基本介紹
  • 定義一個Interface,用於創建相關或有依賴關係的對象簇,而無需指明具體的類。
  • 抽象工廠模式可以將簡單工廠模式和工廠方法模式進行整合。
  • 從設計層面看,抽象工廠模式是對簡單工廠模式的改進,進一步抽象。
  • 將工廠抽象成兩層,AbsFactory(抽象工廠)和具體實現的工廠子類。程序員可以根據創建對象類型使用對應的工廠子類。這樣將簡單的工廠類變成了工廠簇,易於維護。
(2)實現披薩項目

在這裏插入圖片描述

/** 披薩抽象類 **/
public abstract class Pizza{ ... }

/** 北京奶酪披薩 **/
public class BjCheessPizza extends Pizza{ ... }

/** 北京希臘披薩 **/
public class BjGreekPizza extends Pizza{ ... }

/** 倫敦奶酪披薩 **/
public class LdCheessPizza extends Pizza{ ... }

/** 倫敦希臘披薩 **/
public class LdGreekPizza extends Pizza{ ... }

/** 抽象工廠 **/
public interface AbsFactory{
	// 由工廠子類實現
	public Pizza createPizza(String orderType);
}

/** 北京工廠實現類 **/
public class BjFactory implements AbsFactory{
	@Override
	public Pizza createPizza(String orderType){
		Pizza pizza = null;
		if(orderType.equals("cheese")){
			pizza = new BjCheessPizza();
		}else if(orderType.equals("greek")){
			pizza = new BjGreekPizza();
		}
		return pizza;
	}
}

/** 倫敦工廠實現類 **/
public class LdFactory implements AbsFactory{
	@Override
	public Pizza createPizza(String orderType){
		Pizza pizza = null;
		if(orderType.equals("cheese")){
			pizza = new LdCheessPizza();
		}else if(orderType.equals("greek")){
			pizza = new LdGreekPizza();
		}
		return pizza;
	}
}

/** 披薩訂購店1 **/
public class OrderPizza1{
	// 將工廠抽象接口聚合進來
	private AbsFactory factory;
	
	public OrderPizza1(AbsFactory factory){
		setFactory(factory);
	}
	
	// 傳入具體的工廠實例
	public void setFactory(AbsFactory factory){
		Pizza pizza = null;
		String orderType = "";
		this.factory = factory;
		while(true){
			orderType = getType();
			pizza = factory.createPizza();
			 
			pizza.prepare();
			pizza.bake();
			pizza.cut();
			pizza.box();
		}
	}
}

/** 使用 **/
public class Do{
	psvm(){
		// 產生訂購訂單
		new OrderPizza1(new BjFactory());
		new OrderPizza1(new LdFactory());
	}
}
(3)再新增一個上海地區

在這裏插入圖片描述


5.三種方式如何選擇

  • 三種方式差別並不是很大
  • 日常開發過程中,如果實例對象很多,可以使用抽象工廠方法將對象分類創建。如果實例對象並不是那麼多,使用簡單工廠模式即可。

6.JDK源碼分析

Calendar calendar = Calendar.getInstance();

System.out.println("年:"+calendar.get(Calendar.YEAR));
System.out.println("月:"+calendar.get(Calendar.MONTH)+1);
System.out.println("日:"+calendar.get(Calendar.DAY_OF_MONTH));
System.out.println("時:"+calendar.get(Calendar.HOUR_OF_DAY));
System.out.println("分:"+calendar.get(Calendar.MINUTE));
System.out.println("秒:"+calendar.get(Calendar.SECOND));

其中Calendar.getInstance()就使用了簡單工廠模式:
在這裏插入圖片描述


7.工廠模式的意義

將實例化對象的代碼提取出來,放在一個類中統一管理和維護,達到和主項目的依賴關係的解耦,從而提高項目的擴展和維護性。


8.設計模式的依賴抽象原則

  • 創建對象實例時,不要直接new類,而是把這個new類的動作放在一個工廠的人方法中返回。即變量不要直接持有具體類的引用。
  • 不要讓類繼承具體類,而是繼承抽象類或者是實現interface。
  • 不要覆蓋基類中已經實現的方法。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章