前言 此篇博文內容續接的是 UML建模語言、設計原則、創建型設計模式 的內容,有興趣的可以點前面的鏈接去看一下 3.2、行為型 這類設計模式是專門用於:對象間的高效溝通和職責委派 * 3.2.1、責任鏈模式 定義:責任鏈模式又名職責鏈模式,指的是:對某個請求的所有處理構成一條鏈,如果鏈上的某一處理者 ...
前言
- 此篇博文內容續接的是 UML建模語言、設計原則、創建型設計模式 的內容,有興趣的可以點前面的鏈接去看一下
3.2、行為型
這類設計模式是專門用於:對象間的高效溝通和職責委派
* 3.2.1、責任鏈模式
定義:責任鏈模式又名職責鏈模式,指的是:對某個請求的所有處理構成一條鏈,如果鏈上的某一處理者可以處理,則處理後返回。如果不能處理則將請求傳遞給鏈上的下一個處理者
廢話文學:所謂責任鏈模式就是為了避免請求發送者與多個請求處理者耦合在一起,於是將所有請求的處理者通過前一對象記住其下一個對象的引用而連成一條鏈;當有請求發生時,可將請求沿著這條鏈傳遞,直到有對象處理它為止
場景理解:開發中的捕獲異常,只有遇到對應的某一個 或 某一類異常時,才會有對應的處理機制;還有Servlet編程中的Filter過濾器;以及SpringMVC執行原理(請求經過DispatcherServlet,然後經其轉到HandletMapping,然後通過HandlerExcuttionChain這條執行鏈將結果返回給DispatcherServlet.....,)
圖示理解:使用的是Servlet的Filter舉例
生活中的例子:跳槽離職,要找好多人審批、以及擊鼓傳花.....
使用場景:在處理消息的時候要過濾很多道時就可以使用責任鏈模式
責任鏈模式的角色
- 抽象處理者:一個請求介面,裡面包含抽象的處理請求的方法 和 後繼鏈接
- 具體處理者:抽象處理者的子類,實現了處理請求方法,判斷請求是否能夠處理,能則處理返回結果,否則將請求傳給它的後繼者
- 客戶端:創建處理鏈,並向鏈頭的具體處理者對象提交請求,它不關心處理細節和請求的傳遞過程
責任鏈模式的類圖
3.2.1.1、簡單邏輯
1、抽象處理者
package com.zixieqing.handler;
/**
* <p>@description : 該類功能 抽象處理者:做用戶名判斷
* </p>
* <p>@package : com.zixieqing</p>
* <p>@author : ZiXieqing</p>
* <p>@version : V1.0.0</p>
*/
public abstract class AbstractHakimHandler {
private AbstractHakimHandler next;
public AbstractHakimHandler getNext() {
return next;
}
public void setNext(AbstractHakimHandler next) {
this.next = next;
}
public abstract String hakim(String name);
}
2、具體處理者
-
package com.zixieqing.handler; import com.zixieqing.UsernameEnum; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * <p>@description : 該類功能 第一個處理者 * </p> * <p>@package : com.zixieqing</p> * <p>@author : ZiXieqing</p> * <p>@version : V1.0.0</p> */ public class OneHakimHandler extends AbstractHakimHandler{ private Logger logger = LoggerFactory.getLogger(OneHakimHandler.class); @Override public String hakim(String name) { logger.info("進入OneHakimHandler處理器"); // 如果當前處理者能處理該請求,則進行處理,返回結果 if (UsernameEnum.ZIXIEQING.toString().equals(name.trim().toUpperCase())) { return "歡迎:" + name + " 進入系統"; } // 如果當前處理者不能處理改請求,則丟給後繼者 if (null != getNext()) return getNext().hakim(name); return "沒有處理者能夠處理該請求"; } }
-
package com.zixieqing.handler; import com.zixieqing.UsernameEnum; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * <p>@description : 該類功能 第二個處理者 * </p> * <p>@package : com.zixieqing.handler</p> * <p>@author : ZiXieqing</p> * <p>@version : V1.0.0</p> */ public class TwoHakimHandler extends AbstractHakimHandler{ private Logger logger = LoggerFactory.getLogger(OneHakimHandler.class); @Override public String hakim(String name) { logger.info("進入TwoHakimHandler處理器"); if (UsernameEnum.ZIMINGXUAN.toString().equals(name.trim().toUpperCase())) { logger.info("正在進行數據查詢.........."); logger.info("查詢到用戶:{}", name); return "歡迎:" + name + " 進入系統"; } if (null != getNext()) return getNext().hakim(name); return "沒有處理者能夠處理該請求"; } }
-
上述涉及到的枚舉類
-
package com.zixieqing; /** * <p>@description : 該類功能 用戶名集合 * </p> * <p>@package : com.zixieqing</p> * <p>@author : ZiXieqing</p> * <p>@version : V1.0.0</p> */ public enum UsernameEnum { /** * 紫邪情 */ ZIXIEQING, /** * 紫明軒 */ ZIMINGXUAN, ; }
-
3、客戶端
package com.zixieqing;
import com.zixieqing.handler.OneHakimHandler;
import com.zixieqing.handler.TwoHakimHandler;
/**
* <p>@description : 該類功能 客戶端:這部分的代碼可以封裝起來,然後提供一個公共方法給外部調用即可
* </p>
* <p>@package : com.zixieqing</p>
* <p>@author : ZiXieqing</p>
* <p>@version : V1.0.0</p>
*/
public class APITest{
public static void main(String[] args) {
// 創建處理鏈 並 將請求丟給鏈頭的處理者
OneHakimHandler handler1 = new OneHakimHandler();
handler1.setNext(new TwoHakimHandler());
System.out.println(handler1.hakim("LISI"));
System.out.println("============華麗的分隔符=============");
System.out.println(handler1.hakim("ZIXIEQING"));
System.out.println("============華麗的分隔符=============");
System.out.println(handler1.hakim("zimingxuan"));
}
}
責任鏈模式的不足
- 不能保證每個請求一定能被處理,如:上面的“LISI",原因:因為請求沒有明確的接收者,所以一個請求可能一直傳到處理鏈的末端都還未被處理
- 責任鏈是否合理需要靠客戶端來進行創建,所以增加了客戶端的複雜性,可能出現客戶端中責任鏈的錯誤設置而導致系統出錯
- 如果客戶端中的責任鏈過長,那麼就需要很多個處理對象的參與,所以系統性能會受到影響
* 3.2.2、命令模式
定義:指的是請求以命令的形式包裹在對象中,並傳給調用對象。調用對象尋找可以處理該命令的合適的對象,並把該命令傳給相應的對象,該對象執行命令
命令模式是將操作請求(行為請求者)和邏輯實現(行為實現者)進行了分離,從而降低耦合、方便擴展
適用場景: 只要認為是命令的地方都可以使用,如:CMD、GUI界面中的按鈕............
命令模式類圖
- 場景理解:
- 電視機換台:1、想要換台(調用者:發起請求);2、按下遙控器的換台按鈕(將請求包裝成了命令對象);3、電視機進行換台(接收者:執行請求相應的操作)
- 去飯店吃飯:1、你要吃飯(調用者:你要菜單上的某菜,整個菜單就是所有可以調用的命令);2、跟服務員講你要菜單上的什麼菜(命令對象:服務員在本子上記下菜名,即:將請求轉成了命令對象);3、服務員將寫有菜名的紙交給廚師(接收者:廚師通過命令對象接收到調用者的請求,開始對應的操作:炒菜)
3.2.2.1、簡單邏輯
使用上面去飯店吃飯為例,對這個場景進行拆解,可以得到如下的邏輯圖
- 服務員:調用者
- 菜單:命令
- 廚師:接收者
- 邏輯順序是:調用者(invoker)——> 命令(command) ——> 接收者(receiver);而我寫代碼的習慣順序是逆向的
1、先決條件
- 菜組成
package com.zixieqing;
import lombok.Data;
import lombok.ToString;
import lombok.experimental.Accessors;
import java.util.List;
/**
* <p>@description : 該類功能 菜
* </p>
* <p>@package : com.zixieqing</p>
* <p>@author : ZiXieqing</p>
* <p>@version : V1.0.0</p>
*/
@Data
@ToString
@Accessors(chain = true)
public class Food {
/**
* 菜名
*/
private String name;
/**
* 油:菜油、豬油、地溝油(^_^)......
*/
private String oil;
/**
* 辣椒類型:有線辣椒、小米椒、青椒、朝天椒..........
*/
private String chili;
/**
* 配料:食鹽、味精、八角、糖、醬油、耗油、醋、料酒、薑、蔥、蒜.........
*/
private List<String> excipients;
/**
* 容器:碗、鍋、盤子
*/
private String container;
}
- 菜單枚舉類
package com.zixieqing;
/**
* <p>@description : 該類功能 菜單枚舉類:就是去點菜時需要看有哪些菜的菜單
* </p>
* <p>@package : com.zixieqing</p>
* <p>@author : ZiXieqing</p>
* <p>@version : V1.0.0</p>
*/
public enum MenuEnum {
/**
* 魚香肉絲
*/
YU_XIANG_ROU_SI("魚香肉絲", "18"),
/**
* 鴛鴦鍋
*/
YUAN_YANG_GUO("鴛鴦鍋", "50"),
;
private String foodName;
private String price;
MenuEnum(String foodName, String price) {
this.foodName = foodName;
this.price = price;
}
public String getFoodName() {
return foodName;
}
public void setFoodName(String foodName) {
this.foodName = foodName;
}
public String getPrice() {
return price;
}
public void setPrice(String price) {
this.price = price;
}
}
2、接收者:廚師
package com.zixieqing.command;
import com.zixieqing.Food;
import java.util.List;
/**
* <p>@description : 該類功能 廚師
* </p>
* <p>@package : com.zixieqing.command</p>
* <p>@author : ZiXieqing</p>
* <p>@version : V1.0.0</p>
*/
public interface IChef {
Food cook(List<String> foodList);
}
- 張師:專做火鍋系列
package com.zixieqing.command.impl;
import com.zixieqing.Food;
import com.zixieqing.MenuEnum;
import com.zixieqing.command.IChef;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
/**
* <p>@description : 該類功能 張師:假如他是專門做火鍋系列的
* </p>
* <p>@package : com.zixieqing.command.impl</p>
* <p>@author : ZiXieqing</p>
* <p>@version : V1.0.0</p>
*/
public class ZhangChefImpl implements IChef {
private Logger logger = LoggerFactory.getLogger(ZhangChefImpl.class);
@Override
public Food cook(List<String> foodList) {
for (String name : foodList) {
if (MenuEnum.YUAN_YANG_GUO.getFoodName().equals(name)) {
logger.info("張師正在準備做:{}", name);
List<String> excipients = new ArrayList<>();
excipients.add("食鹽半勺");
excipients.add("味精少量");
excipients.add("八角微量");
excipients.add("糖少許");
excipients.add("醬油半勺");
excipients.add("耗油微量");
excipients.add("蔥薑蒜適量");
Food food = new Food();
food.setName(name)
.setOil("菜油")
.setChili("朝天椒")
.setExcipients(excipients)
.setContainer("鴛鴦鍋");
logger.info("張師做好了:{},成品為:{}", name, food);
return food;
}
}
return null;
}
}
- 李師:專做炒菜系列
package com.zixieqing.command.impl;
import com.zixieqing.Food;
import com.zixieqing.MenuEnum;
import com.zixieqing.command.IChef;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
/**
* <p>@description : 該類功能 李師:假如他是專門做炒菜系列的
* </p>
* <p>@package : com.zixieqing.command.impl</p>
* <p>@author : ZiXieqing</p>
* <p>@version : V1.0.0</p>
*/
public class LiChefImpl implements IChef {
private Logger logger = LoggerFactory.getLogger(LiChefImpl.class);
@Override
public Food cook(List<String> foodList) {
for (String name : foodList) {
if (MenuEnum.YU_XIANG_ROU_SI.getFoodName().equals(name)) {
logger.info("李師正在準備做:{}", name);
List<String> excipients = new ArrayList<>();
excipients.add("食鹽少量");
excipients.add("味精少許");
excipients.add("蔥蒜少許");
Food food = new Food();
food.setName(name)
.setOil("豬油")
.setChili("青椒")
.setExcipients(excipients)
.setContainer("盤子");
logger.info("李師做好了:{},成品為:{}", name, food);
return food;
}
}
return null;
}
}
3、命令:菜單
package com.zixieqing.command;
import java.util.List;
/**
* <p>@description : 該類功能 菜單:就是服務員在紙上寫的那些菜名,即:命令對象
* </p>
* <p>@package : com.zixieqing.command</p>
* <p>@author : ZiXieqing</p>
* <p>@version : V1.0.0</p>
*/
public interface IMenu {
void make(List<String> foodList);
}
- 火鍋類型菜單
package com.zixieqing.command.impl;
import com.zixieqing.command.IChef;
import com.zixieqing.command.IMenu;
import java.util.List;
/**
* <p>@description : 該類功能 火鍋類型的菜單
* </p>
* <p>@package : com.zixieqing.command.impl</p>
* <p>@author : ZiXieqing</p>
* <p>@version : V1.0.0</p>
*/
public class HuoGuoMenuImpl implements IMenu {
private IChef chef;
public HuoGuoMenuImpl(IChef chef) {
this.chef = chef;
}
@Override
public void make(List<String> foodList) {
chef.cook(foodList);
}
}
- 炒菜類型菜單
package com.zixieqing.command.impl;
import com.zixieqing.command.IChef;
import com.zixieqing.command.IMenu;
import java.util.List;
/**
* <p>@description : 該類功能 炒菜類型的菜單
* </p>
* <p>@package : com.zixieqing.command.impl</p>
* <p>@author : ZiXieqing</p>
* <p>@version : V1.0.0</p>
*/
public class ChaoCaiMenuImpl implements IMenu {
private IChef chef;
public ChaoCaiMenuImpl(IChef chef) {
this.chef = chef;
}
@Override
public void make(List<String> foodList) {
chef.cook(foodList);
}
}
4、調用者:服務員
package com.zixieqing.command;
import com.zixieqing.MenuEnum;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
/**
* <p>@description : 該類功能 服務員
* </p>
* <p>@package : com.zixieqing.command</p>
* <p>@author : ZiXieqing</p>
* <p>@version : V1.0.0</p>
*/
public class Waiter {
private Logger logger = LoggerFactory.getLogger(Waiter.class);
/**
* 服務員本子上記的菜名
*/
private List<String> foodList = new ArrayList<>();
/**
* 本店菜單
*/
private MenuEnum[] values = MenuEnum.values();
private IMenu menu;
public Waiter(IMenu menu) {
this.menu = menu;
}
/**
* <p>@description : 該方法功能 點單
* </p>
* <p>@methodName : order</p>
* <p>@author: ZiXieqing</p>
* <p>@version: V1.0.0</p>
* @param foodName 菜名
*/
public void order(String foodName) {
for (MenuEnum value : values) {
// 判斷客人所說的菜名是否在本店的菜單中
if (foodName.trim().equals(value.getFoodName()))
this.foodList.add(foodName);
}
}
/**
* <p>@description : 該方法功能 下單
* </p>
* <p>@methodName : placeOrder</p>
* <p>@author: ZiXieqing</p>
* <p>@version: V1.0.0</p>
*
*/
public void placeOrder() {
menu.make(foodList);
foodList.clear();
}
}
5、測試
package com.zixieqing;
import com.zixieqing.command.Waiter;
import com.zixieqing.command.impl.ChaoCaiMenuImpl;
import com.zixieqing.command.impl.HuoGuoMenuImpl;
import com.zixieqing.command.impl.LiChefImpl;
import com.zixieqing.command.impl.ZhangChefImpl;
/**
* <p>@description : 該類功能 測試
* </p>
* <p>@package : com.zixieqing</p>
* <p>@author : ZiXieqing</p>
* <p>@version : V1.0.0</p>
*/
public class APITest {
public static void main(String[] args) {
Waiter waiter = new Waiter(new HuoGuoMenuImpl(new ZhangChefImpl()));
// 點單
waiter.order("鴛鴦鍋");
// 下單
waiter.placeOrder();
System.out.println("==========華麗的分隔符==========");
Waiter newWaiter = new Waiter(new ChaoCaiMenuImpl(new LiChefImpl()));
// 點單
newWaiter.order("魚香肉絲");
// 下單
newWaiter.placeOrder();
}
}
- 結果
11:43:54.317 [main] INFO c.z.command.impl.ZhangChefImpl - 張師正在準備做:鴛鴦鍋
11:43:54.320 [main] INFO c.z.command.impl.ZhangChefImpl - 張師做好了:鴛鴦鍋,成品為:Food(name=鴛鴦鍋, oil=菜油, chili=朝天椒, excipients=[食鹽半勺, 味精少量, 八角微量, 糖少許, 醬油半勺, 耗油微量, 蔥薑蒜適量], container=鴛鴦鍋)
==========華麗的分隔符==========
11:43:54.320 [main] INFO c.zixieqing.command.impl.LiChefImpl - 李師正在準備做:魚香肉絲
11:43:54.320 [main] INFO c.zixieqing.command.impl.LiChefImpl - 李師做好了:魚香肉絲,成品為:Food(name=魚香肉絲, oil=豬油, chili=青椒, excipients=[食鹽少量, 味精少許, 蔥蒜少許], container=盤子)
命令模式的優點:
- 將發出命令的責任和執行命令的責任分隔開(請求者 / 調用者 和 執行者 / 接收者沒有必然聯繫),請求的一方不必知道接收請求的一方的介面,更不必知道請求如何被接收、操作是否被執行、何時被執行,以及是怎麼被執行的,所以降低了請求發出者和請求執行者之間的耦合,當然也方便擴展(Command命令採用的是介面+實現類 或是 抽象類+實現類)
命令模式的缺點:
- 和簡單工廠模式差不多,要是命令Command很多的話,那麼實現類就會變多,造成類臃腫,比較繁瑣
* 3.2.3、迭代器模式
定義:迭代、迭代,就是遍歷唄,所以指的就是:提供一種順序訪問集合容器 / 複雜對象中各個元素的方法,又無須暴露集合容器的內部表示
本質:就是將一個集合容器的遍歷方法抽取出來,然後單獨弄到一個迭代器對象中,從而:做到讓我們以相同的方式,可以遍歷不同數據結構的集合容器(這也是這個模式的應用場景)
典型例子:List、Set系列的集合,他們都繼承了
Iterable
介面,都有Iterator
類型的iterator(
)方法,而hasNext()
和next()
方法就在Iterator
介面中
迭代器模式的角色
- 抽象迭代器(Iterator):負責定義訪問和遍歷元素的介面,通常包含
hasNext()
、next()
等方法 - 具體迭代器(Concretelterator):實現抽象迭代器介面中所定義的方法,完成對聚合對象的遍歷,記錄遍歷的當前位置
- 抽象容器(Aggregate):負責定義創建具體迭代器的介面、以及所謂的對集合容器 / 複雜對象的增刪改這些方法
- 具體容器(ConcreteAggregate):抽象容器的子類,負責創建具體迭代器 / 返回迭代器實例
迭代器模式的邏輯
- 從上面那個list的構成其實也知道了
3.2.3.1、簡單邏輯
- 註:下麵這個邏輯沒多大意思,就意思意思而已,只是為了理解邏輯,因為搞的就是套娃
1、抽象迭代器
package com.zixieqing.o1simple;
/**
* <p>@description : 該類功能 抽象迭代器
* </p>
* <p>@package : com.zixieqing.o1simple</p>
* <p>@author : ZiXieqing</p>
* <p>@version : V1.0.0</p>
*/
public interface Iterator<E> {
boolean hasNext();
E next();
}
2、具體迭代器
package com.zixieqing.o1simple.impl;
import com.zixieqing.o1simple.Iterator;
import java.util.List;
/**
* <p>@description : 該類功能 具體迭代器
* </p>
* <p>@package : com.zixieqing.o1simple.impl</p>
* <p>@author : ZiXieqing</p>
* <p>@version : V1.0.0</p>
*/
public class IteratorImpl<E> implements Iterator<E> {
/**
* 下一個元素的索引
*/
private int cursor;
/**
* 最後一個元素的索引,如果沒有就返回-1
*/
private int lastRet = -1;
private List<E> list;
public IteratorImpl(List<E> list) {
this.list = list;
}
@Override
public boolean hasNext() {
return this.cursor < list.size();
}
@Override
public E next() {
return this.list.get(cursor++);
}
}
3、抽象容器
package com.zixieqing.o1simple;
/**
* <p>@description : 該類功能 抽象容器
* </p>
* <p>@package : com.zixieqing.o1simple</p>
* <p>@author : ZiXieqing</p>
* <p>@version : V1.0.0</p>
*/
public interface ICollection<E> {
Iterator<E> iterator();
boolean add(E element);
}
4、具體容器
package com.zixieqing.o1simple;
import com.zixieqing.o1simple.impl.IteratorImpl;
import java.util.ArrayList;
import java.util.List;
/**
* <p>@description : 該類功能 具體容器
* </p>
* <p>@package : com.zixieqing.o1simple</p>
* <p>@author : ZiXieqing</p>
* <p>@version : V1.0.0</p>
*/
public class NewList<E> implements ICollection<E>{
private List<E> list = new ArrayList<>();
@Override
public Iterator<E> iterator() {
return new IteratorImpl<>(list);
}
@Override
public boolean add(E element) {
return list.add(element);
}
}
5、測試
package com.zixieqing;
import com.zixieqing.o1simple.Iterator;
import com.zixieqing.o1simple.NewList;
/**
* <p>@description : 該類功能 測試
* </p>
* <p>@package : com.zixieqing</p>
* <p>@author : ZiXieqing</p>
* <p>@version : V1.0.0</p>
*/
public class ApiTest {
public static void main(String[] args) {
NewList<Integer> newList = new NewList<>();
newList.add(1);
newList.add(2);
newList.add(3);
newList.add(4);
newList.add(5);
Iterator<Integer> iterator = newList.iterator();
while (iterator.hasNext())
System.out.println(iterator.next());
}
}
迭代器模式的優點:
- 在不需要暴露聚合對象的內部結構的情況下,可以讓我們以相同的方式遍歷不同數據結構(鏈表、數組、樹.......)的聚合對象 / 集合容器的各個元素
迭代器模式的缺點:
- 它的缺點和工廠方法模式的缺點是類似的,因為迭代器模式是將容器存儲數據和遍曆數據進行了分離,所以就會造成:新增聚合對象就可能需要新增迭代器類(主要為具體迭代器)
使用場景:
- 想要遍歷不同的聚合結構,這時提供一個統一的介面(迭代器)
- 想要為聚合對象提供多種遍歷方式
* 3.2.4、中介者模式
定義:我拿你的錢,辦你的事兒;所謂的中介者就是一個和事佬
處理的問題:對象之間存在的依賴關係(多個類相互耦合,形成了網狀結構),即:一個類的方法中用到了另一個的對象(可以是把A類對象當做B類方法的參數;可以是B類方法中new了A類的實例;也可以是B類中的屬性引用了A類對象[但:沒有new,即:聚合],然後在B類方法中new出實例。如果有很多個類之間存在依賴關係,那麼就會造成只要動一個類,則很多類都要進行整改,這就是所謂的牽一發而動全身,典型例子就是如下的關係:
中介者模式的角色
- 抽象中介者(Mediator):定義統一的介面,用於各同事角色之間的通信
- 具體中介者(ConcreteMediator):通過協調各同事對象實現協作行為,因此它必須依賴於各個同事角色
- 同事類(Colleague):每一個同事角色都知道中介者角色,而且與其他的同事角色通信的時候,一定要通過中介者角色協作。每個同事類的行為分為兩種:
- 一種是同事本身的行為,比如改變對象本身的狀態,處理自己的行為等,這種行為叫做自發行為,與其他的同事類或中介者沒有任何的依賴
- 第二種是必須依賴中介者才能完成的行為,叫做依賴方法
- 當然:根據需要也可以把同事類進行抽取,進而變成:抽象同事類+具體同事類
3.2.4.1、簡單邏輯
情景:男女分手+中間調和者,類圖邏輯如下:
1、抽象中介者 / 抽象協調者
package com.zixieqing.o1simple;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* <p>@description : 該類功能 抽象協調者
* </p>
* <p>@package : com.zixieqing.o1simple</p>
* <p>@author : ZiXieqing</p>
* <p>@version : V1.0.0</p>
*/
public abstract class Coordinator {
private Logger logger = LoggerFactory.getLogger(Coordinator.class);
protected Man man;
protected Woman woman;
public Coordinator() {
man = new Man(this);
woman = new Woman(this);
}
/**
* 中介者 / 協調者的核心方法:處理同事角色關係
*
* @param type 事件類型 1、傳話;2、退還物品
* @param thing 發生對應類型時要做的事
*/
public abstract void handler(int type, String thing);
/**
* 傳話
*
* @param context 傳話內容
*/
public abstract void message(String context);
/**
* 退還物品
*
* @param thing 要退還的東西
*/
public abstract void handover(String thing);
}
2、具體中介者
package com.zixieqing.o1simple;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* <p>@description : 該類功能 具體協調者
* </p>
* <p>@package : com.zixieqing.o1simple</p>
* <p>@author : ZiXieqing</p>
* <p>@version : V1.0.0</p>
*/
public class ConsreteConnrdinator extends Coordinator {
private Logger logger = LoggerFactory.getLogger(ConsreteConnrdinator.class);
/**
* 代表吵架
*/
private static final int QUARREL = 1;
/**
* 代表退還物品
*/
private static final int HANDOVER = 2;
/**
* MAG_FLAG 消息標識 1、給女傳話;2、給男傳話
* HANDOVER_FLAG 退還物品標識 1、給女退還物品;2、給男退還物品
*/
public static int MSG_FLAG = 1, HANDOVER_FLAG = 1;
/**
* 中介者 / 協調者的核心方法:處理同事角色關係
* @param type 事件類型 1、傳話;2、退還物品
* @param thing 發生對應類型時要做的事
*/
@Override
public void handler(int type, String thing) {
switch (type) {
case QUARREL:
this.message(thing);
break;
case HANDOVER:
this.handover(thing);
break;
default:
logger.info("貧道愛莫能助,只能擺爛..........");
}
}
/**
* 傳話
* @param context 傳話內容
*/
@Override
public void message(String context) {
if (1==MSG_FLAG)
super.woman.talkToFriend(context);
if (2==MSG_FLAG)
super.man.love(context);
}
/**
* 退還物品
*
* @param thing 要退還的東西
*/
@Override
public void handover(String thing) {
if (1==HANDOVER_FLAG)
super.woman.talkToFriend(thing);
if (2==HANDOVER_FLAG)
super.man.love(thing);
}
}
3、同事類:男人
package com.zixieqing.o1simple;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Scanner;
/**
* <p>@description : 該類功能 男人:同事類A
* </p>
* <p>@package : com.zixieqing.o1simple</p>
* <p>@author : ZiXieqing</p>
* <p>@version : V1.0.0</p>
*/
public class Man {
private Logger logger = LoggerFactory.getLogger(Man.class);
/**
* 聚合協調者
*/
private Coordinator coordinator;
/**
* 姓名
*/
private String name;
/**
* 年齡
*/
private int age;
/**
* 敏感詞
*/
private String filter = "不聽,不聽,分手";
public Man(Coordinator coordinator) {
this.coordinator = coordinator;
}
/**
* 吃
* @param foodName 食物名
*/
public void eat(String foodName) {
logger.info("這個男人今天吃了:{}", foodName);
}
/**
* 喝
* @param drinkName 飲料名
*/
public void drink(String drinkName) {
logger.info("這個男人剛剛喝了:{}", drinkName);
}
/**
* 做飯
* @param foodName 菜名
*/
public void cook(String foodName) {
logger.info("這個男人正在做:{}", foodName);
}
/**
* 談戀愛
* @param thing 做的是戀愛中的什麼事
*/
public void love(String thing) {
logger.info("女:{}", thing);
if (thing.trim().equals(filter)) {
ConsreteConnrdinator.MSG_FLAG = 1;
coordinator.handler(1,"你這娘們兒簡直不可理喻,分就分");
}
ConsreteConnrdinator.MSG_FLAG = 1;
Scanner input = new Scanner(System.in);
coordinator.handler(1,input.next());
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
4、同事類:女人
package com.zixieqing.o1simple;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Scanner;
/**
* <p>@description : 該類功能 女人:同事類B
* </p>
* <p>@package : com.zixieqing.o1simple</p>
* <p>@author : ZiXieqing</p>
* <p>@version : V1.0.0</p>
*/
public class Woman {
private Logger logger = LoggerFactory.getLogger(Woman.class);
private Coordinator coordinator;
private String name;
private int age;
/**
* 火藥桶
*/
private String gunpowder = "你這娘們兒簡直不可理喻,分就分";
public Woman(Coordinator coordinator) {
this.coordinator = coordinator;
}
/**
* 逛街購物
*/
public void shopping() {
logger.info("這個仙女今天去哪裡逛街,又買了xxxxxxxxxxx");
}
/**
* 耍朋友
*/
public void talkToFriend(String context) {
logger.info("男:{}",context);
// 如果觸碰火藥桶,那就退還物品
if (context.trim().equals(gunpowder)) {
logger.info("分就分,把他的破東西拿回去..........");
ConsreteConnrdinator.HANDOVER_FLAG = 2;
ConsreteConnrdinator.MSG_FLAG = 2;
Scanner input = new Scanner(System.in);
String goods = input.next();
// 退還物品
coordinator.handler(2,goods);
}
// 否則就是繼續交流
ConsreteConnrdinator.MSG_FLAG = 2;
Scanner input = new Scanner(System.in);
coordinator.handler(1,input.next());
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
5、測試
package com.zixieqing;
import com.zixieqing.o1simple.ConsreteConnrdinator;
import com.zixieqing.o1simple.Coordinator;
import com.zixieqing.o1simple.Man;
import com.zixieqing.o1simple.Woman;
/**
* <p>@description : 該類功能 測試
* </p>
* <p>@package : com.zixieqing</p>
* <p>@author : ZiXieqing</p>
* <p>@version : V1.0.0</p>
*/
public class ApiTest {
public static void main(String[] args) {
Coordinator connrdinator = new ConsreteConnrdinator();
Woman woman = new Woman(connrdinator);
// 同事類的獨有方法
woman.shopping();
Man man = new Man(connrdinator);
// 同事類獨有方法
man.eat("閉門羹");
man.drink("寂寞");
man.cook("空氣");
// 提供給外部,進行協調的方法
woman.talkToFriend("");
// 提供給外部,進行協調的方法
man.love("");
}
}
自娛自樂的聊天室就來了
當然:中介者被稱為協調者,那也得協調協調,做點事情嘛,不能只當個傳話筒,這個就根據開發場景自由發揮了
同時:上面的同事類是可以抽離成一個介面,變成抽象同事類+實現類
分析一丟丟中介者模式:
- 1、每個同事類對象只與中介者產生依賴關係,同事類彼此之間不產生依賴
- 2、具體協調者中加入了用private修飾的用於處理各同事類關係的方法,從而讓一個對象依賴多個對象的情況移到這個中介者中來進行處理了
- 3、同事類中都加入了中介者,從而讓各個同事類都具有中介者的特性,這樣做之後:各個同事類就可以只負責自己的行為(獨有方法),而不需要自己負責的 / 需要協調的就丟給中介者進行處理(這樣做也取消了各個同事類之間產生依賴或關聯關係)
- 同事類只負責自己的行為,不需要負責的就丟給中介者處理,這樣好處就是:各個同事類之間可以不用知道彼此的存在,它只需要和中介者打交道即可
- 同事類只負責自己的行為,需要協調的就丟給中介者進行處理,這樣可能會造成這個模式濫用的情況(慎用),因為這樣做一不註意就可能導致:同事類之間知道彼此的存在了,即:各同事類之間一不註意就產生關聯或依賴關係,那這模式架構就當白做了(別被我上面弄的男女耍朋友給搞混了,誰跟你說他們知道彼此存在的,我沒說,萬一這裡面有故事呢)
當然上面這些都可以說是中介者模式的優點,相應地就有缺點
- 原本是各個同事類之間產生依賴或關聯關係,現在是變成同事類和中介者之間產生依賴關係,這樣就造成:同事類越多,那麼中介者就越腫大(需要聚合的同事類就變多,核心方法[事件方法]中需要處理的邏輯就越複雜),這個缺點也是決定到底要不要用中介者模式的核心,否則就成為亂整,要用這個模式就一定要考慮是否會讓中介者變得腫大的問題
3.2.5、備忘錄模式
定義:備忘錄模式又稱為快照模式,備忘、備忘嘛,指的就是在不破壞封裝性的前提下,獲取到一個對象的內部狀態,併在對象之外記錄或保存這個狀態,在有需要的時候可將該對象恢復到原先保存的狀態。使用備忘錄模式可以對操作對象提供回滾操作,但對資源消耗過大,每操作一次都需要記錄操作前數據
人話:就是為了防丟失、撤銷、恢復等事情的(即:需要記錄一個對象的內部狀態,目的就是為了允許用戶取消不確定或者錯誤的操作,能夠恢復到他原先的狀態,有 "後悔藥" 可吃,這也是這個模式的適用場景)
註意上面的官方話定義:
獲取對象內部狀態(內部還有什麼狀態,就屬性值唄),併在對象之外保存這個狀態(即:在要保存的對象中會有一個save方法用來把其當前屬性值保存到另一個對象[即:備忘錄對象]中
在有需要時可將對象恢復到原先保存的狀態:也就是說在要保存對象中還會有一個提供和撤銷 / 回滾類似的方法,從而讓對象回到上一步的狀態
備忘錄模式的角色
-
備忘錄(Memento):負責存儲發起人的內部狀態,在需要的時候提供這些內部狀態給發起人
-
發起人(Originator):需要備忘的對象,記錄當前時刻的內部狀態信息,提供將當前時刻的內部狀態狀態信息保存到備忘錄中,並將備忘錄對象返回 以及 從備忘錄中記錄的狀態信息恢復此對象狀態的方法
-
負責人(Caretaker):用於管理備忘錄,提供保存與獲取備忘錄對象的功能(getter、setter),但其不能直接對備忘錄中的內容進行操作(要操作備忘錄中的內容只能找備忘錄本身才可以)
備忘錄模式邏輯關係草圖
3.4.5.1、簡單邏輯
1、備忘錄:存儲發起人的內部狀態信息,併在需要時能將數據交出去
package com.zixieqing.o1simple;
/**
* <p>@description : 該類功能 備忘錄:存儲發起人的內部狀態信息,併在需要時能夠將存儲的數據交出去
* </p>
* <p>@package : com.zixieqing.o1simple</p>
* <p>@author : ZiXieqing</p>
* <p>@version : V1.0.0</p>
*/
public class Memento {
private Object state;
public Memento(Object state) {
this.state = state;
}
public Object getState() {
return state;
}
}
2、負責人:管理備忘錄,提供獲取、保存備忘錄對象的方法,但不可對備忘錄中的內容進行操作
package com.zixieqing.o1simple;
/**
* <p>@description : 該類功能 負責人:管理備忘錄,提供獲取、保存備忘錄對象的方法,但不可以對備忘錄中的內容進行操作
* </p>
* <p>@package : com.zixieqing.o1simple</p>
* <p>@author : ZiXieqing</p>
* <p>@version : V1.0.0</p>
*/
public class Caretaker {
private Memento memento;
public Memento getMemento() {
return memento;
}
public void setMemento(Memento memento) {
this.memento = memento;
}
}
3、發起人:要進行備忘的對象,記錄當前時刻的內部屬性信息到備忘錄中 並返回備忘錄對象,同時在需要時可從備忘錄中恢復當前對象的內部信息
package com.zixieqing.o1simple;
/**
* <p>@description : 該類功能 發起人:需要進行備忘的對象,記錄當前時刻的內部屬性信息到備忘錄中,並返回備忘錄對象
* 併在需要時可從備忘錄中記錄的狀態信息恢複數據
* </p>
* <p>@package : com.zixieqing.o1simple</p>
* <p>@author : ZiXieqing</p>
* <p>@version : V1.0.0</p>
*/
public class Orginal {
private Object field;
/**
* 保存當前時刻的狀態信息到備忘錄中,並返回備忘錄對象
*/
public Memento saveToMemento() {
return new Memento(this.field);
}
/**
* 從備忘錄中恢復當前對象的內部狀態信息
* @param memento 備忘錄
*/
public void rollbackFromMemento(Memento memento) {
this.field = memento.getState();
}
@Override
public String toString() {
return "Orginal{" +
"field=" + field +
'}';
}
public Object getField() {
return field;
}
public void setField(Object field) {
this.field = field;
}
}
4、測試
package com.zixieqing;
import com.zixieqing.o1simple.Memento;
import com.zixieqing.o1simple.Orginal;
/**
* <p>@description : 該類功能 測試
* </p>
* <p>@package : com.zixieqing</p>
* <p>@author : ZiXieqing</p>
* <p>@version : V1.0.0</p>
*/
public class ApiTest {
public static void main(String[] args) {
// 要進行保存的對象
Orginal orginal = new Orginal();
orginal.setField("稀里嘩啦保存了一堆的東西");
// 將對象的當前時刻的內部信息保存到備忘錄中
Memento memento = orginal.saveToMemento();
System.out.println(orginal);
// 模擬
orginal.setField("噼里啪啦又是一頓操作,保存了數據");
System.out.println(orginal);
System.out.println("=============蕪湖~斷電咯================");
System.out.println("。。。。。。。。。。。。。。。。。。。。");
System.out.println("=============耶吼~電力恢復啦=============");
// 從記錄的備忘錄中恢復對象內部狀態
orginal.rollbackFromMemento(memento);
System.out.println("=============對象恢復之後的狀態===========");
System.out.println(orginal);
}
}
備忘錄模式的缺點:
- 如果要保存的對象的內部狀態信息(屬性)很多的話,那記憶體資源消耗是很大的,所以有時可以使用單例、命令模式這些來進行數據保存,當然更可以通過第三方中間件保存
* 3.2.6、觀察者模式
定義:指的是多個觀察者同時監聽一個主題對象,每當主題對象發生變化時,都會通知監聽 / 依賴它的所有觀察者,從而讓觀察者自動更新自身相關的數據。因此主題對象和觀察者之間就是一對多的依賴關係(這定義就是它的適用場景)
核心:將觀察者和被觀察者進行瞭解耦,並通過類似於消息發送的機制讓兩者進行聯動,從而做到被觀察者發生變化時,監聽 / 依賴於它的所有觀察者都會得到通知並做出響應(更新自身數據)
觀察者模式的角色(可以當做發佈訂閱模式來理解,但:這二者不能完全等價,發佈訂閱模式在觀察者和被觀察者之間再套了一層[套的這一層就是發佈訂閱中的事件機制 Event Channel],自行面向百度編程):
- 抽象主題(Subject):被觀察的對象,是介面或抽象類。也就是發佈者,包含了:增加、刪除、通知觀察者對象的方法
- 具體主題(ConcreteSubject):具體被觀察者,是抽象主題的子類。當其內部狀態發生改變時會通知所有依賴與它的觀察者
- 抽象觀察者(Observer):觀察者模型,也就是訂閱者,定義了響應通知的更新方法
- 具體觀察者(ConcreteObserver):是抽象觀察者的子類,在收到主題對象狀態發生改變的通知後,會立刻對其進行響應,並更新自身的相關數據
觀察者模式邏輯草圖
3.2.6.1、簡單邏輯
場景:博主與粉絲,博主發文章,粉絲收到通知
1、先決條件:文章類
package com.zixieqing.o1simple;
import lombok.Data;
import lombok.ToString;
import lombok.experimental.Accessors;
import java.util.Date;
/**
* <p>@description : 該類功能 文章
* </p>
* <p>@package : com.zixieqing.o1simple</p>
* <p>@author : ZiXieqing</p>
* <p>@version : V1.0.0</p>
*/
@Data
@ToString
@Accessors(chain = true)
public class Article {
/**
* 文章標題
*/
private String title;
/**
* 文章內容
*/
private String context;
/**
* 發佈人編號
*/
private Long publisherId;
/**
* 發佈時間
*/
private Date createTime;
/**
* 文章所屬標簽
*/
private String articleLabel;
}
2、博主介面:抽象主題 即發佈者,包含增加、刪除、通知觀察者對象等方法
package com.zixieqing.o1simple;
/**
* <p>@description : 該類功能 博主介面:抽象主題 包含增加、刪除、通知觀察者對象的方法
* </p>
* <p>@package : com.zixieqing.o1simple</p>
* <p>@author : ZiXieqing</p>
* <p>@version : V1.0.0</p>
*/
public interface IBlogger {
/**
* 將粉絲添加到通知列表中,能夠讓粉絲接受到通知
* @param fan 粉絲
* @return true / false
*/
boolean add(IFan fan);
/**
* 將粉絲拉入黑名單,不讓其接收到通知
* @param fan 粉絲
* @return true / false
*/
boolean remove(IFan fan);
/**
* 通知觀察者
* @param article 消息體
*/
void notice(Article article);
}
- 具體博主:具體主題
package com.zixieqing.o1simple.impl;
import com.zixieqing.o1simple.Article;
import com.zixieqing.o1simple.IBlogger;
import com.zixieqing.o1simple.IFan;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
/**
* <p>@description : 該類功能 一個叫紫邪情的厚臉皮博主
* </p>
* <p>@package : com.zixieqing.o1simple.impl</p>
* <p>@author : ZiXieqing</p>
* <p>@version : V1.0.0</p>
*/
public class Zixieqing implements IBlogger {
private Logger logger = LoggerFactory.getLogger(Zixieqing.class);
/**
* 粉絲列表:一對多
*/
private List<IFan> fanList = new ArrayList<>();
@Override
public boolean add(IFan fan) {
if (!fanList.contains(fan))
return fanList.add(fan);
return false;
}
@Override
public boolean remove(IFan fan) {
if (!add(fan))
return fanList.remove(fan);
return false;
}
@Override
public void notice(Article article) {
for (IFan fan : fanList) {
fan.response(article);
}
}
}
- 註:還是補上吧,上面通知的方法,若是再加上"滿足什麼條件就觸發通知方法",把這句話再進行抽取一下搞到另外合適的地方去,就變成了觸發機制,即:被觀察者滿足什麼條件觸發通知,從而讓所依賴的所有觀察者都收到通知,並更新自身相關數據。這裡抽取出來的觸發機制就是事件通道Event channel,抽取出來之後,這個觀察者模式就可以變成發佈-訂閱模式了
3、粉絲介面:即訂閱者 得到通知之後進行響應並改變自身和主題相關的數據
package com.zixieqing.o1simple;
/**
* <p>@description : 該類功能 粉絲介面:抽象觀察者 得到通知之後,更新自身相關數據 / 做自己要做的事情
* </p>
* <p>@package : com.zixieqing.o1simple</p>
* <p>@author : ZiXieqing</p>
* <p>@version : V1.0.0</p>
*/
public abstract class IFan {
public abstract boolean response(Article article);
}
- 具體粉絲Q
package com.zixieqing.o1simple.impl;
import com.zixieqing.o1simple.Article;
import com.zixieqing.o1simple.IFan;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
/**
* <p>@description : 該類功能 一個叫Q的粉絲
* </p>
* <p>@package : com.zixieqing.o1simple.impl</p>
* <p>@author : ZiXieqing</p>
* <p>@version : V1.0.0</p>
*/
public class QFan extends IFan {
private Logger logger = LoggerFactory.getLogger(QFan.class);
/**
* 粉絲感興趣的博文列表
*/
private static List<String> interestList = new ArrayList<>();
static {
interestList.add("設計模式");
interestList.add("愛碼有道");
interestList.add("Java");
}
@Override
public boolean response(Article article) {
for (String interest : interestList) {
if (article.getTitle().equals(interest)) {
logger.info("粉絲:{},接收了通知:{},並開始去做一系列和自身數據相關的事情",
this.getClass().getSimpleName(),
article);
return true;
}
}
return false;
}
}
- 具體粉絲X
package com.zixieqing.o1simple.impl;
import com.zixieqing.o1simple.Article;
import com.zixieqing.o1simple.IFan;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
/**
* <p>@description : 該類功能 一個叫X的粉絲
* </p>
* <p>@package : com.zixieqing.o1simple.impl</p>
* <p>@author : ZiXieqing</p>
* <p>@version : V1.0.0</p>
*/
public class XFan extends IFan {
private Logger logger = LoggerFactory.getLogger(XFan.class);
/**
* 粉絲感興趣的博文列表
*/
private static List<String> interestList = new ArrayList<>();
static {
interestList.add("消息中間件");
interestList.add("愛碼有道");
interestList.add("分散式事務");
interestList.add("分散式事務");
}
@Override
public boolean response(Article article) {
for (String interest : interestList) {
if (article.getTitle().equals(interest)) {
logger.info("粉絲:{},接收了通知:{},並開始去做一系列和自身數據相關的事情",
this.getClass().getSimpleName(),
article);
return true;
}
}
return false;
}
}
4、測試
package com.zixieqing;
import com.zixieqing.o1simple.Article;
import com.zixieqing.o1simple.IBlogger;
import com.zixieqing.o1simple.impl.QFan;
import com.zixieqing.o1simple.impl.XFan;
import com.zixieqing.o1simple.impl.Zixieqing;
import java.util.Date;
/**
* <p>@description : 該類功能 測試
* </p>
* <p>@package : com.zixieqing</p>
* <p>@author : ZiXieqing</p>
* <p>@version : V1.0.0</p>
*/
public class ApiTest {
public static void main(String[] args) {
IBlogger blogger = new Zixieqing();
// 讓指定粉絲能收到通知
QFan qFan = new QFan();
XFan xFan = new XFan();
blogger.add(qFan);
blogger.add(xFan);
// 博主開始寫文章
Article article = new Article();
String context = "設計莫斯一共有23種,分為創建型、行為型、結構型,這三類其實就是不同層別的架構," +
"如:創建型就是專門用於new實例的架構;即:對象,而行為型體現在行為上,也就是所謂的方法," +
"變來變去其實本質就是對類中方法的架構" +
"學完全部設計模式之後,感受就會起來了,然後只需要知曉每個設計模式解決的場景是什麼," +
"最後開發中遇到對應的設計模式場景就套用,然後思路就出來,也熟練了";
article.setTitle("設計模式")
.setPublisherId(System.nanoTime())
.setContext(context)
.setArticleLabel("設計")
.setCreateTime(new Date());
// 將文章推給粉絲
blogger.notice(article);
System.out.println("=============華麗的分隔符===============");
// blogger.remove(xFan);
article.setTitle("愛碼有道")
.setPublisherId(System.nanoTime())
.setContext("正式開發時還是遵循開發規範比較好")
.setArticleLabel("代碼整潔")
.setCreateTime(new Date());
// 將文章推給粉絲
blogger.notice(article);
}
}
3.2.6.2、分析觀察者模式
觀察者模式的優點
- 被觀察者(Subject主題) 和 觀察者(Observer)是松耦合的,包含關係嘛,同時也是面向介面編程的(即:符合依賴倒置原則)
- 將表示層(觀察者) 和 數據邏輯層(被觀察者)進行了分離,只要被觀察者的自身狀態發生改變就可以讓觀察者相應的數據也發生改變,即:制定了一套觸發機制,從而做到數據邏輯層的數據變化可以相應到多個表示層上
- 使用了一對多的機制,被觀察者的狀態發生改變,只有滿足同一套觸發機制的觀察者才能接收到通知,這樣的機制可以做很多事,比如:興趣分發、事件註冊等機制
觀察者模式的缺點
- 因為觀察者是被放在被觀察者的一個容器中,那眾多的觀察者在這個容器中就產生了類似於鏈化的情況,而只要是容器那就逃不開遍歷的事(像鏈表一樣,或者說線性關係更恰當),這就有缺點了,若是觀察者很多的話,那通知所有的觀察者就需要耗費時間了
- 基於前面的線性關係的情況,還有造成一個問題:只要一個觀察者沒處理好,導致出現卡死的情況,那麼後續的觀察者也別通知了(換言之:後續觀察者可能出現接收不到通知的情況),相當於後續觀察者死翹翹了
* 3.2.7、狀態模式
定義:一個對象在其內部狀態改變時改變它的行為
解決的問題:解決複雜對象的狀態轉換以及不同狀態下行為的封裝問題,一句話:對象在不同狀態下有不同行為時就可以使用狀態模式
核心:將一個對象的狀態從該對象中分離出來,封裝到專門的狀態類中(抽象狀態+實現類),從而使得對象狀態可以靈活變化
本質:在代碼中對對象屬性進行的大量
if-else
判斷進行抽離(如代碼:if(p.getxxxx().equals("yyyy")){}
,狀態模式解決的差不多就是這種代碼),在對象內部提供外部調用的方法,從而讓其自身根據狀態的不同去走對應的邏輯(狀態實現類中的邏輯),如:一個界面登錄是一種樣子,未登錄又是另一種樣子,狀態不同行為不同。這也是這個模式的適用場景,即:代碼中包含大量與對象狀態有關的條件語句時(if-else
和switch
)就可以使用狀態模式進行改造
狀態模式的角色
- 上下文(Context):狀態所屬者,維護具體狀態類的實例,這個實例存儲其當前狀態
- 抽象狀態(State):封裝與Context的一個特定狀態相關的行為操作(這裡面可以定義多個方法的,這是和命令模式的一個小區別)
- 具體狀態(ConcreteState):每一個具體的狀態類實現一個與Context的一個狀態相關的行為,在需要時也可以進行狀態切換
狀態模式的邏輯草圖
3.2.7.1、簡單邏輯
場景:去ATM中取款