大型Java進階專題(七) 設計模式之委派模式與策略模式
前言
今天開始我們專題的第七課了。本章節將介紹:你寫的程式碼中是否覺得很臃腫,程式中有大量的if…else,想優化程式碼,精簡程式邏輯,提升程式碼的可讀性,這章節將介紹如何通過委派模式、策略模式讓你程式碼更優雅,消除程式大量冗餘的程式碼。本章節參考資料書籍《Spring 5核心原理》中的第一篇 Spring 內功心法(Spring中常用的設計模式)(沒有電子檔,都是我取其精華並結合自己的理解,一個字一個字手敲出來的)。
委派模式
委派模式的定義及應用場景
委派模式不屬於GOF23種設計模式中。委派模式(Delegate Pattern)的基本作用就是負責任務的調用和分配任務,跟代理模式很像,可以看做是一種特殊情況下的靜態代理的全權代理,但是代理模式注重過程,而委派模式注重結果。委派模式在Spring中應用非常多,大家常用的DispatcherServlet其實就是用到了委派模式。現實生活中也常有委派的場景發生,例如:老闆(Boss)給項目經理(Leader)下達任務,項目經理會根據實際情況給每個員工派發工作任務,待員工把工作任務完成之後,再由項目經理彙報工作進度和結果給老闆。我們用程式碼來模擬下這個業務場景,先來看一下類圖:
創建員工介面
package com.study.demo2;
/**
* @ClassName IEmployee
* @Deacription 員工介面
* @Author wang.zhong.yuan
* @Date 2020/7/9 16:36
* @Version 1.0
**/
public interface IEmployee {
/**
* 需要做的工作
* @param command
*/
void doingSomeThing(String command);
}
員工A實現類
package com.study.demo2;
/**
* @ClassName EmployeeA
* @Deacription 員工A
* @Author wang.zhong.yuan
* @Date 2020/7/9 16:38
* @Version 1.0
**/
public class EmployeeA implements IEmployee {
public void doingSomeThing(String command) {
System.out.println("我是員工A,需要做:"+command);
}
}
員工B實現類
package com.study.demo2;
/**
* @ClassName EmployeeB
* @Deacription 員工B
* @Author wang.zhong.yuan
* @Date 2020/7/9 16:48
* @Version 1.0
**/
public class EmployeeB implements IEmployee {
public void doingSomeThing(String command) {
System.out.println("我是員工B,我要做:"+command);
}
}
領導類
package com.study.demo2;
import java.util.HashMap;
import java.util.Map;
/**
* @ClassName Leader
* @Deacription 領導類
* @Author wang.zhong.yuan
* @Date 2020/7/9 16:51
* @Version 1.0
**/
public class Leader implements IEmployee {
private Map<String,IEmployee> underling = new HashMap<String, IEmployee>();
public Leader() {
underling.put("註冊功能",new EmployeeA());
underling.put("登陸功能",new EmployeeB());
}
/**
* 自己不做事,委派給對應的員工去做
* @param command
*/
public void doingSomeThing(String command) {
underling.get(command).doingSomeThing(command);
}
}
創建BOSS類,安排工作給Leader
package com.study.demo2;
/**
* @ClassName Boss
* @Deacription TODO
* @Author 19054253
* @Date 2020/7/9 17:03
* @Version 1.0
**/
public class Boss {
public void command(String command, Leader leader){
leader.doingSomeThing(command);
}
//測試功能
public static void main(String[] args) {
//客戶請求(Boss)、委派者(Leader)、被被委派者(Target)
//委派者要持有被委派者的引用
//代理模式注重的是過程, 委派模式注重的是結果
//策略模式注重是可擴展(外部擴展),委派模式注重內部的靈活和復用
//委派的核心:就是分發、調度、派遣
//委派模式:就是靜態代理和策略模式一種特殊的組合
Boss boss = new Boss();
Leader leader = new Leader();
boss.command("登陸功能",leader);
}
}
輸出結果
通過上面的程式碼,生動地還原了項目經理分配工作的業務場景,也是委派模式的生動體現。
策略模式
策略模式的定義與應用
策略模式(Strategy Pattern)是指定義了演算法家族、分別封裝起來,讓它們之間可以互相替換,此模式讓演算法的變化不會影響到使用演算法的用戶。假如系統中有很多類,而他們的區別僅僅在於他們的行為不同。一個系統需要動態地在幾種演算法中選擇一種,都可以用策略模式實現。
程式碼實現
一個常見的應用場景就是大家在下單支付時會提示選擇支付方式,如果用戶未選,系統也會默認好推薦的支付方式進行結算。來看一下類圖,下面我們用策略模式來模擬此業務場景:
創建支付狀態類:
package com.study.demo3;
/**
* @ClassName PayState
* @Deacription 支付狀態
* @Author wang
* @Date 2020/7/9 17:48
* @Version 1.0
**/
public class PayState {
private int code;
private Object data;
private String message;
public PayState(int code, Object data, String message) {
this.code = code;
this.data = data;
this.message = message;
}
@Override
public String toString() {
return "PayState{" +
"code=" + code +
", data=" + data +
", message='" + message + '\'' +
'}';
}
}
創建支付介面
package com.study.demo3;
/**
* @ClassName Payment
* @Deacription 支付渠道
* @Author wang
* @Date 2020/7/9 19:12
* @Version 1.0
**/
public interface IPayment {
/**
* 獲取支付類型
* @return
*/
String getName();
/**
* 查詢餘額
* @param uid
* @return
*/
double queryBalance(String uid);
/**
* 支付
* @param uid
* @param amount
* @return
*/
PayState pay(String uid,double amount);
}
創建支付抽象類,完成一些通用的程式碼:
package com.study.demo3;
/**
* @ClassName AbstractPayment
* @Deacription 支付抽象類
* @Author wang
* @Date 2020/7/9 19:16
* @Version 1.0
**/
public abstract class AbstractPayment implements IPayment{
public PayState pay(String uid, double amount) {
if (queryBalance(uid) < amount){
return new PayState(-1,"支付失敗","餘額不足");
}
return new PayState(200,"支付成功","共計支付:"+amount);
}
}
創建阿里支付渠道
package com.study.demo3;
/**
* @ClassName AliPay
* @Deacription 支付寶支付
* @Author wang
* @Date 2020/7/9 19:20
* @Version 1.0
**/
public class AliPay extends AbstractPayment {
public String getName() {
return "支付寶支付";
}
public double queryBalance(String uid) {
return 500;
}
}
創建京東支付渠道
package com.study.demo3;
/**
* @ClassName JDPay
* @Deacription 京東支付
* @Author wang
* @Date 2020/7/9 19:22
* @Version 1.0
**/
public class JDPay extends AbstractPayment {
public String getName() {
return "京東支付";
}
public double queryBalance(String uid) {
return 900;
}
}
創建微信支付渠道
package com.study.demo3;
/**
* @ClassName WXPay
* @Deacription 微信支付
* @Author wang
* @Date 2020/7/9 19:23
* @Version 1.0
**/
public class WXPay extends AbstractPayment {
public String getName() {
return "微信支付";
}
public double queryBalance(String uid) {
return 256;
}
}
package com.study.demo3;
/**
* @ClassName Order
* @Deacription 訂單類
* @Author wang
* @Date 2020/7/9 19:35
* @Version 1.0
**/
public class Order {
private String uid;
private String orderId;
private double amount;
public Order(String uid,String orderId,double amount){
this.uid = uid;
this.orderId = orderId;
this.amount = amount;
}
//完美地解決了 switch 的過程,不需要在程式碼邏輯中寫 switch 了
//更不需要寫 if else if
public PayState pay(){
return pay(PayStrategy.DEFAULT_PAY);
}
public PayState pay(String payKey){
IPayment payment = PayStrategy.get(payKey);
System.out.println("歡迎使用" + payment.getName());
System.out.println("本次交易金額為:" + amount + ",開始扣款...");
return payment.pay(uid,amount);
}
//測試程式碼
public static void main(String[] args) {
Order order = new Order("123", "AB123", 400);
//這個值是在支付的時候才決定用哪個值 用戶自己決定
order.pay("AliPay");
}
}
輸出結果:
希望通過大家耳熟能詳的業務場景來舉例,讓小夥伴們更深刻地理解策略模式。
小結
策略模式的優缺點
優點:
1、策略模式符合開閉原則。
2、避免使用多重條件轉移語句,如if…else…語句、switch語句
3、使用策略模式可以提高演算法的保密性和安全性。
缺點:
1、客戶端必須知道所有的策略,並且自行決定使用哪一個策略類。
2、程式碼中會產生非常多策略類,增加維護難度。