模板方法模式
定義了一個操作中的算法骨架,將算法的一些步驟推遲到子類,使得子類可以不改變該算法結構的情況下重定義該算法的某些步驟
【主要角色
】:
- 抽象類:給出一個算法的輪廓和骨架(包括一個模板方法 和 若干基本方法)
- 模板方法:定義了算法的骨架(執行順序),按某種順序調用其包含的基本方法
- 基本方法:實現算法各個步驟的方法
- 抽象方法:由抽象類聲明,具體子類實現
- 具體方法:由抽象類或具體類聲明或實現,子類可以覆蓋也可以繼承
- 鉤子方法:在抽象類中已經實現,包含用于判斷的邏輯方法和需要子類重寫的空方法(一般是用于判斷的邏輯方法,方法名為isXxx,返回值為boolean類型)
- 具體子類:實現抽象類中所定義的抽象方法和鉤子方法
案例:炒菜
【需求
】:炒菜的步驟是固定的,分為倒油、熱油、倒蔬菜、倒調料品、翻炒等步驟。
抽象類(定義模板方法和基本方法):
public abstract class AbstractClass {// 模板方法定義(子類不能修改模板方法)public final void cookProcess() {pourOil();heatOil();pourVegetable();pourSauce();fry();}public void pourOil() {System.out.println("倒油");}public void heatOil() {System.out.println("熱油");}// 倒蔬菜(一個是包菜、一個是菜心)public abstract void pourVegetable();// 倒調味品public abstract void pourSauce();public void fry() {System.out.println("翻炒");}
}
炒包菜類、炒菜心類(具體子類):
public class ConcreteClassBaoCai extends AbstractClass {@Overridepublic void pourVegetable() {System.out.println("下鍋的蔬菜是包菜");}@Overridepublic void pourSauce() {System.out.println("下鍋的醬料是辣椒");}
}public class ConcreteClassCaiXin extends AbstractClass {@Overridepublic void pourVegetable() {System.out.println("下鍋的蔬菜是菜心");}@Overridepublic void pourSauce() {System.out.println("下鍋的醬料是蒜蓉");}
}
測試類:
public class Client {public static void main(String[] args) {// 創建對象AbstractClass baoCai = new ConcreteClassBaoCai();// 調用炒菜功能baoCai.cookProcess();}
}
將相同的代碼放在抽象的父類中,把不同的代碼放入不同的子類中。
適用場景
- 算法的整體部分比較固定,個別部分容易改變,就可以使用模板方法,將容易變化的部分抽象出來,讓子類去實現這些方法。
- 通過子類來決定父類算法中某個步驟是否執行,實現子類對父類的反向控制。
策略模式
該模式定義了一系列算法, 使他們可以互相替換,算法的變化不會影響使用算法的客戶。
【主要角色
】:
- 抽象策略類:由一個接口或抽象類實現,給出所有具體策略所需的接口
- 具體策略類:實現了抽象策略類定義的接口,提供具體的算法實現或行為
- 環境類:持有一個策略類的引用,最終給客戶端調用
案例:促銷活動
【需求
】:超市爭對不同的節日(春節、中秋節、圣誕節)推出不同的促銷活動,由促銷員(環境類)將促銷活動展示給客戶。
抽象策略類:
public interface Strategy {void show();
}
策略A、B、C類(具體策略類):
public class StrategyA implements Strategy {@Overridepublic void show() {System.out.println("買一送一");}
}public class StrategyB implements Strategy {@Overridepublic void show() {System.out.println("滿200減50");}
}public class StrategyC implements Strategy {@Overridepublic void show() {System.out.println("打8折");}
}
銷售員類(環境類):
public class SalesMan {private Strategy strategy;public SalesMan(Strategy strategy) {this.strategy = strategy;}// 由促銷員展示促銷活動給普通用戶public void salesManShow() {strategy.show();}
}
測試類:
public class Client {public static void main(String[] args) {// 切換策略ASalesMan salesMan = new SalesMan(new StrategyA());salesMan.salesManShow();// 切換策略BsalesMan = new SalesMan(new StrategyB());salesMan.salesManShow();// 切換策略CsalesMan = new SalesMan(new StrategyC());salesMan.salesManShow();}
}
策略類之間可以自由切換;
增加一個新的策略只需要添加一個具體的策略類
策略模式可以避免使用多重的if else 和 switch case
適用場景
- 需要動態在幾種算法中選擇一種,可以把每個算法封裝到策略類中
- 大量的if else和switch cash完全可以用策略模式代替,讓代碼更加美觀優雅
命令模式
將請求封裝成一個對象,使發出請求的責任和執行請求的責任分隔開,這樣兩者之間通過命令對象進行溝通,方便將命令對象進行存儲、傳遞、調用、增加、管理。
【主要角色
】
- 抽象命令角色:定義命令的接口,聲明執行方法
- 具體命令角色:實現命令接口,通常會持有接收者,并調用接收者的功能來完成命令要執行的操作
- 實現者 / 接收者角色:真正執行命令的對象
- 調用者 / 請求者角色:要求命令對象執行請求,通常會持有命令對象
案例:點餐
- 服務員:調用者角色,由服務員發起命令
- 廚師:接收者命令,真正命令執行的對象
- 訂單:命令中包含訂單
訂單類:
@Data
public class Order {// 餐桌號碼private int diningTable;// 所下餐品和份數private Map<String, Integer> foodDir = new HashMap<>();// 添加食物public void addFood(String name, int num) {foodDir.put(name, num);}
}
廚師類(接收者):
public class Chef {// 制作食物public void makeFood(String name, int num) {System.out.println(num + "份" + name);}
}
抽象命令類:
public interface Command {void execute();
}
具體命令類:
public class OrderCommand implements Command {// 持有接收者對象private Chef receiver;private Order order;public OrderCommand(Chef receiver, Order order) {this.receiver = receiver;this.order = order;}@Overridepublic void execute() {System.out.println(order.getDiningTable() + "桌的訂單:");Map<String, Integer> foodDir = order.getFoodDir();foodDir.forEach((foodName, num)->{receiver.makeFood(foodName, num); // 讓廚師去完成訂單里的菜品});System.out.println(order.getDiningTable() + "桌的飯準備完畢");}
}
服務員類(調用者角色):
public class Waitor {// 持有多個命令對象private List<Command> commands = new ArrayList<>();public void addCommand(Command command) {// 將command存儲到List集合中commands.add(command);}// 發起命令public void orderUp() {System.out.println("服務員:廚師,訂單來啦");commands.forEach(command -> {if(command != null) command.execute();});}
}
測試類:
public class Client {public static void main(String[] args) {// 創建第一個訂單對象Order order1 = new Order();order1.setDiningTable(1);order1.addFood("西紅柿雞蛋面", 1);order1.addFood("小杯可樂", 2);// 創建第二個訂單對象Order order2 = new Order();order2.setDiningTable(2);order2.addFood("油悶大蝦", 1);order2.addFood("小杯雪碧", 1);// 創建廚師對象Chef receiver = new Chef();// 創建命令對象OrderCommand cmd1 = new OrderCommand(receiver, order1);OrderCommand cmd2 = new OrderCommand(receiver, order2);// 創建調用者Waitor invoke = new Waitor();invoke.addCommand(cmd1);invoke.addCommand(cmd2);// 讓服務員發起命令invoke.orderUp();}
}
適用場景
- 系統需要將請求調用這和請求接收者解耦
- 系統需要在不同時間指定請求、將請求排隊和執行請求
- 系統需要支持命令的撤銷(undo)和恢復(redo)操作
JDK源碼解析:Runnable類
Runnable類就是一個命令模式,Thread是調用者,start()方法就是執行方法
責任鏈模式
避免發送者和多個請求處理者耦合在一起,將所有請求的處理者通過錢一個對象記住其下一個對象的引用而連成一條鏈;當請求發生時,可以將請求沿著這條鏈傳遞,直到有對象處理它為止
【
問題
】公司員工請假,可以批假的領導有:部門負責人、副總經理、總經理,但是每個領導可以批準的天數不同。員工需要根據自己要請假的天數去找不同的領導簽字。
【解決
】:比如有個員工要請假,他只需要去找部門負責人,如果部門負責人發現他請假的時間自己處理不了,就會把這個請假單交給副總經理,如果副總經理處理不了,就會交給總經理處理。
【主要角色
】:
- 抽象處理者角色:定義一個處理請求的接口,包含抽象處理方法和后繼處理方法
- 具體處理者角色:實現抽象處理者的處理方法,判斷能否處理本次請求,如果可以處理就處理;如果不能處理就把它轉發給后繼的處理者
- 客戶類角色:創建處理鏈,并向鏈頭的具體處理者對象提交請求,它不關心處理細節和請求的傳遞過程
案例:請假流程
【需求
】:請假一天以下的只需要小組長同意;請假1-3天需要部門經理同意;請假3-7天需要總經理同意。
請假條類:
@AllArgsConstructor
@Data
public class LeaveRequest {// 姓名private String name;// 請假天數private int num;// 請假內容private String content;
}
抽象處理者類:
@Data
public abstract class Handler {protected final static int NUM_ONE = 1;protected final static int NUM_THREE = 3;protected final static int NUM_SEVEN = 7;// 領導了可以處理的天數區間private int numStart;private int numEnd;public Handler(int numStart, int numEnd) {this.numStart = numStart;this.numEnd = numEnd;}// 聲明后繼者(上級領導)private Handler nextHandler;// 各級領導處理請假條的方法protected abstract void handleLeave(LeaveRequest leave);// 提交請假條(不能被繼承)public final void submit(LeaveRequest leave) {// 該領導審批handleLeave(leave);if(nextHandler != null && leave.getNum() > numEnd) {// 交給上級領導審批nextHandler.handleLeave(leave);} else {System.out.println("流程結束");}}
}
小組長類、部門經理類、總經理類(具體的處理者)
public class GroupLeader extends Handler {public GroupLeader() {super(0, Handler.NUM_ONE);}@Overrideprotected void handleLeave(LeaveRequest leave) {System.out.println(leave.getName() + "請假" + leave.getNum() + "天,原因:" + leave.getContent());System.out.println("小組長審批:同意");}
}public class Manager extends Handler {public Manager() {super(Handler.NUM_ONE, Handler.NUM_THREE);}@Overrideprotected void handleLeave(LeaveRequest leave) {System.out.println(leave.getName() + "請假" + leave.getNum() + "天,原因:" + leave.getContent());System.out.println("部門經理審批:同意");}
}public class GeneralManager extends Handler {public GeneralManager() {super(Handler.NUM_THREE, Handler.NUM_SEVEN);}@Overrideprotected void handleLeave(LeaveRequest leave) {System.out.println(leave.getName() + "請假" + leave.getNum() + "天,原因:" + leave.getContent());System.out.println("總經理類審批:同意");}
}
測試類:
public class Client {public static void main(String[] args) {// 1. 創建請假條對象LeaveRequest leave = new LeaveRequest("小明", 1, "身體不適");// 2. 創建各級領導對象GroupLeader groupLeader = new GroupLeader();Manager manager = new Manager();GeneralManager generalManager = new GeneralManager();// 3. 設置處理者鏈groupLeader.setNextHandler(manager);manager.setNextHandler(generalManager);// 4. 小明提交請假groupLeader.submit(leave); // 調用小組長里的submit()方法}
}
降低了請求發送者 和 請求接收者的耦合度
每個類只需要處理自己該處理的工作,不能處理的傳遞給下一個對象完成
狀態模式
案例引入:電梯
電梯接口:
public interface ILift {// 電梯狀態常量int OPENING_STATE = 1;int CLOSING_STATE = 2;int RUNNING_STATE = 3;int STOPPING_STATE = 4;// 設置電梯狀態void settState(int state);// 電梯操作功能void open(); // 開門void close(); // 關門void run(); // 運行void stop(); // 停止
}
電梯類(ILift的子實現類):
public class Lift implements ILift {// 記錄當前電梯的狀態private int state;@Overridepublic void settState(int state) {this.state = state;}@Overridepublic void open() {if(state == STOPPING_STATE || state == CLOSING_STATE) {System.out.println("電梯打開");settState(OPENING_STATE);}}@Overridepublic void close() {if(state == OPENING_STATE) {System.out.println("電梯關閉");settState(CLOSING_STATE);}}@Overridepublic void run() {if(state == CLOSING_STATE || state == STOPPING_STATE) {System.out.println("電梯運行");settState(RUNNING_STATE);}}@Overridepublic void stop() {if(state == CLOSING_STATE || state == RUNNING_STATE) {System.out.println("電梯停止");settState(STOPPING_STATE);}}
}
測試類:
public class Client {public static void main(String[] args) {// 1. 創建電梯對象Lift lift = new Lift();// 2. 設置電梯狀態lift.settState(ILift.OPENING_STATE);// 3. 操作電梯lift.open();lift.close();lift.run();lift.stop();}
}
【
存在問題
】使用了大量的if else,使得閱讀性變差。而且如果需要新增一個斷電的狀態,也需要修改上邊的代碼邏輯
狀態模式:對于有狀態的對象,把復雜的判斷邏輯提取到不同的狀態對象中,允許狀態對象在內部狀態發生改變時改變其行為。
【主要角色
】
- 環境角色:定義了客戶程序需要的接口,維護一個當前的狀態,并將與狀態相關的操作委托給當前狀態對象來處理
- 義一個接口,用來封裝環境對象中特定狀態所對應的行為
- 具體狀態角色:實現抽象狀態所對應的行為
案例:電梯
抽象狀態類:
@Data
public abstract class LiftState {// 環境角色變量protected Context context;// 電梯開啟public abstract void open();// 電梯關閉public abstract void close();// 電梯運行public abstract void run();// 電梯停止public abstract void stop();
}
環境角色類:
public class Context {// 定義對應狀態的常量public final static OpeningState OPENING_STATE = new OpeningState();public final static ClosingState CLOSING_STATE = new ClosingState();public final static RunningState RUNNING_STATE = new RunningState();public final static StoppingState STOPPING_STATE = new StoppingState();// 定義一個當前狀態變量private LiftState liftState;public LiftState getLiftState() {return liftState;}public void setLiftState(LiftState liftState) {this.liftState = liftState;// 設置當前對象的Context對象this.liftState.setContext(this);}public void open() {this.liftState.open();}public void close() {this.liftState.close();}public void run() {this.liftState.run();}public void stop() {this.liftState.stop();}
}
電梯關閉、開啟、運行、停止狀態類(具體狀態類):
public class ClosingState extends LiftState {@Overridepublic void open() {super.context.setLiftState(Context.OPENING_STATE);super.context.getLiftState().open();}@Overridepublic void close() {System.out.println("電梯關閉");}@Overridepublic void run() {super.context.setLiftState(Context.RUNNING_STATE);super.context.getLiftState().run();}@Overridepublic void stop() {super.context.setLiftState(Context.STOPPING_STATE);super.context.getLiftState().stop();}
}public class OpeningState extends LiftState {// 當前狀態要執行的方法@Overridepublic void open() {System.out.println("電梯 開啟");}@Overridepublic void close() {// 修改狀態super.context.setLiftState(Context.CLOSING_STATE);// 調用當前狀態中的close()方法super.context.close();}@Overridepublic void run() {// 什么都不做}@Overridepublic void stop() {// 什么都不做}
}public class RunningState extends LiftState {@Overridepublic void open() {// 什么都不做}@Overridepublic void close() {// 什么都不做}@Overridepublic void run() {System.out.println("電梯運行");}@Overridepublic void stop() {super.context.setLiftState(Context.STOPPING_STATE);super.context.stop();}
}public class StoppingState extends LiftState {@Overridepublic void open() {super.context.setLiftState(Context.OPENING_STATE);super.context.getLiftState().open();}@Overridepublic void close() {super.context.setLiftState(Context.CLOSING_STATE);super.context.getLiftState().close();}@Overridepublic void run() {super.context.setLiftState(Context.RUNNING_STATE);super.context.getLiftState().run();}@Overridepublic void stop() {System.out.println("電梯停止");}
}
測試類:
public class Client {public static void main(String[] args) {// 創建環境角色對象Context context = new Context();// 設置當前電梯狀態context.setLiftState(new ClosingState());context.open();context.close();context.run();context.stop();}
}
適用場景
- 當一個對象的行為取決于他的狀態,并且他必須在運行時根據狀態改變他的行為
- 一個操作中含有龐大的分支結構,并且這些分支決定于對象的狀態