摘自《輕量級JavaEE企業應用開發》
【模式】是一條由三個部分組裝成的通用規則:它表示了一個特定環境、一類問題和一個解決方案之間的關系
【設計模式】是對于特定環境下,經常出現的的某類軟件開發問題的一種相對成熟的設計方案
單例模式
如果一個類始終只能創建一個實例,則稱這個類為單例類,這種模式就被稱為單例模式。
Spring推薦獎所有業務邏輯組件、DAO組件、數據源組件等配置為單例的行為方式,因為這些組件不需要保存任何用戶狀態,是所有客戶端都可以通用的組件
代碼實現
package com.singleton;
/**
* 單例模式Demo
* 《輕量級JavaEE企業應用實戰》 P727
* 如果一個類始終只能創建一個實例,則這個類稱為單例類,這種模式稱為單例模式
* Spring推薦獎所有業務邏輯組件、DAO組件、數據源組件等配置成單例的行為方式
* @author Slience
*
*/
class Singleton {
private static Singleton instance;
private Singleton() {
}
public static Singleton getInstance() {
//如果instance等于null,證明還沒有創建過Singleton實例,為其創建Singleton實例
//如果instance不等于null,說明已經創建過Singleton實例,
//直接將這個Singleton返回即可,不必在new一個Singleton
if(instance == null) {
instance = new Singleton();
}
return instance;
}
}
public class SingletonTest {
public static void main(String[] args) {
Singleton s1 = Singleton.getInstance();
Singleton s2 = Singleton.getInstance();
System.out.println(s1 == s2);
}
}
單例模式主要有兩個優勢
- 減少創建Java實例所帶來的系統開銷。
- 便于系統跟蹤單個Java實例的生命周期、實例狀態等。
簡單工廠模式
A實例調用B實例的方法,則稱A依賴于B
當A實例需要調用B實例的時候,有兩種方式可以實現:
- 一種是使用new 關鍵字創建一個B實例,這種方式的缺點在于如果日后需求有變需要用C實例替代B實例,那么A實例中硬編碼耦合B實例就需要修改,如果有很多實例都用new B()的方式去調用B實例的方法,那么需要修改的地方將會很多。
- A實例只需要調用B對象的方法,并不關心B對象的實現創建過程,所以我們可以讓B類實現了一個IB借口,A類只需要和IB接口耦合,A類不直接使用new關鍵字來創建B實例,而是重新定義一個IBFactory類,由該類來負責創建IB實例,A類只需要調用IBFactory工廠方法來得到IB實例。
使用后一種設計,可以讓A類和B類解除耦合,只需要和IB結構和IBFactory耦合,這樣當新需求來時C類可以去實現IB接口然后再IBFactory中修改創建IB實例的代碼,就可以實現新需求了,A類中不需要對代碼進行修改。
這種將多個類對象交給工廠類來生成的設計方式被稱為【簡單工廠模式】
代碼實現
假設程序用有一個Computer對象需要依賴一個輸出設備,可以讓Computer對象依賴一個Output(接口)屬性,再用Printer(實現輸出類)對象去實現Output接口,Computer從OutputFactory工廠類生成Printer從而讓Computer和Printer分離開來,日后如果有更好的實現輸出類BetterPrinter,那么直接修改OutputFactory代碼即可。
Computer類
package com.simplefactory;
/**
* 《輕量級JavaEE企業應用實戰 p728
* Computer類可以通過Output接口打印內容,讓打印機類實現Output接口
* Computer使用OutputFactory來獲取合適的打印機從而讓打印機類和電腦類解耦
*
* 使用簡單工廠模式的優勢是:讓對象的調用者和對象的創建過程分離,當對象調用者需要對象時,
* 直接向工廠請求即可;從而避免了對象的調用者與對象的實現類以硬編碼方式耦合,
* 以提高系統的可維護性、可拓展性。
* 工廠模式也有一個小小的缺陷:當產品修改是,工廠類也要做相應的修改
* @author Slience
*
*/
public class Computer {
private Output out;
public Computer(Output out) {
this.out = out;
}
//定義一個模擬獲取字符串輸入的方法
public void keyIn(String msg) {
out.getDate(msg);
}
//定義一個模擬打印的方法
public void print() {
out.out();
}
public static void main(String[] args) {
OutputFactory factory = new OutputFactory();
//通過工廠類獲取output對象
//Computer和factory硬編碼而不和output實現類耦合,這樣修改到另一個實現類的時候
//只需要修改factory中的getOutput方法即可
Computer computer = new Computer(factory.getOutput());
computer.keyIn("Hello World");
computer.keyIn("Slience");
computer.print();
}
}
Output輸出接口
package com.simplefactory;
public interface Output {
//默認的打印隊列大小
final static int MAX_CACHE_LINE = 1;
void getDate(String msg);
void out();
}
之后用Printer去實現Output
package com.simplefactory;
public class Printer implements Output {
private String[] printData = new String[MAX_CACHE_LINE];
//用以記錄當前需要打印的作業數
private int dataNum = 0;
@Override
public void getDate(String msg) {
// TODO Auto-generated method stub
if(dataNum >= MAX_CACHE_LINE) {
System.out.println("輸出隊列已滿,添加失敗");
} else {
printData[dataNum++] = msg;
}
}
@Override
public void out() {
// TODO Auto-generated method stub
while(dataNum > 0) {
System.out.println("打印機打印:" + printData[0]);
//將原數組第二個到--dataNum個元素前移一位
System.arraycopy(printData, 1, printData, 0, --dataNum);
}
}
}
創建Output對象都通過OutputFactory工廠類去創建
package com.simplefactory;
public class OutputFactory {
public Output getOutput() {
return new Printer();
//換了新的打印機之后就可以打印兩行了
//return new BetterPrinter();
}
}
當我們運行起Computer的時候會輸出
輸出隊列已滿,添加失敗
打印機打印:Hello World
如果此時來了一個更好地Output實現類——BetterPrinter,他能夠打印原大小兩倍的數據量
package com.simplefactory;
public class BetterPrinter implements Output {
private String[] printData = new String[MAX_CACHE_LINE * 2];
//用以記錄當前需要打印的作業數
private int dataNum = 0;
@Override
public void getDate(String msg) {
// TODO Auto-generated method stub
if(dataNum >= MAX_CACHE_LINE * 2) {
System.out.println("輸出隊列已滿,添加失敗");
} else {
printData[dataNum++] = msg;
}
}
@Override
public void out() {
// TODO Auto-generated method stub
while(dataNum > 0) {
System.out.println("打印機打印:" + printData[0]);
//將原數組第二個到--dataNum個元素前移一位
System.arraycopy(printData, 1, printData, 0, --dataNum);
}
}
}
我們想要給Computer換上新的Output的話,只需要在OutputFactory中修改為return new BetterPrinter()
即可,Computer不需要做任何的改變。但是這樣還是有一個小小的缺點,那就是當產品修改時,工廠類也要做相應的修改。
在Spring中可以通過配置XML來實現工廠類的修改
工廠模式
和簡單工廠模式很像,只不過新增了一個OutputFactory接口和BetterPrinterFactory和PrinterFactory工廠類,這樣做的好處在于不同的工廠生產不同的對象,工廠實現類不需要對邏輯進行判斷,邏輯判斷可以在OutputFactoryFactory類中進行,OutputFactoryFactory類是用來根據需要生成不同的實現了OutputFactory接口的工廠類的類。
代碼實現
Output接口不變,新增OutputFactory接口
package com.factory;
public interface OutputFactory {
Output getOutput();
}
還有其實現類OutputFactoryFactory
package com.factory;
public class OutputFactoryFactory {
public static OutputFactory getOutputFactory(String type) {
//不考慮大小寫
if(type.equalsIgnoreCase("better")) {
return new BetterPrinterFactory();
} else {
return new PrinterFactory();
}
}
}
之后創建BetterPrinterFactory和PrinterFactory工廠實現類,PrintFactory和BetterPrinterFactory只是兩者返回的對象不同。
package com.factory;
public class BetterPrinterFactory implements OutputFactory {
@Override
public Output getOutput() {
// TODO Auto-generated method stub
return new BetterPrinter();
//如果是PrinterFactory返回的就是
//return new Printer();
}
}
在Computer中通過對OutputFactoryFactory傳入不同的參數來獲取不同的Factory類,再通過不同的工廠類來創建不同的對象
package com.factory;
/**
* 《輕量級JavaEE企業應用實戰 p737
*
*
*
* @author Slience
*
*/
public class Computer {
private Output out;
public Computer(Output out) {
this.out = out;
}
//定義一個模擬獲取字符串輸入的方法
public void keyIn(String msg) {
out.getDate(msg);
}
//定義一個模擬打印的方法
public void print() {
out.out();
}
public static void main(String[] args) {
//工廠的工廠
OutputFactory factory = OutputFactoryFactory.getOutputFactory("better");
//通過工廠類獲取output對象
//Computer和factory硬編碼而不和output實現類耦合,這樣修改到另一個實現類的時候
//只需要修改factory中的getOutput方法即可
Computer computer = new Computer(factory.getOutput());
computer.keyIn("Hello World");
computer.keyIn("Slience");
computer.print();
}
}