Java模式-三種工廠模式

簡單工廠

簡單工廠模式又 叫靜態(tài)工廠方法模式(Static FactoryMethod Pattern),是通過專門定義一個(gè)類來負(fù)責(zé)創(chuàng)建其他類的實(shí)例筛欢,被創(chuàng)建的實(shí)例通常都具有共同的父類田藐。
我們從一個(gè)實(shí)例展開
現(xiàn)在有一道面試題:使用java實(shí)現(xiàn)一個(gè)計(jì)算機(jī)控制臺(tái)程序,要求輸入數(shù)的運(yùn)算呕诉,得到結(jié)果。
這道題目最原始的寫法:

public class Computer {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        System.out.println("請(qǐng)輸入第一個(gè)數(shù)字:");
        float firstNum  = in.nextFloat();
        System.out.println("請(qǐng)輸入第二個(gè)數(shù)字:");
        float secondNum  = in.nextFloat();
        System.out.println("請(qǐng)輸入運(yùn)算符號(hào):");
        String countQuato = in.next();
        if("+".equals(countQuato)){
            System.out.println("result : "+(firstNum+secondNum));
        }else if("-".equals(countQuato)){
            System.out.println("result : "+(firstNum-secondNum));
        }else if("*".equals(countQuato)){
            System.out.println("result : "+(firstNum*secondNum));
        }else if("/".equals(countQuato)){
            System.out.println("result : "+(firstNum/secondNum));
        }
    }

上面的寫法實(shí)現(xiàn)雖然簡單,但是卻沒有面向?qū)ο蟮奶匦云季ǎa拓展性差味咳,顯然不是出題者想要考察的意圖庇勃。
那么面向?qū)ο缶幊桃绾卧陬}中體現(xiàn)呢?
在面向?qū)ο缶幊陶Z言中莺葫,一切都是對(duì)象匪凉,所以上面運(yùn)算符號(hào)也應(yīng)當(dāng)作對(duì)象來處理。
我們首先建立一個(gè)接口

public abstract class Operation {

    public abstract float getResult(float firstNumber, float secondNumber);

}
//把符號(hào)都當(dāng)做對(duì)象處理捺檬,實(shí)現(xiàn)此接口
public class AddOperation extends Operation {
    @Override
    public float getResult(float firstNumber, float secondNumber) {
        return firstNumber+secondNumber;
    }

}
public class SubOperation extends Operation {
    @Override
    public float getResult(float firstNumber, float secondNumber) {
        return firstNumber-secondNumber;
    }
}
public class MulOperation extends Operation {
    @Override
    public float getResult(float firstNumber, float secondNumber) {
        return firstNumber*secondNumber;
    }
}
public class DivOperation extends Operation {
    @Override
    public float getResult(float firstNumber, float secondNumber) {
        return firstNumber/secondNumber;
    }
}

//接下來需要解決的就是對(duì)象的創(chuàng)建問題了再层,既如何根據(jù)不同的情況創(chuàng)建不同的對(duì)象:我們正好可以通過簡單工廠模式實(shí)現(xiàn)
public class OperationFactory {

    public static Operation getOperation(String quotaFlag){
        Operation o = null;
        switch (quotaFlag){
            case "+" :  o = new AddOperation();
            case "-" :  o = new SubOperation();
            case "*" :  o = new MulOperation();
            case "/" :  o = new DivOperation();
            default:break;
        }
        return o;
    }
}
//調(diào)用:
public class Computer {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        System.out.println("請(qǐng)輸入第一個(gè)數(shù)字:");
        float firstNum  = in.nextFloat();
        System.out.println("請(qǐng)輸入第二個(gè)數(shù)字:");
        float secondNum  = in.nextFloat();
        System.out.println("請(qǐng)輸入運(yùn)算符號(hào):");
        String countQuato = in.next();
        System.out.println(count(firstNum,secondNum,countQuato));
    }
    private static float count(float firstNum,float secondNum , String countQuota){
    //通過工廠類獲取對(duì)象
        Operation operation = OperationFactory.getOperation(countQuota);
        return operation.getResult(firstNum,secondNum);
    }
}

