Design Pattern_Skill_4_簡單工廠模式(Simple Factory Pattern)

在軟體系統設計當中
創建型模式:主要是針對處理物件創建相關情況以某種方式控制物件實體化的設計模式
試圖就實際情況來切換較合適方法進行創建物件
主要核心思想是
將系統使用到的具體Class封裝起來
隱藏具體Class Object的建立與結合

創建型模式:
(1)物件創建型模式:將物件實體化過程部分移至另一個物件中
(2)類別創建型模式:將其物件實體化過程移置子類中

在設計過程中時常會提到要對抽象(介面或抽象類)進行程式編碼設計
而不是對具體(實際物件)進行程式編碼設計

舉例要做一個比薩 就建立一個Pizza Class
做一個麵包則建立一個 Bread Class
一直透過new來實體化物件
這是典型的一個針對具體的設計
隨著時間流逝系統程式碼量倍增情況也影響到後期維護的複雜及工作量
不管後續要做捨麼都要創建新類別實在很麻煩

此時我們想到一種方法
有沒有可能我們設計一種Class是專門負責創建物件的
就好像一座工廠要做pizza或麵包
我們只要去調用該Factory class某一個方法透過傳指定參數來實作並回傳特定物件
此模式就是所謂的「簡單工廠設計模式」

==============================================================

一家剛起步不久的Pizza店中的程式人員
負責幫忙開發pizza的系統
首先它知道pizza有如下工序
備料(準備起司絲、肉片)、
製作(稈麵團、加入蠔油)、
烘烤(統一30分鐘)、
完成(用紙箱裝盒外帶)
之後還有下訂單等業務

一開始推出口味有起司火腿、德國香腸、原味火腿口味的
所以他就直接寫了負責Pizza專屬工序的Class
對應三種不同口味的Class
還有負責訂單的Class


負責Pizza專屬工序的Class


package OrignalApp;

/**
 *
 * @author chous
 */
public class Pizza {
    public void prepare(){
        System.out.println("備料:準備起司絲、肉片");
    }
    
    public void make(){
        System.out.println("製作:稈麵團、加入蠔油");
    }
    
    public void bake(){
        System.out.println("烘烤:統一30分鐘");
    }
    
    public void complete(){
        System.out.println("完成:用紙箱裝盒外帶");
    }
}


負責訂單的Class


package OrignalApp;

/**
 *
 * @author chous
 */
public class PizzShope {
    public Pizza orderPizza(String type){
        //Pizza pizza = new Pizza();
        Pizza pizza = null;
        if(type.equalsIgnoreCase("pork")){
            pizza = new PorkPizza();
        }else if(type.equalsIgnoreCase("cheese")){
            pizza = new CheesePizza();
        }else if(type.equalsIgnoreCase("sausage")){
            pizza = new SausagePizza();
        }
        
        pizza.prepare();
        pizza.make();
        pizza.bake();
        pizza.complete();
        return pizza;
    }
}

在下訂單過程中會發現充斥一個和訂單處理無相關的判斷pizza種類的業務過程
由於pizza口味會推陳出新
後續一定會出現常常變動的可能
因此會導致處理訂單到送貨過程的程式區塊會頻繁更動
更可能會讓系統牽一髮動全身
全面影響pizza訂單處理過程

因此無論簡單工廠或工廠模式
其最初目的都在於將此段需要判斷並實體化過程在額外開一個Class
進行Encapsulate
確保單一職責
因為對於Pizza訂單處理的PizzaShop的Class它不需要去插手甚至管到pizza種類
只要專心於處理統一種類商品Pizza即可


==============================================================

(靜態)簡單工廠模式

->設計一個專門用來創建物件的Class依照傳入不同參數或是讀取組態設定來相應切換




有一天由於該Pizza店的系統由於後來程式人員退休
因此又將此案子發包給其他另一個程式人員幫忙重新設計

資深項目開發工程人員Sam就接手了
這一家Pizza製造廠的生產管理系統的案子
他們可能要有一套屬於它們一個系統
想用來控管制製作Pizza的種類和資料可能會做一些生產製程自動化

