工廠模式
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。
- 不要覆蓋基類中已經實現的方法。