這類設計模式是專門用於:物件間的高效溝通和職責委派
定義:責任鏈模式又名職責鏈模式,指的是:對某個請求的所有處理構成一條鏈,如果鏈上的某一處理者可以處理,則處理後返回。如果不能處理則將請求傳遞給鏈上的下一個處理者
廢話文學:所謂責任鏈模式就是為了避免請求傳送者與多個請求處理者耦合在一起,於是將所有請求的處理者通過前一物件記住其下一個物件的參照而連成一條鏈;當有請求發生時,可將請求沿著這條鏈傳遞,直到有物件處理它為止
場景理解:開發中的捕獲異常,只有遇到對應的某一個 或 某一類異常時,才會有對應的處理機制;還有Servlet程式設計中的Filter過濾器;以及SpringMVC執行原理(請求經過DispatcherServlet,然後經其轉到HandletMapping,然後通過HandlerExcuttionChain這條執行鏈將結果返回給DispatcherServlet.....,)
圖示理解:使用的是Servlet的Filter舉例
生活中的例子:跳槽離職,要找好多人審批、以及擊鼓傳花.....
使用場景:在處理訊息的時候要過濾很多道時就可以使用責任鏈模式
責任鏈模式的角色
責任鏈模式的類圖
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"));
}
}
責任鏈模式的不足
定義:指的是請求以命令的形式包裹在物件中,並傳給呼叫物件。呼叫物件尋找可以處理該命令的合適的物件,並把該命令傳給相應的物件,該物件執行命令
命令模式是將操作請求(行為請求者)和邏輯實現(行為實現者)進行了分離,從而降低耦合、方便擴充套件
適用場景: 只要認為是命令的地方都可以使用,如:CMD、GUI介面中的按鈕............
命令模式類圖
使用上面去飯店吃飯為例,對這個場景進行拆解,可以得到如下的邏輯圖
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=盤子)
命令模式的優點:
命令模式的缺點:
定義:迭代、迭代,就是遍歷唄,所以指的就是:提供一種順序存取集合容器 / 複雜物件中各個元素的方法,又無須暴露集合容器的內部表示
本質:就是將一個集合容器的遍歷方法抽取出來,然後單獨弄到一個迭代器物件中,從而:做到讓我們以相同的方式,可以遍歷不同資料結構的集合容器(這也是這個模式的應用場景)
典型例子:List、Set系列的集合,他們都繼承了
Iterable
介面,都有Iterator
型別的iterator(
)方法,而hasNext()
和next()
方法就在Iterator
介面中
迭代器模式的角色
hasNext()
、next()
等方法迭代器模式的邏輯
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());
}
}
迭代器模式的優點:
迭代器模式的缺點:
使用場景:
定義:我拿你的錢,辦你的事兒;所謂的中介者就是一個和事佬
處理的問題:物件之間存在的依賴關係(多個類相互耦合,形成了網狀結構),即:一個類的方法中用到了另一個的物件(可以是把A類物件當做B類方法的引數;可以是B類方法中new了A類的範例;也可以是B類中的屬性參照了A類物件[但:沒有new,即:聚合],然後在B類方法中new出範例。如果有很多個類之間存在依賴關係,那麼就會造成只要動一個類,則很多類都要進行整改,這就是所謂的牽一髮而動全身,典型例子就是如下的關係:
中介者模式的角色
情景:男女分手+中間調和者,類圖邏輯如下:
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("");
}
}
自娛自樂的聊天室就來了
當然:中介者被稱為協調者,那也得協調協調,做點事情嘛,不能只當個傳話筒,這個就根據開發場景自由發揮了
同時:上面的同事類是可以抽離成一個介面,變成抽象同事類+實現類
分析一丟丟中介者模式:
當然上面這些都可以說是中介者模式的優點,相應地就有缺點
定義:備忘錄模式又稱為快照模式,備忘、備忘嘛,指的就是在不破壞封裝性的前提下,獲取到一個物件的內部狀態,並在物件之外記錄或儲存這個狀態,在有需要的時候可將該物件恢復到原先儲存的狀態。使用備忘錄模式可以對操作物件提供回滾操作,但對資源消耗過大,每操作一次都需要記錄操作前資料
人話:就是為了防丟失、復原、恢復等事情的(即:需要記錄一個物件的內部狀態,目的就是為了允許使用者取消不確定或者錯誤的操作,能夠恢復到他原先的狀態,有 "後悔藥" 可吃,這也是這個模式的適用場景)
注意上面的官方話定義:
獲取物件內部狀態(內部還有什麼狀態,就屬性值唄),並在物件之外儲存這個狀態(即:在要儲存的物件中會有一個save方法用來把其當前屬性值儲存到另一個物件[即:備忘錄物件]中
在有需要時可將物件恢復到原先儲存的狀態:也就是說在要儲存物件中還會有一個提供和復原 / 回滾類似的方法,從而讓物件回到上一步的狀態
備忘錄模式的角色
備忘錄(Memento):負責儲存發起人的內部狀態,在需要的時候提供這些內部狀態給發起人
發起人(Originator):需要備忘的物件,記錄當前時刻的內部狀態資訊,提供將當前時刻的內部狀態狀態資訊儲存到備忘錄中,並將備忘錄物件返回 以及 從備忘錄中記錄的狀態資訊恢復此物件狀態的方法
負責人(Caretaker):用於管理備忘錄,提供儲存與獲取備忘錄物件的功能(getter、setter),但其不能直接對備忘錄中的內容進行操作(要操作備忘錄中的內容只能找備忘錄本身才可以)
備忘錄模式邏輯關係草圖
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);
}
}
備忘錄模式的缺點:
定義:指的是多個觀察者同時監聽一個主題物件,每當主題物件發生變化時,都會通知監聽 / 依賴它的所有觀察者,從而讓觀察者自動更新自身相關的資料。因此主題物件和觀察者之間就是一對多的依賴關係(這定義就是它的適用場景)
核心:將觀察者和被觀察者進行了解耦,並通過類似於訊息傳送的機制讓兩者進行聯動,從而做到被觀察者發生變化時,監聽 / 依賴於它的所有觀察者都會得到通知並做出響應(更新自身資料)
觀察者模式的角色(可以當做釋出訂閱模式來理解,但:這二者不能完全等價,釋出訂閱模式在觀察者和被觀察者之間再套了一層[套的這一層就是釋出訂閱中的事件機制 Event Channel],自行面向百度程式設計):
觀察者模式邏輯草圖
場景:博主與粉絲,博主發文章,粉絲收到通知
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);
}
}
}
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);
}
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;
}
}
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);
}
}
觀察者模式的優點
觀察者模式的缺點
定義:一個物件在其內部狀態改變時改變它的行為
解決的問題:解決複雜物件的狀態轉換以及不同狀態下行為的封裝問題,一句話:物件在不同狀態下有不同行為時就可以使用狀態模式
核心:將一個物件的狀態從該物件中分離出來,封裝到專門的狀態類中(抽象狀態+實現類),從而使得物件狀態可以靈活變化
本質:在程式碼中對物件屬性進行的大量
if-else
判斷進行抽離(如程式碼:if(p.getxxxx().equals("yyyy")){}
,狀態模式解決的差不多就是這種程式碼),在物件內部提供外部呼叫的方法,從而讓其自身根據狀態的不同去走對應的邏輯(狀態實現類中的邏輯),如:一個介面登入是一種樣子,未登入又是另一種樣子,狀態不同行為不同。這也是這個模式的適用場景,即:程式碼中包含大量與物件狀態有關的條件語句時(if-else
和switch
)就可以使用狀態模式進行改造
狀態模式的角色
狀態模式的邏輯草圖
場景:去ATM中取款
1、抽象狀態:ATM狀態對應的行為封裝
package com.zixieqing;
/**
* <p>@description : 該類功能 抽象狀態:ATM狀態對應的行為
* 封裝與Context狀態所屬者的狀態 / 屬性相關的行為
* </p>
* <p>@package : com.zixieqing</p>
* <p>@author : ZiXieqing</p>
* <p>@version : V1.0.0</p>
*/
public interface ATMState {
/**
* 插卡
*/
void insertCard();
/**
* 提交密碼
*/
void submitPwd();
/**
* 取款
*/
void getCash();
/**
* 查詢餘額
*/
void queryBalance();
/**
* 退卡
*/
void checkOut();
}
2、具體狀態
package com.zixieqing.impl;
import com.zixieqing.ATMContext;
import com.zixieqing.ATMState;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* <p>@description : 該類功能 具體狀態:準備狀態
* 具體狀態:1、實現Context的一個狀態的相關行為邏輯
* 2、在需要時進行狀態切換
* 狀態切換可以是本狀態中的不同行為切換,也可以是切換到不同具體狀態者
* </p>
* <p>@package : com.zixieqing.impl</p>
* <p>@author : ZiXieqing</p>
* <p>@version : V1.0.0</p>
*/
public class ReadyState implements ATMState {
private Logger logger = LoggerFactory.getLogger(ReadyState.class);
/**
* 依賴:保留狀態所屬者Context的參照,作用:方便對其進行操作
* 介面ATMState 是對狀態進行了抽象
* 實現類(ReadyState或其他實現類)是具體的狀態
* 這些具體狀態者(實現類)都只是狀態本身而已,而狀態所屬者是Context,最後狀態是要回到所屬者身邊的
* 如果這個具體狀態者中沒有 對 狀態所屬者Context進行相應操作,那:具體狀態者 和 狀態所屬者就是割裂 / 分開的
*/
private ATMContext atmContext;
public ReadyState(ATMContext atmContext) {
this.atmContext = atmContext;
}
@Override
public void insertCard() {
logger.info("完成插卡");
}
@Override
public void submitPwd() {
logger.info("正在進行密碼校驗............");
if ("123456".equals(atmContext.getPwd())) {
logger.info("密碼校驗成功");
}else {
logger.info("密碼不正確");
// 狀態切換:退卡 / 重新進入準備狀態..........
checkOut();
}
}
@Override
public void getCash() {
// 如果ATM中沒錢了,那就不能再提供服務
if (atmContext.getATMBalance() == 0) {
logger.info("無法使用服務,請去另外機子進行業務辦理");
// 彈卡
checkOut();
/*
狀態切換
注意這裡:這裡沒有用new,而是用的atmContext的setter,這也是前面說保留atmContext參照的好處之一
好處:降低耦合(迪米特原則:最少知道原則),獲取彈性
*/
atmContext.setCurrState(atmContext.getNoServiceState());
}else {
if (atmContext.getMoney() <= atmContext.getATMBalance() && atmContext.getMoney() <= atmContext.getBalance()) {
// 出鈔、減少使用者的賬戶餘額
logger.info("出鈔¥:{}",atmContext.getMoney());
atmContext.setBalance(atmContext.getBalance() - atmContext.getMoney());
// 減少ATM中的鈔票金額
atmContext.setATMBalance(atmContext.getATMBalance() - atmContext.getMoney());
// 列印發票、回到準備狀態...........
// 彈卡
checkOut();
}else {
logger.info("餘額不足");
checkOut();
}
}
}
@Override
public void queryBalance() {
logger.info("賬戶餘額為:{}", atmContext.getBalance());
}
@Override
public void checkOut() {
logger.info("退卡成功");
}
}
package com.zixieqing.impl;
import com.zixieqing.ATMContext;
import com.zixieqing.ATMState;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* <p>@description : 該類功能 具體狀態:停止服務
* </p>
* <p>@package : com.zixieqing.impl</p>
* <p>@author : ZiXieqing</p>
* <p>@version : V1.0.0</p>
*/
public class NoServiceState implements ATMState {
private ATMContext atmContext;
public NoServiceState(ATMContext atmContext) {
this.atmContext = atmContext;
}
private Logger logger = LoggerFactory.getLogger(NoServiceState.class);
@Override
public void insertCard() {
logger.info("服務停止,請去其他取款機進行業務辦理");
}
@Override
public void submitPwd() {
logger.info("服務停止,請去其他取款機進行業務辦理");
}
@Override
public void getCash() {
logger.info("服務停止,請去其他取款機進行業務辦理");
}
@Override
public void queryBalance() {
logger.info("服務停止,請去其他取款機進行業務辦理");
}
@Override
public void checkOut() {
logger.info("服務停止,請去其他取款機進行業務辦理");
}
}
3、核心:Context上下文物件
package com.zixieqing;
import com.zixieqing.impl.NoServiceState;
import com.zixieqing.impl.ReadyState;
import lombok.Data;
import lombok.ToString;
import lombok.experimental.Accessors;
/**
* <p>@description : 該類功能 Context上下文物件(這個Context是核心)
* 狀態所屬者:1、維護具體狀態者範例;
* 2、也可以有它自己的特有狀態;
* 3、將狀態改變時其對應的行為交給對應的狀態物件,即:狀態改變時讓其走對應的邏輯(供外部呼叫的)
* 上面2、3根據情況決定可有可無
* </p>
* <p>@package : com.zixieqing</p>
* <p>@author : ZiXieqing</p>
* <p>@version : V1.0.0</p>
*/
@Data
@ToString
@Accessors(chain = true)
public class ATMContext {
// 假設此Context物件有如下的獨有狀態(實質是:老衲待會兒測試需要,一次性寫在這裡了)
/**
* 密碼
*/
private String pwd;
/**
* 使用者取款金額
*/
private int money;
/**
* 使用者的賬戶餘額(假設為int型別)
*/
private int balance;
/**
* ATM機中的鈔票餘額
*/
private int ATMBalance;
// 下面這些就是這個Context所維護的具體狀態者範例
/**
* 當前狀態
*/
private ATMState currState;
/**
* 準備狀態
*/
private ATMState readyState;
/**
* 無服務狀態
*/
private ATMState noServiceState;
/**
* 初始化所有狀態
* @param pwd 密碼
* @param money 使用者取款金額
* @param balance 使用者的賬戶餘額
* @param ATMBalance ATM機中的鈔票餘額
*/
public ATMContext(String pwd, int money, int balance, int ATMBalance) throws Exception {
this.pwd = pwd;
this.money = money;
this.balance = balance;
this.ATMBalance = ATMBalance;
this.readyState = new ReadyState(this);
this.noServiceState = new NoServiceState(this);
if (this.getATMBalance() > 0) {
this.currState = readyState;
} else if (this.getATMBalance() < 0) {
this.currState = noServiceState;
}else {
throw new Exception();
}
}
// 下面這些就是這個Context在其狀態改變時將具體行為委託給具體狀態物件(可以直接供外部呼叫)
/**
* 轉換到插卡行為
*/
public void insertCard() {
this.currState.insertCard();
}
/**
* 提交密碼
*/
public void submitPwd() {
this.currState.submitPwd();
}
/**
* 取款
*/
public void getCash() {
this.currState.getCash();
}
/**
* 查詢餘額
*/
public void queryBalance() {
this.currState.queryBalance();
}
public void checkOut() {
this.currState.checkOut();
}
}
4、測試
package com.zixieqing;
/**
* <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) throws Exception {
ATMContext atmContext = new ATMContext("123456", 300, 200000, 100000);
System.out.println("===============初始狀態===================");
System.out.println(atmContext);
atmContext.insertCard();
atmContext.submitPwd();
atmContext.getCash();
atmContext.queryBalance();
atmContext.checkOut();
System.out.println("===============結束狀態===================");
System.out.println(atmContext);
}
}
===============初始狀態===================
ATMContext(pwd=123456, money=300, balance=200000, ATMBalance=100000, currState=com.zixieqing.impl.ReadyState@5d6f64b1, readyState=com.zixieqing.impl.ReadyState@5d6f64b1, noServiceState=com.zixieqing.impl.NoServiceState@32a1bec0)
17:50:01.410 [main] INFO com.zixieqing.impl.ReadyState - 完成插卡
17:50:01.413 [main] INFO com.zixieqing.impl.ReadyState - 正在進行密碼校驗............
17:50:01.413 [main] INFO com.zixieqing.impl.ReadyState - 密碼校驗成功
17:50:01.413 [main] INFO com.zixieqing.impl.ReadyState - 出鈔¥:300
17:50:01.413 [main] INFO com.zixieqing.impl.ReadyState - 退卡成功
17:50:01.413 [main] INFO com.zixieqing.impl.ReadyState - 賬戶餘額為:199700
17:50:01.413 [main] INFO com.zixieqing.impl.ReadyState - 退卡成功
===============結束狀態===================
ATMContext(pwd=123456, money=300, balance=199700, ATMBalance=99700, currState=com.zixieqing.impl.ReadyState@5d6f64b1, readyState=com.zixieqing.impl.ReadyState@5d6f64b1, noServiceState=com.zixieqing.impl.NoServiceState@32a1bec0)
狀態模式的優點
if-else
或switch
),通過Context狀態所屬者中的狀態行為託管,將屬性 / 狀態的邏輯處理遷移到了State子類來進行,降低了相互間的依賴。使程式碼結構清晰的同時也易擴充套件和易維護狀態模式的缺點
使用狀態模式的建議:
1、具體狀態類一般不要超過5個左右,不然維護起來也是很麻煩的,因為每一個具體狀態類中不同行為邏輯本身就很複雜,而且不同行為之間會進行切換,甚至會切換到另外的狀態去,例子如下(這還不是複雜的,一般真要用這個模式時複雜程度不算低):
這兩個模式的思路,或者說結構吧,是有點像的,但還是有區別的,還有一個策略模式,但這個還沒玩呢,後續比較時再加入
狀態模式和命令模式最大的區別是下面這些
execute()
介面就行(當然:也不是一定要叫這個介面名定義:又名政策模式(Policy),指的是針對一組演演算法,將每一個演演算法封裝到具有共同介面的獨立的類中,使得它們可以互換(實現同一個介面:里氏替換原則),可以讓演演算法隨著呼叫者的變化而變化
解決的問題 / 適用場景:在有多種演演算法相似(同類可替代的行為邏輯演演算法)的情況下,去掉使用條件分支語句(
if-else
或switch
)所帶來的複雜性和難以維護場景理解:不同型別的交易方式(微信、支付寶、銀行卡);生成唯一主鍵策略(UUID、DB自增、DB自增+Redis、Leaf演演算法),這些就可以使用策略模式對不同的行為進行封裝,供外部呼叫
策略模式的角色
上下文 / 環境類(Context):負責查詢要做什麼。這裡面也可以封裝著具體策略物件需要的資料,具體策略物件通過回撥上下文的方法來獲取這些資料,這玩意兒可以在必要時當做引數(即:關聯)傳給具體策略物件
抽象策略(Strategy):演演算法的抽象 / 演演算法的規範。介面或抽象類都可以
具體策略(ConcreteStrategy):具體的某一個演演算法 / 行為實現。若是有必要可以從上下文中回撥它裡面的方法來獲取所需要的資料
策略模式的邏輯草圖(可以對照命令模式來看,二者結構很像的)
場景:就用上面說的不同型別交易方式(微信、支付寶)
1、上下文Context:在必要時儲存具體策略需要的資料;負責呼叫者要做什麼
package com.zixieqing;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* <p>@description : 該類功能 上下文 / 環境類
* 1、可以在必要時儲存具體策略物件需要的資料
* 2、負責呼叫者要做什麼
* </p>
* <p>@package : com.zixieqing</p>
* <p>@author : ZiXieqing</p>
* <p>@version : V1.0.0</p>
*/
public class PayContext {
private Logger logger = LoggerFactory.getLogger(PayContext.class);
private PayStrategy payStrategy;
/**
* 讓呼叫者自己決定用哪種支付策略
* @param payStrategy 支付方式
*/
public PayContext(PayStrategy payStrategy) {
this.payStrategy = payStrategy;
}
/**
* 負責要做什麼:支付
*/
public void pay() {
this.payStrategy.pay();
}
}
2、抽象策略:對同組不同演演算法進行抽象,制定規範
package com.zixieqing;
/**
* <p>@description : 該類功能 抽象策略:對同組不同演演算法進行抽象
* </p>
* <p>@package : com.zixieqing</p>
* <p>@author : ZiXieqing</p>
* <p>@version : V1.0.0</p>
*/
public interface PayStrategy {
void pay();
}
package com.zixieqing.impl;
import com.zixieqing.PayStrategy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* <p>@description : 該類功能 具體策略:微信支付方式
* </p>
* <p>@package : com.zixieqing.impl</p>
* <p>@author : ZiXieqing</p>
* <p>@version : V1.0.0</p>
*/
public class WeChatPayImpl implements PayStrategy {
private Logger logger = LoggerFactory.getLogger(WeChatPayImpl.class);
@Override
public void pay() {
logger.info("本微信系統:收到商戶平臺中傳來的 建立訂單請求");
logger.info("本微信系統:正在建立 預付單");
logger.info("本微信系統:預付單生成成功:返回商戶平臺預付單標識");
logger.info("這中間就是商戶平臺生成帶簽名的支付資訊、使用者發起支付請求、商戶平臺找微信使用者端調起微信支付");
logger.info("本微信系統:收到微信使用者端(即:微信APP)發起的支付請求,開始驗證支付授權許可權");
logger.info("本微信系統:給微信使用者端返回支付授權");
logger.info("使用者:確認支付、輸入密碼,即提交授權給本微信支付系統");
logger.info("本微信系統:正在驗證支付授權");
logger.info("本微信系統:非同步通知商戶平臺:支付結果");
logger.info("商戶平臺:儲存支付通知,並返回本微信支付系統成功接收、處理與否");
logger.info("本微信系統:給微信使用者端返回支付結果,並行微信訊息提醒(即:微信中的那個微信支付的訊息)");
logger.info("最後就是另外的一堆邏輯處理情況");
}
}
package com.zixieqing.impl;
import com.zixieqing.PayStrategy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* <p>@description : 該類功能 具體策略:支付寶支付
* </p>
* <p>@package : com.zixieqing.impl</p>
* <p>@author : ZiXieqing</p>
* <p>@version : V1.0.0</p>
*/
public class AliPayImpl implements PayStrategy {
private Logger logger = LoggerFactory.getLogger(AliPayImpl.class);
@Override
public void pay() {
logger.info("支付寶支付和微信支付邏輯差不多的,假設這裡就是一堆的支付寶支付邏輯,意思意思");
}
}
3、測試
package com.zixieqing;
import com.zixieqing.impl.AliPayImpl;
import com.zixieqing.impl.WeChatPayImpl;
/**
* <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) {
PayContext payContext = new PayContext(new WeChatPayImpl());
payContext.pay();
System.out.println("==================華麗的分隔符====================");
PayContext payContext1 = new PayContext(new AliPayImpl());
payContext1.pay();
}
}
16:56:43.248 [main] INFO com.zixieqing.impl.WeChatPayImpl - 本微信系統:收到商戶平臺中傳來的 建立訂單請求
16:56:43.250 [main] INFO com.zixieqing.impl.WeChatPayImpl - 本微信系統:正在建立 預付單
16:56:43.250 [main] INFO com.zixieqing.impl.WeChatPayImpl - 本微信系統:預付單生成成功:返回商戶平臺預付單標識
16:56:43.250 [main] INFO com.zixieqing.impl.WeChatPayImpl - 這中間就是商戶平臺生成帶簽名的支付資訊、使用者發起支付請求、商戶平臺找微信使用者端調起微信支付
16:56:43.250 [main] INFO com.zixieqing.impl.WeChatPayImpl - 本微信系統:收到微信使用者端(即:微信APP)發起的支付請求,開始驗證支付授權許可權
16:56:43.250 [main] INFO com.zixieqing.impl.WeChatPayImpl - 本微信系統:給微信使用者端返回支付授權
16:56:43.250 [main] INFO com.zixieqing.impl.WeChatPayImpl - 使用者:確認支付、輸入密碼,即提交授權給本微信支付系統
16:56:43.251 [main] INFO com.zixieqing.impl.WeChatPayImpl - 本微信系統:正在驗證支付授權
16:56:43.251 [main] INFO com.zixieqing.impl.WeChatPayImpl - 本微信系統:非同步通知商戶平臺:支付結果
16:56:43.251 [main] INFO com.zixieqing.impl.WeChatPayImpl - 商戶平臺:儲存支付通知,並返回本微信支付系統成功接收、處理與否
16:56:43.251 [main] INFO com.zixieqing.impl.WeChatPayImpl - 本微信系統:給微信使用者端返回支付結果,並行微信訊息提醒(即:微信中的那個微信支付的訊息)
16:56:43.251 [main] INFO com.zixieqing.impl.WeChatPayImpl - 最後就是另外的一堆邏輯處理情況
==================華麗的分隔符====================
16:56:43.251 [main] INFO com.zixieqing.impl.AliPayImpl - 支付寶支付和微信支付邏輯差不多的,假設這裡就是一堆的支付寶支付邏輯,意思意思
策略模式的優點
符合開閉原則,當要新增策略時,只需要去實現介面,然後編寫相應的策略邏輯即可
由於實現同一介面,同時在Context上下文中擁有介面的參照,所以方便使用者端對不同策略進行自由切換
策略模式的缺點
狀態模式和命令模式已在前面玩狀態模式時進行了對比
這二者可以說是"親兄弟",二者在結構上很像,但是二者有區別
這二者在結構上"有點像",命令模式可以抽象地認為是一種策略模式(命令模式多了一個"接收者") 且 命令處理的問題也更復雜,二者區別如下:
首先從定義上來看:
策略模式:是同組不同演演算法。它認為"演演算法"是一個已經不可拆分的業務(即:一件事,如:支付),只是這個業務有不同方式的實現而已,目的是為了讓每個"具體演演算法"獨立,並彼此之間可替代,讓使用者端來抉擇使用哪一種"演演算法"。所以:此模式的關注重心在於"演演算法"的可替代問題
命令模式:它是將動作進行解耦(即:將請求包裹在命令物件中傳給呼叫物件,由呼叫物件來找能處理該命令的物件進行處理),換言之:就是將一個動作分為了執行行為(命令物件)、執行物件(接收者角色),讓這二者彼此獨立而不相互影響。所以:此模式的關注重心在於動作的解耦問題
負責點不同(策略模式的抽象策略+具體策略、命令的接收者[也可拆為抽象接收者+具體接收者])
適用場景不同
定義:模板模式也可以叫模板方法模式,指的是:在一個方法中定義一個演演算法的骨架(即:演演算法的結構),而一些其他非公共的步驟推遲到子類中去實現
一句話:封裝公有部分,擴充套件非公有
場景理解:建房子。工地上會把地基、走線、水管.....通用的東西搭建好(毛坯房),然後根據情況加壁櫃、加柵欄....這些有差異的就是其他接手了這個毛坯房的裝修者來弄
適用場景:有多個子類共有的方法,並且邏輯相同;當然對於重要的、複雜的方法,也可以使用模板模式
注意點:因為是在父類別中定義"演演算法"骨架,並實現一些公有操作,而子類是不可以改變這個"演演算法"骨架的,因此:模板方法一般加上
final
關鍵字,即:不可更改
模板方法模式的角色:
模板方法模式的邏輯草圖:
場景:搞豆漿,分為選原料、新增原料、浸泡、打碎,新增原料是非公有的(可為紅豆、綠豆、黑豆、赤小豆.........)
1、抽象模板
package com.zixieqing;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* <p>@description : 該類功能 抽象模板
* 1、定義模板方法 制定要做的這件事的演演算法結構
* 2、封裝公有部分 實現其邏輯
* 3、抽象非公有部分 等著子類來實現邏輯
* </p>
* <p>@package : com.zixieqing</p>
* <p>@author : ZiXieqing</p>
* <p>@version : V1.0.0</p>
*/
public abstract class AbstractSoyMilk {
private Logger logger = LoggerFactory.getLogger(AbstractSoyMilk.class);
/**
* 模板方法:制定"演演算法"結構
*/
protected final void make() {
// 1、選擇原料
choose();
// 2、新增調料
add();
// 3、浸泡
soak();
// 4、打碎
smashed();
}
/**
* 新增調料:非公有邏輯交由子類來實現
*/
protected abstract void add();
/**
* 選擇原料:封裝公有部分
*/
protected void choose() {
logger.info("{} 正在準備新增原料 ",this.getClass().getSimpleName());
logger.info("正在選擇原料");
}
/**
* 浸泡:封裝公有部分
*/
protected void soak() {
logger.info("{} 正在準備新增原料 ",this.getClass().getSimpleName());
logger.info("正在進行浸泡!");
}
/**
* 打碎:封裝公有部分
*/
protected void smashed() {
logger.info("{} 正在準備新增原料 ",this.getClass().getSimpleName());
logger.info("正在進行打碎");
}
}
2、具體模板
package com.zixieqing;
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 RedBeans extends AbstractSoyMilk{
private Logger logger = LoggerFactory.getLogger(RedBeans.class);
@Override
protected void add() {
logger.info("{} 正在準備新增原料 ",this.getClass().getSimpleName());
logger.info("價值3個W的紅豆已新增");
}
}
package com.zixieqing;
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 GreenBeans extends AbstractSoyMilk{
private Logger logger = LoggerFactory.getLogger(GreenBeans.class);
@Override
protected void add() {
logger.info("{} 正在準備新增原料 ",this.getClass().getSimpleName());
logger.info("已經新增成功適合你顏色的綠豆");
}
}
3、測試
package com.zixieqing;
/**
* <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) {
// 紅豆型豆漿
AbstractSoyMilk redBeans = new RedBeans();
redBeans.make();
System.out.println("=====================華麗的分隔符==================");
// 綠豆型豆漿
AbstractSoyMilk greenBeans = new GreenBeans();
greenBeans.make();
}
}
15:05:06.896 [main] INFO com.zixieqing.AbstractSoyMilk - RedBeans 正在準備新增原料
15:05:06.899 [main] INFO com.zixieqing.AbstractSoyMilk - 正在選擇原料
15:05:06.899 [main] INFO com.zixieqing.RedBeans - RedBeans 正在準備新增原料
15:05:06.899 [main] INFO com.zixieqing.RedBeans - 價值3個W的紅豆已新增
15:05:06.899 [main] INFO com.zixieqing.AbstractSoyMilk - RedBeans 正在準備新增原料
15:05:06.899 [main] INFO com.zixieqing.AbstractSoyMilk - 正在進行浸泡!
15:05:06.899 [main] INFO com.zixieqing.AbstractSoyMilk - RedBeans 正在準備新增原料
15:05:06.899 [main] INFO com.zixieqing.AbstractSoyMilk - 正在進行打碎
=====================華麗的分隔符==================
15:05:06.899 [main] INFO com.zixieqing.AbstractSoyMilk - GreenBeans 正在準備新增原料
15:05:06.899 [main] INFO com.zixieqing.AbstractSoyMilk - 正在選擇原料
15:05:06.899 [main] INFO com.zixieqing.GreenBeans - GreenBeans 正在準備新增原料
15:05:06.899 [main] INFO com.zixieqing.GreenBeans - 已經新增成功適合你顏色的綠豆
15:05:06.899 [main] INFO com.zixieqing.AbstractSoyMilk - GreenBeans 正在準備新增原料
15:05:06.899 [main] INFO com.zixieqing.AbstractSoyMilk - 正在進行浸泡!
15:05:06.899 [main] INFO com.zixieqing.AbstractSoyMilk - GreenBeans 正在準備新增原料
15:05:06.899 [main] INFO com.zixieqing.AbstractSoyMilk - 正在進行打碎
模板方法模式的優點
模板方法模式的缺點
定義·:使用了一個存取者類,它改變了元素類的執行演演算法。通過這種方式,元素的執行演演算法可以隨著存取者改變而改變。 元素物件已接受存取者物件,這樣存取者物件就可以處理元素物件上的操作
解決的問題: 穩定的資料結構和易變的操作耦合問題(方式:在被存取的類裡面加一個對外提供接待存取者的介面)
適用場景: 需要對一個物件結構中的物件進行很多不同的並且不相關的操作,而需要避免讓這些操作"玷汙"這些物件的類(即:這些物件所在類的原有的資料結構),使用存取者模式將這些封裝到類中。通俗易懂的話:就是一個東西不同的人看到的是不一樣的結果也可以用,如:公司查賬和稅務局查賬
存取者模式的角色
1、首先知道原有資料結構指的是什麼。 肯定用過如下的程式碼
@Data
public class Person {
private String name;
}
class Test{
public static void main(String[] args) {
Person person = new Person();
person.setName("紫邪情");
System.out.println(person.getName());
}
}
Person
就是資料結構,而name
就是所謂的元素,而person.setName("紫邪情")
就是對元素的操作,而存取者模式就是解決對這種資料結構中的物件(元素)進行很多不同且不相關的操作,從而避免這些操作"玷汙"這些物件(元素,如上面的name·
)的類(如上面的Person
)的原有資料結構(即:Person
原來的結構)2、抽象元素:接收存取者能夠存取的方法
name
,因此:對類的所有要進行不同且不相關操作的屬性 進行 抽象化/**
* <p>@description : 該類功能 抽象元素:把原有物件的所有屬性 進行 抽象化
* 1、接收存取者存取的方法
* </p>
* <p>@package : com.zixieqing.o1derive</p>
* <p>@author : ZiXieqing</p>
* <p>@version : V1.0.0</p>
*/
public interface People {
/**
* 接收存取者存取的方法 方法名不一定是這個,
* 根據對要進行不同且不相關操作的屬性操作的抽象化命名即可
* @param visitor 存取者
*/
void accept(Visitor visitor);
}
Person
)中要進行不同且不相關操作的屬性(如:name
......),這些每一個屬性就是一個具體元素(子類),同時:這些子類都有存取者的參照,所以都能夠讓存取者存取了;相應地,具體元素中的構成就變成要對這個元素(屬性)進行的那些不同且不相關的操作(如:簡單的那種setter,但不是真搞這種無趣的操作)package com.zixieqing.o1derive.impl;
import com.zixieqing.o1derive.People;
import com.zixieqing.o1derive.Visitor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* <p>@description : 該類功能 具體元素
* 1、讓存取者能夠對原有類中的屬性進行操作
* </p>
* <p>@package : com.zixieqing.o1derive.impl</p>
* <p>@author : ZiXieqing</p>
* <p>@version : V1.0.0</p>
*/
public class Name implements People {
private Logger logger = LoggerFactory.getLogger(Name.class);
@Override
public void accept(Visitor visitor) {
logger.info("{}這個類就是原來類中的屬性,這個{}類中就可以進行這個{}屬性的不同且不相關的操作",
this.getClass().getSimpleName(),
this.getClass().getSimpleName(),
this.getClass().getSimpleName().toLowerCase());
}
}
3、存取者:提供存取所有元素的方法 存取所有元素,就是存取原有類(如:Perosn
)的屬性,而現在屬性變成了一個單獨的類(即:具體元素)
import com.zixieqing.o1derive.impl.Name;
/**
* <p>@description : 該類功能 抽象存取者:提供能夠存取所有元素(原來類中屬性)的方法
* </p>
* <p>@package : com.zixieqing.o1derive</p>
* <p>@author : ZiXieqing</p>
* <p>@version : V1.0.0</p>
*/
public interface Visitor {
/**
* 存取原有類的name屬性 / 存取name元素
* 要是還有其他的就繼續加 如:sex........
* @param name 要存取的元素
*/
void visit(Name name);
}
package com.zixieqing.o1derive.impl;
import com.zixieqing.o1derive.Visitor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* <p>@description : 該類功能 具體存取者
* </p>
* <p>@package : com.zixieqing.o1derive.impl</p>
* <p>@author : ZiXieqing</p>
* <p>@version : V1.0.0</p>
*/
public class ConcreteVisitor implements Visitor {
private Logger logger = LoggerFactory.getLogger(ConcreteVisitor.class);
@Override
public void visit(Name name) {
logger.info("{}存取者即將開始元素存取",this.getClass().getSimpleName());
// 需要存取者,當前這個就是一個具體的存取者,當然:也可以根據情況來弄
name.accept(this);
}
}
4、物件結構:維護元素、提供存取者存取所有元素的方法
package com.zixieqing.o1derive;
import java.util.ArrayList;
import java.util.List;
/**
* <p>@description : 該類功能 物件結構
* 1、新增元素 / 維護元素
* 2、讓存取者能存取所有元素
* </p>
* <p>@package : com.zixieqing.o1derive</p>
* <p>@author : ZiXieqing</p>
* <p>@version : V1.0.0</p>
*/
public class SubjectStructure {
private List<People> peoples = new ArrayList<>();
/**
* 新增元素
* @param people 要新增的元素
* @return true/false
*/
public boolean addElement(People people) {
return peoples.add(people);
}
/**
* 讓存取者能夠存取所有元素
* @param visitor 存取者
*/
public void getPeoples(Visitor visitor) {
for (People people : peoples) {
people.accept(visitor);
}
}
}
5、現在我們要新增元素、獲取元素就找"物件結構"+存取者
package com.zixieqing;
import com.zixieqing.o1derive.SubjectStructure;
import com.zixieqing.o1derive.impl.ConcreteVisitor;
import com.zixieqing.o1derive.impl.Name;
/**
* <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) {
SubjectStructure subjectStructure = new SubjectStructure();
// 新增要存取的元素
subjectStructure.addElement(new Name());
// 通過物件結構+具體存取者進行元素存取
subjectStructure.getPeoples(new ConcreteVisitor());
}
}
因此:現在這個模式的邏輯草圖就出來了
場景:同一問題不同角度觀察,校長對於學生、老師的關注點(升學率);家長對學生老師的關注點(成績)
1、抽象元素:接收存取者存取
package com.zixieqing.o2simple.user;
import com.zixieqing.o2simple.visitor.Visitor;
/**
* <p>@description : 該類功能 抽象元素:使用者資訊
* 1、接收存取者存取
* </p>
* <p>@package : com.zixieqing.o2simple</p>
* <p>@author : ZiXieqing</p>
* <p>@version : V1.0.0</p>
*/
public abstract class User {
/**
* 姓名
*/
public String name;
/**
* 身份;重點班、普通班 | 特級教師、普通教師、實習教師
*/
public String identity;
/**
* 班級
*/
public String clazz;
public User(String name, String identity, String clazz) {
this.name = name;
this.identity = identity;
this.clazz = clazz;
}
/**
* 核心存取方法
* @param visitor 存取者
*/
public abstract void accept(Visitor visitor);
}
package com.zixieqing.o2simple.user.impl;
import com.zixieqing.o2simple.user.User;
import com.zixieqing.o2simple.visitor.Visitor;
/**
* <p>@description : 該類功能 具體元素:學生
* </p>
* <p>@package : com.zixieqing.o2simple.user.impl</p>
* <p>@author : ZiXieqing</p>
* <p>@version : V1.0.0</p>
*/
public class Student extends User {
public Student(String name, String identity, String clazz) {
super(name, identity, clazz);
}
public void accept(Visitor visitor) {
visitor.visit(this);
}
/**
* 對這個元素進行的不同且不相關操作之一:排名
* @return int 排名
*/
public int ranking() {
return (int) (Math.random() * 100);
}
}
package com.zixieqing.o2simple.user.impl;
import com.zixieqing.o2simple.user.User;
import com.zixieqing.o2simple.visitor.Visitor;
import java.math.BigDecimal;
/**
* <p>@description : 該類功能 具體元素:老師
* </p>
* <p>@package : com.zixieqing.o2simple.user.impl</p>
* <p>@author : ZiXieqing</p>
* <p>@version : V1.0.0</p>
*/
public class Teacher extends User {
public Teacher(String name, String identity, String clazz) {
super(name, identity, clazz);
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
/**
* 對這個具體元素進行的不同且不相關的操作之一:升本率
* @return double 升學率
*/
public double entranceRatio() {
return BigDecimal.valueOf(Math.random() * 100).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
}
}
2、存取者:存取每個具體元素
package com.zixieqing.o2simple.visitor;
import com.zixieqing.o2simple.user.impl.*;
/**
* <p>@description : 該類功能 存取者:存取每個具體元素
* </p>
* <p>@package : com.zixieqing.o2simple.visitor</p>
* <p>@author : ZiXieqing</p>
* <p>@version : V1.0.0</p>
*/
public interface Visitor {
/**
* 存取學生資訊
* @param student 具體元素:學生
*/
void visit(Student student);
/**
* 存取老師資訊
* @param teacher 具體元素:老師
*/
void visit(Teacher teacher);
}
package com.zixieqing.o2simple.visitor.impl;
import com.zixieqing.o2simple.visitor.Visitor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.zixieqing.o2simple.user.impl.*;
/**
* <p>@description : 該類功能 具體存取者:校長
* </p>
* <p>@package : com.zixieqing.o2simple.visitor.impl</p>
* <p>@author : ZiXieqing</p>
* <p>@version : V1.0.0</p>
*/
public class Principal implements Visitor {
private Logger logger = LoggerFactory.getLogger(Principal.class);
public void visit(Student student) {
logger.info("學生資訊 姓名:{} 班級:{}", student.name, student.clazz);
}
public void visit(Teacher teacher) {
logger.info("學生資訊 姓名:{} 班級:{} 升學率:{}", teacher.name, teacher.clazz, teacher.entranceRatio());
}
}
package com.zixieqing.o2simple.visitor.impl;
import com.zixieqing.o2simple.visitor.Visitor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.zixieqing.o2simple.user.impl.*;
/**
* <p>@description : 該類功能 具體存取者:家長
* </p>
* <p>@package : com.zixieqing.o2simple.visitor.impl</p>
* <p>@author : ZiXieqing</p>
* <p>@version : V1.0.0</p>
*/
public class Parent implements Visitor {
private Logger logger = LoggerFactory.getLogger(Parent.class);
public void visit(Student student) {
logger.info("學生資訊 姓名:{} 班級:{} 排名:{}", student.name, student.clazz, student.ranking());
}
public void visit(Teacher teacher) {
logger.info("老師資訊 姓名:{} 班級:{} 級別:{}", teacher.name, teacher.clazz, teacher.identity);
}
}
3、物件結構/資料看板
package com.zixieqing.o2simple;
import com.zixieqing.o2simple.user.User;
import com.zixieqing.o2simple.user.impl.Student;
import com.zixieqing.o2simple.user.impl.Teacher;
import com.zixieqing.o2simple.visitor.Visitor;
import java.util.ArrayList;
import java.util.List;
/**
* <p>@description : 該類功能 物件結構/資料看板
* 1、新增元素
* 2、存取所有元素
* </p>
* <p>@package : com.zixieqing.o2simple</p>
* <p>@author : ZiXieqing</p>
* <p>@version : V1.0.0</p>
*/
public class DataView {
private List<User> userList = new ArrayList<User>();
public DataView() {
userList.add(new Student("謝飛機", "重點班", "一年一班"));
userList.add(new Student("windy", "重點班", "一年一班"));
userList.add(new Student("大毛", "普通班", "二年三班"));
userList.add(new Student("Shing", "普通班", "三年四班"));
userList.add(new Teacher("BK", "特級教師", "一年一班"));
userList.add(new Teacher("娜娜Goddess", "特級教師", "一年一班"));
userList.add(new Teacher("dangdang", "普通教師", "二年三班"));
userList.add(new Teacher("澤東", "實習教師", "三年四班"));
}
/**
* 獲取所有元素
* @param visitor 存取者
*/
public void show(Visitor visitor) {
for (User user : userList) {
user.accept(visitor);
}
}
}
4、測試
package com.zixieqing;
import com.zixieqing.o2simple.DataView;
import com.zixieqing.o2simple.visitor.impl.Parent;
import com.zixieqing.o2simple.visitor.impl.Principal;
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 ApiTest {
public static void main(String[] args) {
Logger logger = LoggerFactory.getLogger(ApiTest.class);
DataView dataView = new DataView();
logger.info("\r\n家長視角存取:");
// 家長
dataView.show(new Parent());
logger.info("\r\n校長視角存取:");
// 校長
dataView.show(new Principal());
}
}
10:53:55.565 [main] INFO com.zixieqing.ApiTest -
家長視角存取:
10:53:55.568 [main] INFO c.z.o2simple.visitor.impl.Parent - 學生資訊 姓名:謝飛機 班級:一年一班 排名:54
10:53:55.568 [main] INFO c.z.o2simple.visitor.impl.Parent - 學生資訊 姓名:windy 班級:一年一班 排名:83
10:53:55.568 [main] INFO c.z.o2simple.visitor.impl.Parent - 學生資訊 姓名:大毛 班級:二年三班 排名:84
10:53:55.568 [main] INFO c.z.o2simple.visitor.impl.Parent - 學生資訊 姓名:Shing 班級:三年四班 排名:91
10:53:55.568 [main] INFO c.z.o2simple.visitor.impl.Parent - 老師資訊 姓名:BK 班級:一年一班 級別:特級教師
10:53:55.568 [main] INFO c.z.o2simple.visitor.impl.Parent - 老師資訊 姓名:娜娜Goddess 班級:一年一班 級別:特級教師
10:53:55.568 [main] INFO c.z.o2simple.visitor.impl.Parent - 老師資訊 姓名:dangdang 班級:二年三班 級別:普通教師
10:53:55.568 [main] INFO c.z.o2simple.visitor.impl.Parent - 老師資訊 姓名:澤東 班級:三年四班 級別:實習教師
10:53:55.568 [main] INFO com.zixieqing.ApiTest -
校長視角存取:
10:53:55.569 [main] INFO c.z.o2simple.visitor.impl.Principal - 學生資訊 姓名:謝飛機 班級:一年一班
10:53:55.569 [main] INFO c.z.o2simple.visitor.impl.Principal - 學生資訊 姓名:windy 班級:一年一班
10:53:55.569 [main] INFO c.z.o2simple.visitor.impl.Principal - 學生資訊 姓名:大毛 班級:二年三班
10:53:55.569 [main] INFO c.z.o2simple.visitor.impl.Principal - 學生資訊 姓名:Shing 班級:三年四班
10:53:55.571 [main] INFO c.z.o2simple.visitor.impl.Principal - 學生資訊 姓名:BK 班級:一年一班 升學率:44.54
10:53:55.571 [main] INFO c.z.o2simple.visitor.impl.Principal - 學生資訊 姓名:娜娜Goddess 班級:一年一班 升學率:45.49
10:53:55.571 [main] INFO c.z.o2simple.visitor.impl.Principal - 學生資訊 姓名:dangdang 班級:二年三班 升學率:70.92
10:53:55.571 [main] INFO c.z.o2simple.visitor.impl.Principal - 學生資訊 姓名:澤東 班級:三年四班 升學率:78.14
存取者模式的優點:
存取者模式的缺點:
存取者模式的重點:元素+存取者