行為型設(shè)計模式范圍
- 觀察者模式
- 模板方法
- 策略模式
- 職責鏈模式
- 狀態(tài)模式
- 迭代器模式
- 訪問者模式
- 備忘錄模式
- 命令模式
- 解釋器模式
- 中介模式
行為型設(shè)計模式作用
行為型設(shè)計模式主要關(guān)注的是類與類之間的交互問題腻格。
7. 訪問者模式
7.1 定義
允許一個或多個操作應(yīng)用到一組對象上液肌,解耦操作和對象本身。
7.2 作用
- 解耦操作和對象本身胧后,使得操作和對象本身都可以單獨擴展肴盏,且滿足職責單一科盛、開閉原則等設(shè)計思想和原則
- 變向支持雙分派實現(xiàn),即調(diào)用哪個對象的哪個方法都可以在運行時決定
7.3 類結(jié)構(gòu)圖
7.4 經(jīng)典實現(xiàn)
public abstract class ResourceFile {
protected String filePath;
public ResourceFile(String filePath) {
this.filePath = filePath;
}
abstract public void accept(Visitor vistor);
}
public class PdfFile extends ResourceFile {
public PdfFile(String filePath) {
super(filePath);
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
//...
}
//...PPTFile菜皂、WordFile跟PdfFile類似贞绵,這里就省略了...
public interface Visitor {
void visit(PdfFile pdfFile);
void visit(PPTFile pdfFile);
void visit(WordFile pdfFile);
}
public class Extractor implements Visitor {
@Override
public void visit(PPTFile pptFile) {
//...
System.out.println("Extract PPT.");
}
@Override
public void visit(PdfFile pdfFile) {
//...
System.out.println("Extract PDF.");
}
@Override
public void visit(WordFile wordFile) {
//...
System.out.println("Extract WORD.");
}
}
public class Compressor implements Visitor {
@Override
public void visit(PPTFile pptFile) {
//...
System.out.println("Compress PPT.");
}
@Override
public void visit(PdfFile pdfFile) {
//...
System.out.println("Compress PDF.");
}
@Override
public void visit(WordFile wordFile) {
//...
System.out.println("Compress WORD.");
}
}
public class ToolApplication {
public static void main(String[] args) {
Extractor extractor = new Extractor();
List<ResourceFile> resourceFiles = listAllResourceFiles(args[0]);
for (ResourceFile resourceFile : resourceFiles) {
resourceFile.accept(extractor);
}
Compressor compressor = new Compressor();
for(ResourceFile resourceFile : resourceFiles) {
resourceFile.accept(compressor);
}
}
private static List<ResourceFile> listAllResourceFiles(String resourceDirectory) {
List<ResourceFile> resourceFiles = new ArrayList<>();
//...根據(jù)后綴(pdf/ppt/word)由工廠方法創(chuàng)建不同的類對象(PdfFile/PPTFile/WordFile)
resourceFiles.add(new PdfFile("a.pdf"));
resourceFiles.add(new WordFile("b.word"));
resourceFiles.add(new PPTFile("c.ppt"));
return resourceFiles;
}
}
具體實現(xiàn):
- 定義 Visotor 接口,根據(jù)不同的功能實現(xiàn) Visotor 接口
- 在使用 Visotor 提供功能的類中通過
accept(Visitor visitor)
接收這個 Visotor 對象的函數(shù)注入操作 - 通過調(diào)用
accept(visitor)
方法來將當前對象通過 this 傳入到 Visitor 中并進行處理
擴展問題
當需要擴展功能時恍飘,只需要實現(xiàn) Visotor 接口榨崩,并實現(xiàn)新的功能邏輯谴垫,并在最終調(diào)用方創(chuàng)建新的 Visotor 對象并傳入到待處理的業(yè)務(wù)類中即可。
不需要改動待處理的業(yè)務(wù)類中的邏輯母蛛。
7.5 多態(tài)和函數(shù)重載的區(qū)別
多態(tài)是一種動態(tài)綁定翩剪,也就是在運行時獲取到對象的實現(xiàn)類型。
而重載是一種靜態(tài)綁定彩郊,在代碼編譯的過程中肢专,并不能獲取對象的實現(xiàn)類型,只能根據(jù)聲明類型執(zhí)行類型對象的方法焦辅。
public abstract class ResourceFile {
protected String filePath;
public ResourceFile(String filePath) {
this.filePath = filePath;
}
}
public class PdfFile extends ResourceFile {
public PdfFile(String filePath) {
super(filePath);
}
//...
}
//...PPTFile、WordFile代碼省略...
public class Extractor {
public void extract2txt(PPTFile pptFile) {
//...
System.out.println("Extract PPT.");
}
public void extract2txt(PdfFile pdfFile) {
//...
System.out.println("Extract PDF.");
}
public void extract2txt(WordFile wordFile) {
//...
System.out.println("Extract WORD.");
}
}
public class ToolApplication {
public static void main(String[] args) {
Extractor extractor = new Extractor();
List<ResourceFile> resourceFiles = listAllResourceFiles(args[0]);
for (ResourceFile resourceFile : resourceFiles) {
extractor.extract2txt(resourceFile);
}
}
private static List<ResourceFile> listAllResourceFiles(String resourceDirectory) {
List<ResourceFile> resourceFiles = new ArrayList<>();
//...根據(jù)后綴(pdf/ppt/word)由工廠方法創(chuàng)建不同的類對象(PdfFile/PPTFile/WordFile)
resourceFiles.add(new PdfFile("a.pdf"));
resourceFiles.add(new WordFile("b.word"));
resourceFiles.add(new PPTFile("c.ppt"));
return resourceFiles;
}
}
由于Extractor 類中定義了幾個重載方法椿胯,分別接收繼承了同一個接口的不同實現(xiàn)的類筷登。當在使用過程中,傳入接口聲明類型給方法時哩盲,由于沒有實現(xiàn)參數(shù)為該接口的函數(shù)前方,導致 無法匹配到任意函數(shù),而使代碼無法通過編譯廉油。
7.6 應(yīng)用場景
一般來說惠险,訪問者模式針對繼承或?qū)崿F(xiàn)了同一個類或接口的不同對象(PdfFile、PPTFile 和 WordFile)進行一系列不相關(guān)的業(yè)務(wù)操作抒线。將業(yè)務(wù)操作抽離出來班巩,定義在獨立細分的訪問者類中,并通過組合的方式嘶炭,將業(yè)務(wù)操作類與對象本身解耦抱慌。
7.7 什么是單分派(Single Dispatch)和雙分派(Double Dispatch)
單分派
執(zhí)行哪個對象的方法,根據(jù)對象的運行時類型決定眨猎;而執(zhí)行對象的哪個方法抑进,根據(jù)方法參數(shù)的編譯時類型來決定。
雙分派
執(zhí)行哪個對象的方法睡陪,根據(jù)對象的運行時類型決定寺渗;而執(zhí)行對象的哪個方法,也根據(jù)方法參數(shù)的運行時類型來決定兰迫。
如何理解 Single 和 Double 這兩個詞
Single 說明執(zhí)行對象的哪個方法只跟對象的“運行時”類型有關(guān)信殊;
Double 說明執(zhí)行對象的哪個方法跟對象和參數(shù)的“運行時”類型兩者有關(guān)。
C++/JAVA 都只支持 Single Dispatch汁果。
7.8 為什么支持 Double Dispatch 的語言不需要訪問者模式
由于 Double Dispatch 支持調(diào)用哪種對象的哪個方法鸡号,即可以通過對象的運行時類型,決定調(diào)用哪個重載函數(shù)须鼎。那直接將對象本身傳遞給操作相關(guān)類中的重載函數(shù)即可執(zhí)行鲸伴。無需將操作相關(guān)類反向注入到對象本身中府蔗,并通過 this 獲取當前運行時類型來完成根據(jù)運行時類型調(diào)用重載函數(shù)的操作。
7.9 使用其它方式實現(xiàn)不同類型文件對象的多種操作
public abstract class ResourceFile {
protected String filePath;
public ResourceFile(String filePath) {
this.filePath = filePath;
}
public abstract ResourceFileType getType();
}
public class PdfFile extends ResourceFile {
public PdfFile(String filePath) {
super(filePath);
}
@Override
public ResourceFileType getType() {
return ResourceFileType.PDF;
}
//...
}
//...PPTFile/WordFile跟PdfFile代碼結(jié)構(gòu)類似汞窗,此處省略...
public interface Extractor {
void extract2txt(ResourceFile resourceFile);
}
public class PdfExtractor implements Extractor {
@Override
public void extract2txt(ResourceFile resourceFile) {
//...
}
}
//...PPTExtractor/WordExtractor跟PdfExtractor代碼結(jié)構(gòu)類似姓赤,此處省略...
public class ExtractorFactory {
private static final Map<ResourceFileType, Extractor> extractors = new HashMap<>();
static {
extractors.put(ResourceFileType.PDF, new PdfExtractor());
extractors.put(ResourceFileType.PPT, new PPTExtractor());
extractors.put(ResourceFileType.WORD, new WordExtractor());
}
public static Extractor getExtractor(ResourceFileType type) {
return extractors.get(type);
}
}
public class ToolApplication {
public static void main(String[] args) {
List<ResourceFile> resourceFiles = listAllResourceFiles(args[0]);
for (ResourceFile resourceFile : resourceFiles) {
Extractor extractor = ExtractorFactory.getExtractor(resourceFile.getType());
extractor.extract2txt(resourceFile);
}
}
private static List<ResourceFile> listAllResourceFiles(String resourceDirectory) {
List<ResourceFile> resourceFiles = new ArrayList<>();
//...根據(jù)后綴(pdf/ppt/word)由工廠方法創(chuàng)建不同的類對象(PdfFile/PPTFile/WordFile)
resourceFiles.add(new PdfFile("a.pdf"));
resourceFiles.add(new WordFile("b.word"));
resourceFiles.add(new PPTFile("c.ppt"));
return resourceFiles;
}
}
使用工廠類創(chuàng)建不同的操作對象,并根據(jù)不同類型的文件獲取不同的操作對象來完成一個或多個操作仲吏。
8. 備忘錄模式
8.1 定義
在不違反封裝原則的前提下不铆,捕獲一個對象的內(nèi)部狀態(tài),并在該對象之外保存這個狀態(tài)裹唆,以便之后恢復對象為之前的狀態(tài)誓斥。
8.2 作用
- 防丟失,快速撤銷许帐、恢復數(shù)據(jù)劳坑。
8.3 類結(jié)構(gòu)圖
8.4 經(jīng)典實現(xiàn)
public class InputText {
private StringBuilder text = new StringBuilder();
public String getText() {
return text.toString();
}
public void append(String input) {
text.append(input);
}
public void setText(String text) {
this.text.replace(0, this.text.length(), text);
}
}
public class SnapshotHolder {
private Stack<InputText> snapshots = new Stack<>();
public InputText popSnapshot() {
return snapshots.pop();
}
public void pushSnapshot(InputText inputText) {
InputText deepClonedInputText = new InputText();
deepClonedInputText.setText(inputText.getText());
snapshots.push(deepClonedInputText);
}
}
public class ApplicationMain {
public static void main(String[] args) {
InputText inputText = new InputText();
SnapshotHolder snapshotsHolder = new SnapshotHolder();
Scanner scanner = new Scanner(System.in);
while (scanner.hasNext()) {
String input = scanner.next();
if (input.equals(":list")) {
System.out.println(inputText.getText());
} else if (input.equals(":undo")) {
InputText snapshot = snapshotsHolder.popSnapshot();
inputText.setText(snapshot.getText());
} else {
snapshotsHolder.pushSnapshot(inputText);
inputText.append(input);
}
}
}
}
實現(xiàn)存在問題
- InputText 快照類中中提供的
setText()
只是為了實現(xiàn)備忘錄模式而定義的一個函數(shù),而這個函數(shù)可能被其它業(yè)務(wù)調(diào)用成畦,暴露了不該暴露的方法違背了封裝原則距芬。 - 對于快照類來說,是不可變的循帐,不應(yīng)該提供任何 set 函數(shù)框仔。所以,InputText 這個快照類的設(shè)計違背了封裝原則拄养。
改進版本
public class InputText {
private StringBuilder text = new StringBuilder();
public String getText() {
return text.toString();
}
public void append(String input) {
text.append(input);
}
public Snapshot createSnapshot() {
return new Snapshot(text.toString());
}
public void restoreSnapshot(Snapshot snapshot) {
this.text.replace(0, this.text.length(), snapshot.getText());
}
}
public class Snapshot {
private String text;
public Snapshot(String text) {
this.text = text;
}
public String getText() {
return this.text;
}
}
public class SnapshotHolder {
private Stack<Snapshot> snapshots = new Stack<>();
public Snapshot popSnapshot() {
return snapshots.pop();
}
public void pushSnapshot(Snapshot snapshot) {
snapshots.push(snapshot);
}
}
public class ApplicationMain {
public static void main(String[] args) {
InputText inputText = new InputText();
SnapshotHolder snapshotsHolder = new SnapshotHolder();
Scanner scanner = new Scanner(System.in);
while (scanner.hasNext()) {
String input = scanner.next();
if (input.equals(":list")) {
System.out.println(inputText.toString());
} else if (input.equals(":undo")) {
Snapshot snapshot = snapshotsHolder.popSnapshot();
inputText.restoreSnapshot(snapshot);
} else {
snapshotsHolder.pushSnapshot(inputText.createSnapshot());
inputText.append(input);
}
}
}
}
創(chuàng)建新的 Snapshot 類來表示快照類离斩,不提供任何修改快照狀態(tài)的函數(shù)。將 InputText 類的 setText()
方法改為 restoreSnapShot()
方法更為達意瘪匿。
8.5 備忘錄模式和備份的區(qū)別
備忘錄模式更側(cè)重于代碼的設(shè)計和實現(xiàn)捐腿,而備份側(cè)重于架構(gòu)設(shè)計或產(chǎn)品設(shè)計上。
8.6 如何優(yōu)化內(nèi)存和時間消耗
不同的應(yīng)用場景下有不同的解決方法柿顶。像上面的例子茄袖,我們只需要記錄每次變化后的數(shù)據(jù)長度,結(jié)合 InputText 原始數(shù)據(jù)就可以完成撤銷和恢復的工作嘁锯。
而對于備份的對象通常比較大宪祥,里面記錄了很多的狀態(tài)等信息,這個時候家乘,需要采用“低頻全量更新蝗羊、高頻增量更新的策略”。也就是在一段時間內(nèi)記錄只記錄每次變動的增量信息仁锯,當這段時間過后耀找,對數(shù)據(jù)進行一次全量備份,然后,再在一段時間內(nèi)記錄增量信息野芒,循環(huán)往復這個過程來完成備份的操作蓄愁。如果恢復數(shù)據(jù)呢?
通過要恢復的點往前找到最近一次全量備份的數(shù)據(jù)狞悲,然后撮抓,再把增量部分的數(shù)據(jù)進行累加,完成備份數(shù)據(jù)的恢復操作摇锋。
8.7 應(yīng)用場景
備忘錄模式主要是用來對數(shù)據(jù)進行備份丹拯,防止數(shù)據(jù)丟失、撤銷和恢復數(shù)據(jù)等應(yīng)用場景荸恕。
9. 命令模式
9.1 定義
命令模式將請求(命令)封裝為一個對象乖酬,這樣可以使用不同的請求參數(shù)化其它對象(將不同請求依賴注入到其它對象),并且能夠支持請求(命令)的排隊執(zhí)行融求、記錄日志咬像、撤銷等(附加控制)功能。
核心手段
將函數(shù)封裝成對象双肤。借助命令模式我們可以將函數(shù)封裝成對象。
具體實現(xiàn)
設(shè)計一個包含函數(shù)的類钮惠,實例化一個對象進行傳遞茅糜,這樣就近似地實現(xiàn)了把函數(shù)像對象一樣傳遞(主要是從設(shè)計意圖上來看)。從實現(xiàn)角度來看素挽,類似于回調(diào)蔑赘。
9.2 作用
- 控制命令的執(zhí)行,使用命令模式來模擬將函數(shù)像對象一樣傳遞预明,提高業(yè)務(wù)的易理解性及實現(xiàn)的靈活性缩赛。
- 封裝數(shù)據(jù)和數(shù)據(jù)處理邏輯到同一對象,提高了代碼的封裝性和易讀性撰糠。
9.3 類結(jié)構(gòu)圖
9.4 經(jīng)典實現(xiàn)
public interface Command {
void execute();
}
public class GotDiamondCommand implements Command {
// 省略成員變量
public GotDiamondCommand(/*數(shù)據(jù)*/) {
//...
}
@Override
public void execute() {
// 執(zhí)行相應(yīng)的邏輯
}
}
//GotStartCommand/HitObstacleCommand/ArchiveCommand類省略
public class GameApplication {
private static final int MAX_HANDLED_REQ_COUNT_PER_LOOP = 100;
private Queue<Command> queue = new LinkedList<>();
public void mainloop() {
while (true) {
List<Request> requests = new ArrayList<>();
//省略從epoll或者select中獲取數(shù)據(jù)酥馍,并封裝成Request的邏輯,
//注意設(shè)置超時時間阅酪,如果很長時間沒有接收到請求旨袒,就繼續(xù)下面的邏輯處理。
for (Request request : requests) {
Event event = request.getEvent();
Command command = null;
if (event.equals(Event.GOT_DIAMOND)) {
command = new GotDiamondCommand(/*數(shù)據(jù)*/);
} else if (event.equals(Event.GOT_STAR)) {
command = new GotStartCommand(/*數(shù)據(jù)*/);
} else if (event.equals(Event.HIT_OBSTACLE)) {
command = new HitObstacleCommand(/*數(shù)據(jù)*/);
} else if (event.equals(Event.ARCHIVE)) {
command = new ArchiveCommand(/*數(shù)據(jù)*/);
} // ...一堆else if...
queue.add(command);
}
int handledCount = 0;
while (handledCount < MAX_HANDLED_REQ_COUNT_PER_LOOP) {
if (queue.isEmpty()) {
break;
}
Command command = queue.poll();
command.execute();
}
}
}
}
9.5 設(shè)計模式兩個部分及不同設(shè)計模式的區(qū)分
第一部分為:應(yīng)用場景(設(shè)計意圖)术辐。即這個模式可以解決哪類問題砚尽。
第二部分為:解決方案。即這個模式的設(shè)計思想和代碼實現(xiàn)辉词。
設(shè)計模式的主要區(qū)別
不同設(shè)計模式的主要區(qū)別在于設(shè)計意圖必孤,也就是應(yīng)用場景。
9.5 命令模式和策略模式的區(qū)別
在策略模式中瑞躺,不同的策略具有相同的目的敷搪、不同的實現(xiàn)兴想、互相之間可以替換。而命令模式中购啄,不同的命令目的不同襟企,對應(yīng)的處理邏輯也不一樣,無法相互替換狮含。
9.6 應(yīng)用場景
用來控制命令的執(zhí)行顽悼。比如:異步、延遲几迄、排隊執(zhí)行命令蔚龙、撤銷重做命令、存儲命令等映胁。
10. 解釋器模式
10.1 定義
解釋器模式為某個語言定義它的語法表示木羹,并定義一個解釋器用來處理這些語法。
10.2 作用
- 通過定義語法和語法解釋器來應(yīng)對復雜的需求解孙,提高代碼的復用性坑填。
- 將語法解析的工作拆分到各個小類中,然后對每個語法單元進行解析弛姜,最終合并為對整個語法規(guī)則進行解析脐瑰。
10.3 類結(jié)構(gòu)圖
10.4 經(jīng)典實現(xiàn)
public class ExpressionInterpreter {
private Deque<Long> numbers = new LinkedList<>();
public long interpret(String expression) {
String[] elements = expression.split(" ");
int length = elements.length;
for (int i = 0; i < (length+1)/2; ++i) {
numbers.addLast(Long.parseLong(elements[i]));
}
for (int i = (length+1)/2; i < length; ++i) {
String operator = elements[i];
boolean isValid = "+".equals(operator) || "-".equals(operator)
|| "*".equals(operator) || "/".equals(operator);
if (!isValid) {
throw new RuntimeException("Expression is invalid: " + expression);
}
long number1 = numbers.pollFirst();
long number2 = numbers.pollFirst();
long result = 0;
if (operator.equals("+")) {
result = number1 + number2;
} else if (operator.equals("-")) {
result = number1 - number2;
} else if (operator.equals("*")) {
result = number1 * number2;
} else if (operator.equals("/")) {
result = number1 / number2;
}
numbers.addFirst(result);
}
if (numbers.size() != 1) {
throw new RuntimeException("Expression is invalid: " + expression);
}
return numbers.pop();
}
}
解耦之后的版本
public interface Expression {
long interpret();
}
public class NumberExpression implements Expression {
private long number;
public NumberExpression(long number) {
this.number = number;
}
public NumberExpression(String number) {
this.number = Long.parseLong(number);
}
@Override
public long interpret() {
return this.number;
}
}
public class AdditionExpression implements Expression {
private Expression exp1;
private Expression exp2;
public AdditionExpression(Expression exp1, Expression exp2) {
this.exp1 = exp1;
this.exp2 = exp2;
}
@Override
public long interpret() {
return exp1.interpret() + exp2.interpret();
}
}
// SubstractionExpression/MultiplicationExpression/DivisionExpression與AdditionExpression代碼結(jié)構(gòu)類似,這里就省略了
public class ExpressionInterpreter {
private Deque<Expression> numbers = new LinkedList<>();
public long interpret(String expression) {
String[] elements = expression.split(" ");
int length = elements.length;
for (int i = 0; i < (length+1)/2; ++i) {
numbers.addLast(new NumberExpression(elements[i]));
}
for (int i = (length+1)/2; i < length; ++i) {
String operator = elements[i];
boolean isValid = "+".equals(operator) || "-".equals(operator)
|| "*".equals(operator) || "/".equals(operator);
if (!isValid) {
throw new RuntimeException("Expression is invalid: " + expression);
}
Expression exp1 = numbers.pollFirst();
Expression exp2 = numbers.pollFirst();
Expression combinedExp = null;
if (operator.equals("+")) {
combinedExp = new AdditionExpression(exp1, exp2);
} else if (operator.equals("-")) {
combinedExp = new AdditionExpression(exp1, exp2);
} else if (operator.equals("*")) {
combinedExp = new AdditionExpression(exp1, exp2);
} else if (operator.equals("/")) {
combinedExp = new AdditionExpression(exp1, exp2);
}
long result = combinedExp.interpret();
numbers.addFirst(new NumberExpression(result));
}
if (numbers.size() != 1) {
throw new RuntimeException("Expression is invalid: " + expression);
}
return numbers.pop().interpret();
}
}
10.5 應(yīng)用場景
自定義接口告警規(guī)則功能
對自定義告警規(guī)則的語法規(guī)則進行解析廷臼,并通過解析出來的結(jié)果進行過濾苍在,滿足條件的即觸發(fā)告警通知。
public interface Expression {
boolean interpret(Map<String, Long> stats);
}
public class GreaterExpression implements Expression {
private String key;
private long value;
public GreaterExpression(String strExpression) {
String[] elements = strExpression.trim().split("\\s+");
if (elements.length != 3 || !elements[1].trim().equals(">")) {
throw new RuntimeException("Expression is invalid: " + strExpression);
}
this.key = elements[0].trim();
this.value = Long.parseLong(elements[2].trim());
}
public GreaterExpression(String key, long value) {
this.key = key;
this.value = value;
}
@Override
public boolean interpret(Map<String, Long> stats) {
if (!stats.containsKey(key)) {
return false;
}
long statValue = stats.get(key);
return statValue > value;
}
}
// LessExpression/EqualExpression跟GreaterExpression代碼類似荠商,這里就省略了
public class AndExpression implements Expression {
private List<Expression> expressions = new ArrayList<>();
public AndExpression(String strAndExpression) {
String[] strExpressions = strAndExpression.split("&&");
for (String strExpr : strExpressions) {
if (strExpr.contains(">")) {
expressions.add(new GreaterExpression(strExpr));
} else if (strExpr.contains("<")) {
expressions.add(new LessExpression(strExpr));
} else if (strExpr.contains("==")) {
expressions.add(new EqualExpression(strExpr));
} else {
throw new RuntimeException("Expression is invalid: " + strAndExpression);
}
}
}
public AndExpression(List<Expression> expressions) {
this.expressions.addAll(expressions);
}
@Override
public boolean interpret(Map<String, Long> stats) {
for (Expression expr : expressions) {
if (!expr.interpret(stats)) {
return false;
}
}
return true;
}
}
public class OrExpression implements Expression {
private List<Expression> expressions = new ArrayList<>();
public OrExpression(String strOrExpression) {
String[] andExpressions = strOrExpression.split("\\|\\|");
for (String andExpr : andExpressions) {
expressions.add(new AndExpression(andExpr));
}
}
public OrExpression(List<Expression> expressions) {
this.expressions.addAll(expressions);
}
@Override
public boolean interpret(Map<String, Long> stats) {
for (Expression expr : expressions) {
if (expr.interpret(stats)) {
return true;
}
}
return false;
}
}
public class AlertRuleInterpreter {
private Expression expression;
public AlertRuleInterpreter(String ruleExpression) {
this.expression = new OrExpression(ruleExpression);
}
public boolean interpret(Map<String, Long> stats) {
return expression.interpret(stats);
}
}
11. 中介者模式
11.1 定義
中介者模式定義了一個單獨的(中介)對象寂恬,來封裝一組對象之間的交互。將這組對象之間的交互委派給與中介對象交互莱没,來避免對象之間的直接交互初肉。
11.2 作用
- 將對象之間多對多復雜的交互關(guān)系,轉(zhuǎn)化為一對多的交互關(guān)系饰躲,大大降低對象之間交互的復雜性朴译。
- 每個對象只需要和中介者對象交互,提高了代碼可讀性和可維護性属铁。
11.3 類結(jié)構(gòu)圖
11.4 經(jīng)典實現(xiàn)
public interface Mediator {
void handleEvent(Component component, String event);
}
public class LandingPageDialog implements Mediator {
private Button loginButton;
private Button regButton;
private Selection selection;
private Input usernameInput;
private Input passwordInput;
private Input repeatedPswdInput;
private Text hintText;
@Override
public void handleEvent(Component component, String event) {
if (component.equals(loginButton)) {
String username = usernameInput.text();
String password = passwordInput.text();
//校驗數(shù)據(jù)...
//做業(yè)務(wù)處理...
} else if (component.equals(regButton)) {
//獲取usernameInput眠寿、passwordInput、repeatedPswdInput數(shù)據(jù)...
//校驗數(shù)據(jù)...
//做業(yè)務(wù)處理...
} else if (component.equals(selection)) {
String selectedItem = selection.select();
if (selectedItem.equals("login")) {
usernameInput.show();
passwordInput.show();
repeatedPswdInput.hide();
hintText.hide();
//...省略其他代碼
} else if (selectedItem.equals("register")) {
//....
}
}
}
}
public class UIControl {
private static final String LOGIN_BTN_ID = "login_btn";
private static final String REG_BTN_ID = "reg_btn";
private static final String USERNAME_INPUT_ID = "username_input";
private static final String PASSWORD_INPUT_ID = "pswd_input";
private static final String REPEATED_PASSWORD_INPUT_ID = "repeated_pswd_input";
private static final String HINT_TEXT_ID = "hint_text";
private static final String SELECTION_ID = "selection";
public static void main(String[] args) {
Button loginButton = (Button)findViewById(LOGIN_BTN_ID);
Button regButton = (Button)findViewById(REG_BTN_ID);
Input usernameInput = (Input)findViewById(USERNAME_INPUT_ID);
Input passwordInput = (Input)findViewById(PASSWORD_INPUT_ID);
Input repeatedPswdInput = (Input)findViewById(REPEATED_PASSWORD_INPUT_ID);
Text hintText = (Text)findViewById(HINT_TEXT_ID);
Selection selection = (Selection)findViewById(SELECTION_ID);
Mediator dialog = new LandingPageDialog();
dialog.setLoginButton(loginButton);
dialog.setRegButton(regButton);
dialog.setUsernameInput(usernameInput);
dialog.setPasswordInput(passwordInput);
dialog.setRepeatedPswdInput(repeatedPswdInput);
dialog.setHintText(hintText);
dialog.setSelection(selection);
loginButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
dialog.handleEvent(loginButton, "click");
}
});
regButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
dialog.handleEvent(regButton, "click");
}
});
//....
}
}
中介者模式的實現(xiàn)是有一定副作用的焦蘑,中介者模式里面不應(yīng)該包含太多具體實現(xiàn)的邏輯盯拱,而應(yīng)該只是作用類與類之間交互的調(diào)度存在的。
11.5 應(yīng)用場景
主要應(yīng)用于類與類之間的交互非常復雜的情況。
說明
此文是根據(jù)王爭設(shè)計模式之美相關(guān)專欄內(nèi)容整理而來狡逢,非原創(chuàng)宁舰。