前言
【開發】:老大,你教了我挺多設計模式的,已經全部教完了嗎?
【BOSS】:沒呢,還有好幾個設計模式沒說過呢,今天再傳授你三個吧,分別是建造者模式,責任鏈模式,備忘錄模式,如何?
【開發】:好啊,我最喜歡學習了!
建造者模式
意圖
將一個複雜對象的構建與它的表示分離,使得同樣的構建過程可以創建不同的表示。
核心代碼
定義建造接口
public interface Builder {
void buildPartOne();
void buildPartTwo();
void buildPartThr();
/***
* 一般情況肯定是一個複雜的對象
*/
String getResult();
}
定義實際建造工人
public class ConcreteBuilder implements Builder {
private StringBuffer buffer = new StringBuffer();
@Override
public void buildPartOne() {
buffer.append("i am part one\n");
}
@Override
public void buildPartTwo() {
buffer.append("i am part two\n");
}
@Override
public void buildPartThr() {
buffer.append("i am part Thr\n");
}
@Override
public String getResult() {
return buffer.toString();
}
}
如何創建不同的表示?
定義督公
public class Director {
private Builder builder;
public Director(Builder builder) {
this.builder = builder;
}
public void setBuilder(Builder builder) {
this.builder = builder;
}
public void construct() {
builder.buildPartOne();
builder.buildPartTwo();
builder.buildPartThr();
}
}
模擬調用
public class App {
/***
* 建造者模式
* 建造者模式(Builder Pattern)使用多個簡單的對象一步一步構建成一個複雜的對象,這種類型的設計模式屬於創建型模式,它提供了一種創建對象的最佳方式
*
* 主要解決
* 主要解決在軟件系統中,有時候面臨着"一個複雜對象"的創建工作,其通常由各個部分的子對象用一定的算法構成;由於需求的變化,這個複雜對象的各個部分經常面臨着劇烈的變化,但是將它們組合在一起的算法卻相對穩定
*
* 何時使用
* 一些基本部件不會變,而其組合經常變化的時候。
*
* 如何解決
* 將變與不變分離開。
*
* 關鍵代碼
* 建造者:創建和提供實例
* 建造者接口:依賴接口編程
* 指導者:管理建造出來的實例的依賴關係
* 產品:建造者所生產的產品
* 建造者作爲參數進入指導者構造方法,通過特定普遍的構造順序或算法執行,得到產品
*
* 應用實例:
* 1.去肯德基,漢堡、可樂、薯條、炸雞翅等是不變的,而其組合是經常變化的,生成出所謂的"套餐"
* 2.StringBuilder
*/
public static void main(String[] args){
// 創建建造者接口並指向具體建造者 - 包含最終產品
Builder concreteBuilder = new ConcreteBuilder();
// 創建指導者, 把具體建造者即工人作爲參數傳入, 通過統一方法執行相應的構建命令
Director director = new Director(concreteBuilder);
director.construct();
// 從工人即具體建造者獲取產品
String result = concreteBuilder.getResult();
System.out.println(result);
}
}
建造者的延展思考:鏈式調用
鏈式調用讓代碼更優雅~
public class MyBuilder {
// 省略不必要的代碼
MyBuilder withName(String name) {
this.setName(name);
return this;
}
MyBuilder withYear(String year) {
this.setYear(year);
return this;
}
MyBuilder withSex(String sex) {
this.setSex(sex);
return this;
}
}
UML圖
代碼見下方~
責任鏈模式
意圖
使多個對象都有機會處理請求,從而避免請求的發送者和接收者之間的耦合關係。將這些對象連城一條鏈,並沿着這條鏈傳遞該請求,直到有一個對象處理它爲止
典型應用
Log4J 日誌系統即是使用了責任鏈的思想,通過不同日誌級別的傳遞,按級別處理日誌
簡單實現一個Log等級系統
抽象類
定義日誌等級,設置下一個處理器,抽象出寫入方法
public abstract class AbstractLogger {
// 責任級別
public static int INFO = 1;
public static int DEBUG = 2;
public static int ERROR = 3;
// 當前級別
int level;
//責任鏈中的下一個元素
AbstractLogger nextLogger;
public void setNextLogger(AbstractLogger nextLogger){
this.nextLogger = nextLogger;
}
// 記錄日誌
public void logMessage(int level, String message){
if(this.level <= level){
write(message);
}
if(nextLogger != null){
nextLogger.logMessage(level, message);
}
}
// 抽象方法 -> 重寫具體日誌輸出類型
abstract protected void write(String message);
}
具體日誌類
public class InfoLoger extends AbstractLogger {
public InfoLoger(int level){
this.level = level;
}
@Override
protected void write(String message) {
System.out.println("InfoLoger Console::Logger: " + message);
}
}
爲了避免重複,只展示一個類
實際調用
public class App {
public static void main(String[] args){
AbstractLogger log = getChainOfLoggers();
log.logMessage(AbstractLogger.INFO, "i am info");
log.logMessage(AbstractLogger.DEBUG, "i am debug");
log.logMessage(AbstractLogger.ERROR, "i am error");
}
private static AbstractLogger getChainOfLoggers(){
AbstractLogger error = new ErrorLoger(AbstractLogger.ERROR);
AbstractLogger debug = new DebugLoger(AbstractLogger.DEBUG);
AbstractLogger info = new InfoLoger(AbstractLogger.INFO);
error.setNextLogger(debug);
debug.setNextLogger(info);
return error;
}
}
// 輸出結果:
// InfoLoger Console::Logger: i am info
//
// ------------------------
//
// DebugLoger Console::Logger: i am debug
// InfoLoger Console::Logger: i am debug
//
// ------------------------
//
// ErrorLoger Console::Logger: i am error
// DebugLoger Console::Logger: i am error
// InfoLoger Console::Logger: i am error
總結
多種形式
- 當前pattern下類似日誌級別形式, 只要等級比A大,那B,C都會處理
- 如A->B->C 由低到高級別執行,只要執行就返回等
- 最高級形式: 低級發起請求後, 高級任一處理後,請求反饋即可(涉及到異步相關,線程通信)
優點
- 降低耦合度。它將請求的發送者和接收者解耦
- 簡化了對象。使得對象不需要知道鏈的結構
- 增強給對象指派職責的靈活性,通過改變鏈內的成員或者調動它們的次序,允許動態地新增或者刪除責任
- 增加新的請求處理類很方便
缺點
- 不能保證請求一定被接收
- 系統性能將受到一定影響,而且在進行代碼調試時不太方便,可能會造成循環調用
- 可能不容易觀察運行時的特徵,有礙於除錯
使用場景
- 有多個對象可以處理同一個請求,具體哪個對象處理該請求由運行時刻自動確定
- 在不明確指定接收者的情況下,向多個對象中的一個提交一個請求
- 可動態指定一組對象處理請求
UML圖
代碼見下方~
備忘錄模式
意圖
在不破壞封裝性的前提下,捕獲一個對象的內部狀態,並在該對象之外保存這個狀態,這樣以後可將對象恢復到原先保存的狀態
核心代碼
備忘錄
/**
* ******************************
* description: 備忘錄,確定數據結構即可
* ******************************
*/
public class Memento {
Map<String, String> data;
}
模擬短信場景
/**
* ******************************
* description: 模擬短信場景
* ******************************
*/
public class MessageData {
private String time;
private String message;
/**
* 存儲數據
*/
public Memento saveMemento () {
Map<String, String> map = Maps.newHashMap();
map.put("TIME", time);
map.put("MESSAGE", message);
return new Memento(map);
}
/**
* 取出數據
*/
public void getFromMemento(Memento memento){
time = memento.getData().get("TIME");
message = memento.getData().get("MESSAGE");
}
// 省略部分代碼
}
備忘錄存儲容器
public class MementoTaker {
private List<Memento> mementoList = new ArrayList<>();
public void add(Memento state){
mementoList.add(state);
}
public Memento get(int index){
return mementoList.get(index);
}
}
核心調用代碼
public class App {
public static void main(String[] args) throws InterruptedException {
// 創建備忘錄管理者
MementoTaker mementoTaker = new MementoTaker();
MessageData messageData = new MessageData();
messageData.setTime(System.currentTimeMillis() + "");
messageData.setMessage("This is messgae first.");
mementoTaker.add(messageData.saveMemento());
System.out.println("First: -> " + messageData);
Thread.sleep(2000);
messageData.setTime(System.currentTimeMillis() + "");
messageData.setMessage("This is messgae second.");
mementoTaker.add(messageData.saveMemento());
System.out.println("Second: -> " + messageData);
Thread.sleep(2000);
// 回覆初次狀態
messageData.getFromMemento(mementoTaker.get(0));
System.out.println("********************檢測數據是否回到初始狀態******************");
System.out.println(messageData);
}
}
模式總結:其實該模式非常簡單,即確定好數據結構在容器中存儲一份,以便後續恢復,或者重新使用等等
總結
主要解決
所謂備忘錄模式就是在不破壞封裝的前提下,捕獲一個對象的內部狀態,並在該對象之外保存這個狀態,這樣可以在以後將對象恢復到原先保存的狀態
備忘錄思想的實踐
- 打遊戲時的存檔
- Windows 裏的 ctri + z
- 數據庫的事務管理
UML圖
相關代碼鏈接
- 兼顧了《HeadFirst》以及《GOF》兩本經典書籍中的案例
- 提供了友好的閱讀指導