經過了第一輪開會
你跟Pizza店家的企劃和業務等人員討論Pizza的作法流程大概會有捨麼SOP
可能有要先備料、製作(擀麵、揉麵團、起司、番茄醬、調味)
烘烤、完成可能有這幾個階段吧!
但沒有很具體講到比方要有捨麼口味呀、調味放幾匙、考幾分鐘啦等等
都很抽象~
都很抽象~
都非常的抽象~

感覺後續還有很多未知和變化的可能
但我至少知道基本應該有捨麼流程

Pizza
public abstract class Pizza {
    
    public abstract void prepare();
    
    public abstract void make();
    
    public abstract void bake();
    
    public abstract void complete();
}


到了後期二、三次會議
找來了大廚跟業務他們說要
做肉類Pizza和素食者也可以吃的蔬食Pizza

蔬食Pizza
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package factorysample;

/**
 *
 * @author chous
 */
public class PizzaVegetable extends Pizza {

    @Override
    public void prepare() {
        System.out.println("為蔬食類Pizza備料");
    }

    @Override
    public void make() {
        System.out.println("製作蔬食類Pizza");
    }

    @Override
    public void bake() {
        System.out.println("烘烤蔬食類Pizza");
    }

    @Override
    public void complete() {
        System.out.println("蔬食類Pizza完成");
    }
    
}


肉類Pizza
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package factorysample;

/**
 *
 * @author chous
 */
public class PizzaMeat extends Pizza {

    @Override
    public void prepare() {
        System.out.println("為肉類Pizza備料");
    }

    @Override
    public void make() {
        System.out.println("製作肉類Pizza");
    }

    @Override
    public void bake() {
        System.out.println("烘烤肉類Pizza");
    }

    @Override
    public void complete() {
        System.out.println("肉類Pizza完成");
    }
    
}

之後進行到製造工廠運行
終於開始要製造Pizza了
工廠也開始運作了
我們需要一個工廠去幫我們生成這些不同種類的pizza



Pizza工廠(簡單版)
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package factorysample;

/**
 *
 * @author chous
 */
public class SimplePizzaFactory {
    public Pizza createPizza(String pizzaType){
        if(pizzaType.equals("VEGE")){
            return new PizzaVegetable();
        }else if(pizzaType.equals("MEAT")){
            return new PizzaMeat();
        }else{
            return null;
        }
    }
}



既然Pizza都已經有工廠製造可以幫我們產出一堆Pizza
那就可以拿去Pizza店賣了


Pizza店
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package factorysample;

/**
 *
 * @author chous
 */
public class PizzaStore {
    private SimplePizzaFactory spFactory;
    
    public PizzaStore(SimplePizzaFactory spFactory){
        this.spFactory = spFactory;
    }
    
    public void orderPizza(String typePizza){
        System.out.println("客戶開始訂購Pizza~種類為:" + typePizza);
        Pizza pizza = spFactory.createPizza(typePizza);
        pizza.prepare();
        pizza.make();
        pizza.bake();
        pizza.complete();
    }
    
}



基本上我們在之後用戶端做開發時就能夠很便利
透過傳入指定好的參數去套用不同種類Pizza會跑的製造流程
因此開發端只需要知道對應Class相應參數字串即可




缺點就是你的工廠會因為又有新口味而要做新增的更新
因此弊端就在於目前簡單工廠Class存有易變性
由於是Hard code配判斷式寫死的因而不具彈性

再加上若今天Pizza有一百種
那想必if 的判別程式切換就會很大一串不好維護
也會造成Pizza不同種類的sub class 太多個的問題









留言

這個網誌中的熱門文章

經得起原始碼資安弱點掃描的程式設計習慣培養(五)_Missing HSTS Header

經得起原始碼資安弱點掃描的程式設計習慣培養(三)_7.Cross Site Scripting(XSS)_Stored XSS_Reflected XSS All Clients

(2021年度)駕訓學科筆試準備題庫歸納分析_法規是非題