委派模式
概述
委派模式(Delegate Pattern)的基本目的就是負責任務的調用和分配,和代理模式很像,可以看成是一個特殊的靜態代理的全權代理,但是代理模式注重過程,委派模式注重結果。委派模式不是GOF23種設計模式。現實生活中也時常有委派的場景發生,比如老板(Boss)下達命令,部門經理(Manager)把具體任務分配給員工(Employee),待員工完成工作再由經理匯報進度和情況給老板。我們用代碼模擬一下這個場景
員工接口
public interface IEmployee { void doing(String command); }
員工A
public class EmployeeA implements IEmployee { @Override public void doing(String command) { System.out.println("我是員工A,我正在做"+ command); } }
員工B
public class EmployeeB implements IEmployee { @Override public void doing(String command) { System.out.println("我是員工B,我正在做"+ command); } }
經理分發任務給員工
public class Manager { private Map<String,IEmployee> map = new HashMap<String, IEmployee>(); public Manager() { map.put("登錄",new EmployeeA()); map.put("加密",new EmployeeB()); } void doing(String command) { IEmployee employee = map.get(command); employee.doing(command); } }
老板下達命令
public class Boss { void command(String command, Manager manager) { manager.doing(command); } }
可以看出經理分配任務生動體現了委派模式
下面我們來看一下在SpringMVC的DispatcherServlet中如何體現委派模式
業務類MemberController
public class MemberController { public void getMemberById(String mid) { } }
OrderController
public class OrderController { public void getOrderById(String oid) { } }
SystemController
public class SystemController { public void login() { } }
DispatcherServlet
public class DispatcherServlet extends HttpServlet {
private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws IOException {
String uri = req.getRequestURI();
String id = req.getParameter("id");
if("getMemberById".equals(uri))
{
new MemberController().getMemberById(id);
}
else if ("getOrderById".equals(uri))
{
new OrderController().getOrderById(id);
}
else if("login".equals(uri))
{
new SystemController().login();
}
else
{
resp.getWriter().write("404 Not Found!!");
}
}
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doDispatch(req,resp);
}
}
web.xml
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd" > <web-app> <display-name>Archetype Created Web Application</display-name> <servlet> <servlet-name>delegateServlet</servlet-name> <servlet-class>com.stu.pattern.delegate.mvc.DispatcherServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>delegateServlet</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping> </web-app>
一個完整的委派模式就實現出來了
策略模式
策略模式(Strategy Pattern)定義了一系列算法,並且把每一個算法封裝起來,使得每一個算法可以相互替代,把算法策略與客戶端分離開來相互獨立。
舉個例子:去商場買東西碰到促銷活動,那么活動有很多種比如優惠券抵扣活動、返現活動、拼團活動,下面是不使用策略模式的代碼實現
促銷管理類
public class PromotionManager { public void doPromotion(String sType) { if("coupon".equals(sType)) { System.out.println("領取優惠券直接抵扣"); } else if ("cashBack".equals(sType)) { System.out.println("返現促銷,返現金額到支付寶賬號"); } else if ("groupBuy".equals(sType)) { System.out.println("團購促銷,滿20人參加團購活動"); } } }
客戶端調用
public class Test { public static void main(String[] args) { PromotionManager promotionManager = new PromotionManager(); promotionManager.doPromotion("groupBuy"); } }
測試發現上面的代碼可以實現這樣一個場景功能,可是代碼是有問題的,我們發現促銷管理類太過龐大和臃腫,當我們如果有一個別的促銷增加時要修改促銷管理類違背開閉原則
使用策略模式實現上面的促銷
促銷策略接口
public interface IPromotionStrategy { void doPromotion(); }
優惠券促銷策略
public class CouponStrategy implements IPromotionStrategy { @Override public void doPromotion() { System.out.println("領取優惠券直接抵扣"); } }
返現促銷策略
public class CashBackStrategy implements IPromotionStrategy { @Override public void doPromotion() { System.out.println("返現促銷,返現金額到支付寶賬號"); } }
團購優惠策略
public class GroupBuyStrategy implements IPromotionStrategy { @Override public void doPromotion() { System.out.println("團購促銷,滿20人參加團購活動"); } }
促銷活動方案接收具體策略
public class PromotionActivity { private IPromotionStrategy promotionStrategy; public PromotionActivity(IPromotionStrategy promotionStrategy) { this.promotionStrategy = promotionStrategy; } public void doPromotion() { this.promotionStrategy.doPromotion(); } }
測試
public class PromotionStrategyTest { public static void main(String[] args) { PromotionActivity promotionActivity = new PromotionActivity(new CashBackStrategy()); promotionActivity.doPromotion(); } }
可以發現我們客戶端需要確定使用哪一個算法即可調用促銷活動具體的策略,當我們策略新增時我們只需要增加策略類即可比如我們現在要有一個打折策略
public class DiscountStrategy implements IPromotionStrategy { @Override public void doPromotion() { System.out.println("打折活動,折扣8折"); } }
我們無需修改已經寫好的策略,只需要新增類就好
策略模式理解
策略模式的作用:把具體的算法實現從業務邏輯中剝離出來,成為一系列獨立算法類,使得它們可以相互替換。
策略模式的着重點:不是如何來實現算法,而是如何組織和調用這些算法,從而讓我們的程序結構更加的靈活、可擴展。
可以發現我們不用策略模式會有很多的if和else if,這些邏輯其實是對立的是平等的每次只會走一個邏輯,這與策略模式不謀而合
策略模式的應用場景
- 系統中有很多類,他們的區別僅僅是行為不同
- 一個系統需要動態的在幾種算法中選擇一個
為了加深對策略模式的印象,我們在舉個例子,我們都用過支付寶、微信支付、銀聯支付和京東支付。一個常見的應用場景就是大家在下單支付時會選擇支付方式,如果用戶未選擇則會有一個默認的支付方式
抽象類支付策略
public abstract class Payment { //支付類型 public abstract String getName(); //查詢余額 public abstract double queryBalance(String uid); //支付扣款 public MsgResult pay(String uid,double amount) { if(queryBalance(uid) < amount) { return new MsgResult(500,"支付失敗","余額不足"); } return new MsgResult(200,"支付成功","支付金額:"+ amount); } }
返回結果類
public class MsgResult { private int code; private Object data; private String msg; public MsgResult(int code, Object data, String msg) { this.code = code; this.data = data; this.msg = msg; } @Override public String toString() { return "支付狀態:["+ code +"]," + msg + ",交易詳情:"+data; } }
策略子類-Alipay
public class AliPay extends Payment { @Override public String getName() { return "支付寶"; } @Override public double queryBalance(String uid) { return 500; } }
策略子類-JDPay
public class JDPay extends Payment { @Override public String getName() { return "京東白條"; } @Override public double queryBalance(String uid) { return 500; } }
策略子類-UnionPay
public class UnionPay extends Payment { @Override public String getName() { return "銀聯支付"; } @Override public double queryBalance(String uid) { return 2000; } }
策略子類-Alipay
public class WeChatPay extends Payment { @Override public String getName() { return "微信支付"; } @Override public double queryBalance(String uid) { return 1000; } }
管理策略的類
public class PayStrategyManager { public static final String ALI_PAY = "Alipay"; public static final String JD_PAY = "JDPay"; public static final String WECHAT_PAY = "WeChatPay"; public static final String UNION_PAY = "UnionPay"; public static final String DEFAULT_PAY = ALI_PAY; private static Map<String,Payment> payStrategies = new HashMap<>(); static { payStrategies.put(ALI_PAY,new AliPay()); payStrategies.put(JD_PAY,new JDPay()); payStrategies.put(WECHAT_PAY,new WeChatPay()); payStrategies.put(UNION_PAY,new UnionPay()); } public static Payment get(String key) { if (!payStrategies.containsKey(key)) return payStrategies.get(DEFAULT_PAY); return payStrategies.get(key); } }
訂單類
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; } public MsgResult pay(String key) { Payment payment= PayStrategyManager.get(key); System.out.println("歡迎使用" + payment.getName()); System.out.println("本次交易金額是:" + amount + "開始扣款"); return payment.pay(uid,amount); } public MsgResult pay() { return pay(PayStrategyManager.DEFAULT_PAY); } }
客戶端測試
public class PayTest { public static void main(String[] args) { Order order = new Order("1","201801020558",500); System.out.println(order.pay(PayStrategyManager.ALI_PAY)); } }
測試結果

看下類圖
通過不同的支付策略實現不同方式的支付
策略在JDK中的使用
說一個比較常用的比較器接口Comparator,我們常用的compare()方法
public interface Comparator<T> { int compare(T o1, T o2); ... }
Comparator 抽象下面有非常多的實現類,我們經常會把 Comparator 作為參數傳入作為排序策略,例如 Arrays 類的 parallelSort 方法等:
public static <T> void parallelSort(T[] a, int fromIndex, int toIndex, Comparator<? super T> cmp) { .... }
傳入不同的排序策略,得到不同的結果
策略模式的優缺點
優點
- 符合開閉原則
- 避免使用多重if、else語句和switch
- 提高算法的保密性和安全性
缺點
- 客戶端必須知道所有的策略,自己決定使用哪一種策略
- 代碼中會出現很多的策略類,增加系統維護難度