簡單工廠將對(duì)象的創(chuàng)建過程進(jìn)行了封裝,用戶不需要知道具體的創(chuàng)建過程堡纬,只需要調(diào)用工廠類獲取對(duì)象即可聂受。

這種簡單工廠的寫法是通過switch-case來判斷對(duì)象創(chuàng)建過程的。在實(shí)際使用過程中烤镐,違背了 開放-關(guān)閉原則蛋济,當(dāng)然有些情況下可以通過反射調(diào)用來彌補(bǔ)這種不足。

工廠方法

工廠方法 定義一個(gè)用于創(chuàng)建對(duì)象的接口炮叶,讓子類決定實(shí)例化哪一個(gè)類碗旅,工廠方法使得一個(gè)類的實(shí)例化延遲到了子類
工廠方法在簡單工廠的基礎(chǔ)上再包了一層工廠,所有的工廠都是此工廠的子類镜悉。而產(chǎn)生對(duì)象的類型由子類工廠決定祟辟。使用工廠方法來實(shí)現(xiàn)上面的加減乘除對(duì)象的創(chuàng)建

//定義上級(jí)工廠的接口
public interface IFractory {
    public Operation generateOper();
}
//為每一個(gè)類創(chuàng)建工廠
/**
 * 工廠方法  為每個(gè)對(duì)象生成一個(gè)工廠類
 */
public class AddOperationFactory implements IFractory{

    @Override
    public Operation generateOper() {
        return new AddOperation();
    }
}
public class SubOperationFactory implements IFractory {
    @Override
    public Operation generateOper() {
        return new SubOperation();
    }
}
public class MulOperationFactory implements IFractory {
    @Override
    public Operation generateOper() {
        return new MulOperation();
    }
}
public class DivOperationFactory implements IFractory {
    @Override
    public Operation generateOper() {
        return new DivOperation();
    }
}
//客戶端代碼
IFractory fractory = new AddOperationFactory();
Operation operation = fractory.generateOper();
operation.getResult(firstNum,secondNum);

工廠方法將類的實(shí)例化推遲到了其子類。所以使用工廠方法模式時(shí)侣肄,需要客戶端決定實(shí)例化哪一個(gè)工廠類旧困。選擇判斷問題還是存在的。也就是說,工廠方法把簡單的工廠內(nèi)部邏輯判斷轉(zhuǎn)移到了客戶端來運(yùn)行吼具。你想要加的功能僚纷,本來是要改工廠類的,而現(xiàn)在是修改客戶端拗盒。不過怖竭,我們?cè)谀承┣闆r下通過工廠方法,只需要修改一行實(shí)例化的代碼就可以實(shí)現(xiàn)系統(tǒng)元素的切換(比如切換數(shù)據(jù)源)锣咒。這也是很方便的侵状。

抽象工廠

提供一個(gè)創(chuàng)建一系列相關(guān)相互依賴對(duì)象的接口,而無需指定他們具體的類毅整。抽象工廠為不同產(chǎn)品族的對(duì)象創(chuàng)建提供接口趣兄。
使用場景:系統(tǒng)需要在不同產(chǎn)品族進(jìn)行切換
代碼實(shí)現(xiàn):

public interface IFacfory {
    public IUser createUser();
    public IDepartment createDepartment();
}
public interface IUser {
    public void insert();
    public void getById();
}
public interface IDepartment {
    public void insert();
    public void getDepartmentById();
}
public class SqlServerUser implements IUser {
    @Override
    public void insert() {
        System.out.println("insert into sqlserver.");
    }

    @Override
    public void getById() {
        System.out.println("get user by id from sqlserver.");
    }
}
public class SqlServerDepartment implements IDepartment {
    @Override
    public void insert() {
        System.out.println("insert department into sqlserver.");
    }

    @Override
    public void getDepartmentById() {
        System.out.println("get department in sqlserver by id.");
    }
}

public class AccessUser implements IUser {
    @Override
    public void insert() {
        System.out.println("insert into access");
    }

    @Override
    public void getById() {
        System.out.println("get by id from access");
    }
}
public class AccessDepartment implements IDepartment {
    @Override
    public void insert() {
        System.out.println("insert department into sqlserver.");
    }

