工廠模式
工廠模式
class Form{
public Form(){
if(k == 1){
Ui ui = new Ui1();
}
else if(k == 2){
Ui ui = new Ui2();
}
else{
Ui ui = new Ui3();
}
ui.setPosition(10, 40);
ui.setColor(10, 40, 210);
if(k == 1){
Draw draw = new Draw1();
}
else if(k == 2){
Draw draw = new Draw2();
}
else{
Draw draw = new Draw3();
}
draw.draw();
}
}
上述很亂的程式在產生ui並設定位置
產生工具並畫圖
new 用得好好的,為什麼要使用工廠模式
- 耦合高
在程式裡面有太多實例化,會讓程式的耦合變高
這個Form還要維護這些物件的實例化 - 拒絕修改
好不容易把這麼亂的程式碼開發完了
有一天被告知,需要修改功能
有新的ui要加入
還是要打開這亂成一坨的程式碼,冒著失誤的風險 - 統整性
如果只有這邊有使用這些程式碼,已經很煩
如果好多地方都要修改,那一定是一場地獄
最不能接受的還是會一直變動的程式碼
最好的情況就是開發完那就封存
我們先把上面這一份程式碼會變動的部分找出來
封裝起來
// !!這個區域
if(k == 1){
Ui ui = new Ui1();
}
else if(k == 2){
Ui ui = new Ui2();
}
else{
Ui ui = new Ui3();
}
// !!這個區域
ui.setPosition(10, 40);
ui.setColor(10, 40, 210);
上面那段實例化的程式碼是讓我們程式很亂的原因
修改是必須的
上面的Form就是使用者
而且可能有更多使用者
可能你的同事、朋友、還是其他人
每次碰到這段程式碼都要維護很不現實的
簡單工廠模式
public class UiFactory{
public Ui createUi(int type){
if(type == 1){
Ui ui = new Ui1();
}
else if(type == 2){
Ui ui = new Ui2();
}
else{
Ui ui = new Ui3();
}
return ui;
}
}
這樣子就可以先解決眾多使用者們的問題
這個類別也不只只能產生Ui物件
例如有些有多語系的問題
也可以在這邊都弄完
並且幫助用戶把令人繁雜的實例化程式清除了
靜態的簡單工廠
public class UiFactory{
public static Ui createUi(int type){
if(type == 1){
Ui ui = new Ui1();
}
else if(type == 2){
Ui ui = new Ui2();
}
else{
Ui ui = new Ui3();
}
return ui;
}
}
這樣寫也可以
這樣可以確保你在任何時刻都可以直接使用工廠
不用實例化工廠
缺點就是失去了彈性
你無法繼承createUi的行為,做出更符合你想要的行為
簡單工廠的不足之處
簡單工廠非常方便好用,但其實它不是設計模式
只是很直覺常用的寫法
現在的UI不可能只有一種風格
可能有fancy花俏的,古典classical
public class FancyUiFactory{
public Ui createUi(int type){
if(type == 1){
Ui ui = new FancyUi1();
}
else if(type == 2){
Ui ui = new FancyUi2();
}
else{
Ui ui = new FancyUi3();
}
return ui;
}
}
FancyUiFactory fancyUiFactory = new FancyUiFactory();
ClassicalUiFactory classicalUiFactory = new ClassicalUiFactory();
直接分別定義兩個簡單工廠的類別,仔細思考,缺少一種範本Template的感覺
if(uiType == "fancy){
ui = fancyUiFactory(k);
}
else{
ui = fancyUiFactory(k);
}
ui.setPosition(10, 40);
ui.setColor(10, 40, 210);
ui.delete();
ui.changeColor(255, 40, 210);
想要標準化這個流程
可以讓不同的Ui風格都可以套用這個template
而且還有一定的流程控管,這樣才可以成為一個稱職的工廠
標準化流程
public abstract class UiFactory{
public Ui getUi(int type, int[] position, int[] color){
Ui ui = createUi(type);
ui.setPosition(position);
ui.setColor(color);
return ui;
}
abstract Ui createUi(int type);
}
把工廠改成抽象類就可以去執行Template的任務了
因為抽象類不可以實例化,所以你必須去繼承類別
並且createUi 也是抽象方法,為什麼? 可以強迫繼承的子類別去實作這個方法
子類別決定是什麼風格的UI
public class FancyUiFactory extends UiFactory{
public Ui createUi(int type){
if(type == 1){
Ui ui = new FancyUi1();
}
else if(type == 2){
Ui ui = new FancyUi2();
}
else{
Ui ui = new FancyUi3();
}
return ui;
}
}
這樣不管是Fancy還是Classical或其他等等
都可以實作自己的方法,產生相對應風格的UI
並且能維持我們想要的流程
程式如何運行
以基礎類別UiFactory來說
定義了一個createUi的方法
Ui ui = createUi(type);
這行沒有使用任何具體類別的new去實例化,代表與Ui具體類解耦合
並且多型的接受一個Ui,不會知道是哪一個風格的Ui會被製作
那是在哪時候知道被製作UI的風格的呢?
當子類別完成繼承的時候已經決定好了
因為你無法在Fancy的工廠要求產出Classical風格的UI
回頭設計UI
public abstract class UI{
int width;
int height;
Frame frame; //UI外框
Font font;
abstract void init();
void setPosition(int [] position){
frame.setPosition(position);
}
void setColor(int [] color){
frame.setColor(color);
}
}
//工廠框架也要修正
public abstract class UiFactory{
public Ui getUi(int type, int[] position, int[] color){
Ui ui = createUi(type);
ui.init() //初始化UI
ui.setPosition(position);
ui.setColor(color);
return ui;
}
abstract Ui createUi(int type);
}
用抽象方法createUi回傳一個UI變數,讓子類別去實作
使用抽象方法init讓UI類別去實作各自需要的需求
接下來來是設計等待以久的風格UI
public class FancyUi1 extends UI{
public void init(){
frame = new FancyFrame();
font = new FancyFont();
}
}
// 古典風格的一樣步驟
去實作init得到相對應的frame和font,大功告成
工廠模式介紹
工廠模式包含兩種類型的類別
- 建立者(Cretor)
UiFactory是基礎類別
有兩個繼承它的子類別,Fancy和Classical - 產品(Product)
UI是基礎類別 一樣對應健立者的類別
不過各個類型產品,可以有多個,如FancyBlue,FancyCart
所有工廠模式都會將建立物件的方法封裝起來,透過子類別來實作工廠方法更有彈性
維基百科的解釋;
工廠方法模式(英語:Factory method pattern)是一種實現了「工廠」概念的物件導向設計模式。就像其他建立型模式一樣,
它也是處理在不指定物件具體類型的情況下建立物件的問題。工廠方法模式的實質是「定義一個建立物件的介面,但讓實現這個介
面的類來決定實例化哪個類。工廠方法讓類別的實例化推遲到子類中進行。」
UI產品還是有問題
UI工廠已經是一個很靈活的框架,而且設計良好
但UI產品沒有,因為沒有給它一個明確的規範
public class FancyUi1 extends UI{
public void init(){
frame = new FancyFrame();
font = new FancyFont();
}
}
上面的程式碼實例化與實體類別耦合
現在希望做的事情
可以讓產品內部的實例化與與產品本身解耦合
思考一下UI產品
這邊有兩個風格的UI,Fancy、Classical
底下各有三個產品
它們應該會有各自的font與frame等等,才可以組成不同的風格
所以用介面把這兩個包裝起來
public interface UiProductFactory{
Font createFont();
Frame CreateFrame();
}
組建UI產品的工廠
先實作剛剛準備好的介面,限制類別一定要實作font和frame的方法
public class FancyUIFactory implements UiProductFactory{
public Font createFont(){
return new FancyFont();
}
public Frame createFrame(){
return new FancyFrame();
}
}
與UI產品做結合
現在我們希望做到的事情是可以幫目前的UI產品解耦合
public class FancyUi1 extends UI{
public void init(){
frame = new FancyFrame();
font = new FancyFont();
}
}
不管是fancy或者Classical風格
唯一個差別就是new frame和new font的類別不一樣而已
可以使用介面統約定這兩個方法
public class FancyUi1 extends UI{
UiProductFactory uiFactory;
public FancyUi(UiProductFactory uiFactory){
this.uiFactory = uiFactory;
}
public void init(){
frame = uiFactory.createFrame();
font = uiFactory.createFont();
}
}
如此就可以消除掉剛剛類別與UI實例化的耦合
可以更有彈性了
抽象工廠
剛剛的稱為抽象工廠
參考維基百科的解釋
抽象工廠模式提供了一種方式,可以將一組具有同一主題的單獨的工廠封裝起來。在正常使用中,客戶端程式需要建立抽象工廠的具體實現,
然後使用抽象工廠作為介面來建立這一主題的具體物件。客戶端程式不需要知道(或關心)它從這些內部的工廠方法中獲得物件的具體類型,
因為客戶端程式僅使用這些物件的通用介面。
間單的說提供介面來建立產品的家族
介面就是指這一個
public interface UiProductFactory{
Font createFont();
Frame CreateFrame();
}
這樣的設計很好的幫我們解決了耦合的問題,更有彈性
這樣就可以很輕易的替換產品的工廠,產品並不知材料是哪一間工廠提供的
而我們就可以根據不同的狀況使用不同的工廠,來獲得不同的行為了