    @Override
    public void getDepartmentById() {
        System.out.println("get department in sqlserver by id.");
    }
}

//不同產(chǎn)品組使用一個(gè)工廠
public class SqlServerFactory implements IFacfory {
    @Override
    public IUser createUser() {
        return new SqlServerUser();
    }

    @Override
    public IDepartment createDepartment() {
        return new SqlServerDepartment();
    }
}
public class AccessFactory implements IFacfory {
    @Override
    public IUser createUser() {
        return new AccessUser();
    }

    @Override
    public IDepartment createDepartment() {
        return new AccessDepartment();
    }
}
客戶端:
IFacfory facfory = new AccessFactory();
IUser user = facfory.createUser();
IDepartment department = facfory.createDepartment();
user.insert();
user.getById();
department.insert();
department.getDepartmentById();

抽象工廠最大的好處就是便于交換產(chǎn)品系列,具體工廠在代碼中一般只出現(xiàn)一次悼嫉。這就使得改變應(yīng)用的具體工廠很容易艇潭。
第二個(gè)好處是他能讓具體的創(chuàng)建對(duì)象實(shí)例和客戶端分離,客戶端是通過他們的抽象接口操作實(shí)例
抽象工廠不太易于拓展戏蔑,如果需要自增功能蹋凝,或者自增產(chǎn)品,則需要至少修改三個(gè)類总棵,而且實(shí)例化的代碼是寫死在程序中的 鳍寂, 這樣無法避免違背開放-關(guān)閉原則。
對(duì)于上述問題情龄,可以通過配置文件迄汛,結(jié)合反射的方式來解決。在這里就不再啰嗦

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末骤视,一起剝皮案震驚了整個(gè)濱河市鞍爱,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌专酗,老刑警劉巖睹逃,帶你破解...
    沈念sama閱讀 210,914評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異祷肯,居然都是意外死亡沉填,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,935評(píng)論 2 383
  • 文/潘曉璐 我一進(jìn)店門佑笋,熙熙樓的掌柜王于貴愁眉苦臉地迎上來翼闹,“玉大人,你說我怎么就攤上這事允青。” “怎么了?”我有些...
    開封第一講書人閱讀 156,531評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵颠锉,是天一觀的道長法牲。 經(jīng)常有香客問我,道長琼掠,這世上最難降的妖魔是什么拒垃? 我笑而不...
    開封第一講書人閱讀 56,309評(píng)論 1 282
  • 正文 為了忘掉前任,我火速辦了婚禮瓷蛙,結(jié)果婚禮上悼瓮,老公的妹妹穿的比我還像新娘。我一直安慰自己艰猬,他們只是感情好横堡,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,381評(píng)論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著冠桃,像睡著了一般命贴。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上食听,一...
    開封第一講書人閱讀 49,730評(píng)論 1 289
  • 那天胸蛛,我揣著相機(jī)與錄音,去河邊找鬼樱报。 笑死葬项,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的迹蛤。 我是一名探鬼主播民珍,決...
    沈念sama閱讀 38,882評(píng)論 3 404
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼笤受!你這毒婦竟也來了穷缤?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,643評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤箩兽,失蹤者是張志新(化名)和其女友劉穎津肛,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體汗贫,經(jīng)...
    沈念sama閱讀 44,095評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡身坐,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,448評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了落包。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片部蛇。...
    茶點(diǎn)故事閱讀 38,566評(píng)論 1 339
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖咐蝇,靈堂內(nèi)的尸體忽然破棺而出涯鲁,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 34,253評(píng)論 4 328
  • 正文 年R本政府宣布抹腿,位于F島的核電站岛请,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏警绩。R本人自食惡果不足惜崇败,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,829評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望肩祥。 院中可真熱鬧后室,春花似錦、人聲如沸混狠。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,715評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽檀蹋。三九已至松申,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間俯逾,已是汗流浹背贸桶。 一陣腳步聲響...
    開封第一講書人閱讀 31,945評(píng)論 1 264
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留桌肴,地道東北人皇筛。 一個(gè)月前我還...
    沈念sama閱讀 46,248評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像坠七,于是被迫代替她去往敵國和親水醋。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,440評(píng)論 2 348