要分析經常使用框架spring、mybatis、springboot、springcloud等的源碼,首先要了解各類設計模式,由於框架裏面應用了各類設計模式html
學習設計模式最重要的是掌握設計思想和設計原則,理解了設計思想和設計原則並運用到平時的編碼中是最重要的!!!java
每天加班編程,編程到底都作的是什麼?git
擼代碼,加班擼代碼,寫接口、寫類、寫方法程序員
用設計模式或作設計的做用是什麼?
指導、規定該如何擼代碼,如何來寫接口、寫類、寫方法github
爲何要作設計、用設計模式?web
代碼會變,爲應對變化,爲了之後方便擴展。作到以不變應萬變,作一個會偷懶的程序員!算法
首先是從現實出發理清現實,在寫代碼以前先從實際分析,而後就開始寫代碼,寫代碼時要區分出不變的代碼和會變化的代碼,會變得代碼會怎麼變,使用者如何隔絕這種變化,所謂的隔絕這種變化就是不讓調用者感知到內部的變化,只須要很簡單的方式就能使用沒必要關心內部的邏輯,這樣的話就要用到各類設計模式。不一樣的變化方式對應不一樣的設計模式。spring
設計的最終體現:如何來定義類、接口、方法數據庫
1)類是作什麼用的?
模擬現實,封裝數據與代碼編程
2)接口是作什麼用的?
定義相接的口子
定義功能使用者和功能提供者間的接口
3)爲何要有接口?
隔離變化
4)抽象類是作什麼用的?
包容不變與變的
1)多態爲咱們提供了什麼?
一種實現變化的方式
1. 找出變化,分開變化和不變的
隔離,封裝變化的部分,讓其餘部分不受它的影響。
2. 面向接口編程 ——隔離變化的方式
使用者使用接口,提供者實現接口。「接口」能夠是超類!
3. 依賴倒置原則(里氏替換原則)——隔離變化的方式
依賴抽象,不要依賴具體類!
4. 對修改閉合,對擴展開放——隔離變化的方式
能夠繼承一個類或者接口擴展功能,可是不能修改類或者接口的原有功能
5. 多用組合,少用繼承——靈活變化的方式
「有一個」可能比「是一個」更好。
6. 單一職責原則——方法設計的原則
每一個方法只負責一個功能,不要把不少功能寫在一個方法裏面
應用設計模式的目的:
易擴展,易維護
少改代碼,不改代碼
示例:
京東、天貓雙十一促銷,各類商品有多種不一樣的促銷活動:
滿減:滿400減50
每滿減:每滿100減20
數量折扣:買兩件8折、三件7折
數量減:滿三件減最低價的一件
……
顧客下單時可選擇多種促銷活動的其中一種來下單
後端代碼中如何來靈活應對訂單金額的計算?之後還會有不少的促銷活動出現!
控制器OrderController.java
@RestController @RequestMapping("/order") public class OrderController { @Autowired private OrderService orderService; /** * 計算訂單的促銷金額 */ @RequestMapping("prepare") public Order prepareOrder(Order order, String promotion) { …….. return this.orderService.prepareOrder(order, promotion); } }
OrderService.java改怎麼來寫呢
@Service public class OrderService { public Order prepareOrder(Order order, String promotion) { // 該如何寫 return order; } }
@Service public class OrderService { public Order prepareOrder(Order order, String promotion) { switch (promotion) { case "promotion-1": // 促銷1的算法 …… break; case "promotion-2": // 促銷2的算法 …… break; case "promotion-3": // 促銷3的算法 …… break; …… } return order; } }
營銷活動有不少,這個switch會變得很龐大,不利於維護,而且很容易引入新的問題
@Service public class OrderService { public Order prepareOrder(Order order, String promotion) { switch (promotion) { case "promotion-1": // 促銷1的算法 return calPromotion1(order); case "promotion-2": // 促銷2的算法 return calPromotion2(order); case "promotion-3": // 促銷3的算法 return calPromotion3(order); …… } return order; } private Order calPromotion1(Order order) { System.out.println("促銷1計算.............................."); return order; } ……. }
把每一個促銷算法單獨抽出一個方法,新加入一個促銷活動只須要新增一個方法和case就能夠了
這裏利用了設計原則的方法設計原則:單一職責原則
可是這樣寫還會存在以下問題:
營銷活動常常變,這個switch就得常常改,還得不斷加促銷的算法方法…….
改代碼是bug的源泉,咱們但願少改動OrderService!!!
分析:這裏變的是什麼?
促銷的金額的算法!同一行爲的不一樣算法!
咱們不但願OrderService被算法代碼爆炸!
同一行爲的不一樣算法實現,咱們能夠用接口來定義行爲,不一樣的算法分別去實現接口。
這裏利用了設計原則:對修改關閉,對擴展開放!
這就是策略模式的應用!
策略模式的的定義:
策略模式定義了一系列的算法,並將每個算法封裝起來,並且使他們能夠相互替換,讓算法獨立於使用它的用戶而獨立變化。
@Service public class OrderService { public Order prepareOrder(Order order, String promotion) { switch (promotion) { case "promotion-1": // 促銷1的算法 return new Promotion1Calculation().calculate(order); case "promotion-2": // 促銷2的算法 return new Promotion2Calculation().calculate(order); case "promotion-3": // 促銷3的算法 return new Promotion3Calculation().calculate(order); ...... } } }
可是switch中的代碼仍是會不斷變!!!switch中須要知道全部的實現!
如何讓OrderService的代碼不要改變?
把變的部分移出去!改怎麼移呢?
@Component public class PromotionCalculationFactory { public PromotionCalculation getPromotionCalculation(String promotion) { switch (promotion) { case "promotion-1": // 促銷1的算法 return new Promotion1Calculation(); case "promotion-2": // 促銷2的算法 return new Promotion2Calculation(); case "promotion-3": // 促銷3的算法 return new Promotion3Calculation(); ...... } } }
這是簡單工廠模式:全部產品由一個工廠建立
@Service public class OrderService { @Autowired private PromotionCalculationFactory promotionCalculationFactory; public Order prepareOrder(Order order, String promotion) { return promotionCalculationFactory.getPromotionCalculation(promotion).calculate(order); } }
想要工廠中的代碼也不要隨促銷的變化而變化,你以爲該怎麼辦?
方式一:promotion = beanName
把各類促銷算法的實現交給spring容器來管理,用戶選擇的促銷活動promotion 做爲bean的名字,在PromotionCalculationFactory 工廠裏面經過getBean("promotion")就能拿到各類促銷算法的實現了
方式一的僞代碼實現:
spring裏面的bean配置:
<bean id="promotion1" calss="Promotion1Calculation"> <bean id="promotion2" calss="Promotion2Calculation"> <bean id="promotion3" calss="Promotion3Calculation">
PromotionCalculationFactory 工廠改寫:
@Component public class PromotionCalculationFactory { public PromotionCalculation getPromotionCalculation(String promotion) { return getBean("promotion1/promotion2/promotion3"); } } }
方式二: 配置promotion與實現類的對應關係
把用戶選擇的促銷活動promotion和對應的促銷算法的實現類放到map裏面,或者存到數據庫裏面,在PromotionCalculationFactory 工廠裏面經過map.get("promotion"),或者從數據庫裏面獲取對應促銷算法的實現類路徑經過Class.forName("促銷算法的實現類路徑")就能拿到各類促銷算法的實現了
方式二的僞代碼實現:
PromotionCalculationFactory 工廠改寫:
package com.study.design.mode.service; import java.util.Map; import org.springframework.stereotype.Component; @Component public class PromotionCalculationFactory { private Map<String, PromotionCalculation> maps; public PromotionCalculation getPromotionCalculation(String promotion) { PromotionCalculation prom = maps.get(promotion); if (prom == null) { // 從配置的地方加載 prom = getFromDb(promotion); if (prom != null) maps.put(promotion, prom); } return prom; } public void init() { // 第一次將全部的促銷策略都加載到Map中 } private PromotionCalculation getFromDb(String promotion) { // 從數據庫中取到對應的類名 //配置的格式: promotion1=com.study.dn.promotion.calculation.Promotion1 String className = 從數據庫(或其餘配置源)中得到; // Class c = Class.forName(className); // 實例化 // 返回 } }
一個工廠負責建立全部實例。好比上面的策略模式中使用的就是簡單工廠模式
根據傳入的工廠類型參數String建立對應的實例(產品)
父類中定義工廠方法,各子類在+factoryMethod():Product方法裏面實現具體的實例建立
使用者持有具體的工廠ChildAClass、ChildBClass、ChildCClass,傳入對應的工廠ChildAClass、ChildBClass、ChildCClass建立對應的工廠實例
定義一個工廠接口,全部具體工廠實現工廠接口
使用者調用FactoryProducer的getFactory(type)方法傳入type,type爲AFactory、BFactory、CFactory對應的類型,就會返回對應的工廠AFactory、BFactory、CFactory,不須要傳入AFactory、BFactory、CFactory,由於type已經跟AFactory、BFactory、CFactory綁定了。
示例:促銷活動可多重疊加,該如何靈活實現訂單金額計算?
OrderController
@RestController @RequestMapping("/order") public class OrderController { @Autowired private OrderService orderService; /** * 計算訂單的促銷金額,促銷按給入的順從疊加 */ @RequestMapping("prepare") public Order prepareOrder(Order order, String... promotion) { return this.orderService.prepareOrder(order, promotion); } }
OrderService
@Service public class OrderService { @Autowired private PromotionCalculationFactory promotionCalculationFactory; public Order prepareOrder(Order order, String... promotion) { for (String p : promotion) { order = promotionCalculationFactory. getPromotionCalculation(p).calculate(order); } return order; } }
裝飾者模式的定義:以裝飾的方式,動態地將責任附加到對象上。
說明:
不改變具體類代碼(被裝飾者ConcreteComponent),動態疊加加強行爲功能。
若要擴展功能,裝飾者提供了比繼承更有彈性的替代方案
相較於前面的for循環,有何區別?
當須要對一個類的多個方法進行加強,使用者會隨意使用被加強方法時,for循環就不夠靈活了。
責任鏈和裝飾者模式完成的是相同的事情。
裝飾者模式-代碼示例:
共同的需裝飾的行爲定義成接口
public interface Component { String methodA(); int methodB(); }
被裝飾者實現接口Component
public class ConcreteComponent implements Component { public String methodA() { return "concrete-object"; } public int methodB() { return 100; } }
裝飾者實現接口Component
public class Decorator implements Component { //裝飾者包含被裝飾者(被裝飾者實現的接口) protected Component component; public Decorator(Component component) { super(); this.component = component; } public String methodA() { return this.component.methodA(); } public int methodB() { return this.component.methodB(); } }
裝飾者派生出的裝飾者
public class DecoratorA extends Decorator { public DecoratorA(Component component) { super(component); } public String methodA() { //在這裏能夠進行前置加強,實現要處理的邏輯 return this.component.methodA() + " + A"; //在這裏能夠進行後置加強,實現要處理的邏輯 } public int methodB() { //在這裏能夠進行前置加強,實現要處理的邏輯 return this.component.methodB() + 10; //在這裏能夠進行後置加強,實現要處理的邏輯 } }
調用示例:
public class DecoratorSample { public static void main(String[] args) { //建立一個被裝飾者 Component cc = new ConcreteComponent(); //建立一個派生的裝飾者,同時把被裝飾者傳入裝飾者裏面,即說的裝飾者包含被裝飾者 cc = new DecoratorA(cc); //方法調用 System.out.println(cc.methodA()); System.out.println(cc.methodB()); } }
輸出結果:
concrete-object + A
110
代理模式的定義:爲其餘對象提供一種代理以控制對這個對象的訪問。
在某些狀況下,一個對象不適合或者不能直接引用另外一個對象,而代理對象能夠在客戶端和目標對象之間起到中介的做用。
做用:不改變原類的代碼,而加強原類對象的功能,可選擇前置、後置、環繞、異常處理加強
代理模式的類圖:
類圖與裝飾者模式同樣,那麼代理模式和裝飾者模式有什麼區別呢?
代理模式意在在代理中控制使用者對目標對象的訪問,以及進行功能加強。裝飾者模式意在對功能的疊加,好比對多種促銷活動的疊加
代理模式有兩種實現方式:
靜態代理:由程序員建立或由特定工具自動生成代理類源代碼,再對其編譯。在程序運行前,代理類的.class文件就已經存在了。
動態代理:代理類在程序運行時,運用反射機制動態建立而成。
靜態代理事先知道要代理的是什麼,而動態代理不知道要代理什麼東西,只有在運行時才知道。
有一個土豪要找蒼老師約會,他不能直接和蒼老師約,須要通過一箇中間代理Tony
須要被代理控制加強的行爲定義成接口或者超類
public interface Girl { boolean dating(float length); }
代理和被代理的目標對象都要實現接口Girl
代理:
package com.study.design.mode.samples.proxy; /** * * @Description: 代理類實現Girl * @author leeSamll * @date 2018年11月24日 * */ public class Tony implements Girl { //代理類持有被代理的目標對象TeacherCang(目標對象實現的超類或者接口) private Girl girl; public Girl getGirl() { return girl; } public void setGirl(Girl girl) { this.girl = girl; } //代理:控制、加強被代理對象的行爲 public boolean dating(float length) { // 前置加強 doSomethingBefore(); boolean res = this.girl.dating(length); // 後置加強 doSomethingAfter(); return res; } private void doSomethingBefore() { System.out.println("老闆,這個我試過了,很不錯,推薦給你!"); } private void doSomethingAfter() { System.out.println("老闆,你以爲怎樣,歡迎下次再約!"); } }
被代理的目標對象
package com.study.design.mode.samples.proxy; /** * * @Description: 被代理的目標對象實現Girl * @author leeSamll * @date 2018年11月24日 * */ public class TeacherCang implements Girl { public boolean dating(float length) { if (length >= 1.7F) { System.out.println("身高能夠,能夠約!"); return true; } System.out.println("身高不能夠,不可約!"); return false; } }
土豪使用者
package com.study.design.mode.samples.proxy; /** * * @Description: 使用者 * @author leeSamll * @date 2018年11月24日 * */ public class TuHao { private float length; public TuHao(float length) { super(); this.length = length; } public float getLength() { return length; } public void setLength(float length) { this.length = length; } //約會 public void dating(Girl g) { g.dating(length); } }
調用示例:
package com.study.design.mode.samples.proxy; /** * * @Description: 調用示例 * @author leeSamll * @date 2018年11月24日 * */ public class PlayGame { public static void main(String[] args) { //建立土豪(使用者)、蒼老師(目標對象)、tony(代理)三個對象 TuHao th = new TuHao(1.7F); Girl tc = new TeacherCang(); Tony tony = new Tony(); //tony對蒼老師進行代理 tony.setGirl(tc); //土豪和tony約 th.dating(tony); }
輸出結果:
老闆,這個我試過了,很不錯,推薦給你!
身高能夠,能夠約!
老闆,你以爲怎樣,歡迎下次再約!
靜態代理缺點:
擴展能力差
橫向擴展:代理更多的類
縱向擴展:加強更多的方法
可維護性差
因爲靜態代理的擴展能力差、可維護性差,這就須要使用動態代理了!!!
在運行時,動態爲不一樣類的對象建立代理,加強功能。靈活擴展,易維護!
動態代理的實現方式:
JDK動態代理:只可對接口建立代理
CGLIB動態代理:可對接口、類建立代理
在運行時,對接口建立代理對象
生成代理類$Proxy0的方法:
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
參數說明:
ClassLoader loader:類加載器
Class<?>[] interfaces:須要被代理的目標對象實現的接口,能夠傳入多個
InvocationHandler h:功能加強的接口
功能加強的接口:
public interface InvocationHandler { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable; }
參數說明:
Object proxy:被代理的目標對象(接口)
Method method:要調用的目標對象的方法
Object[] args:要調用的目標對象的方法的參數
被代理控制加強的行爲生成接口:
Girl
package com.study.design.mode.samples.proxy; /** * * @Description: 被代理控制加強的行爲生成接口Girl * @author leeSamll * @date 2018年11月24日 * */ public interface Girl { boolean dating(float length); }
Boy
package com.study.design.mode.samples.proxy; /** * * @Description: 被代理控制加強的行爲生成接口Boy * @author leeSamll * @date 2018年11月24日 * */ public interface Boy { boolean dating(char cup); void show(); }
被代理的目標對象
TeacherCang
package com.study.design.mode.samples.proxy; /** * * @Description: 被代理的目標對象TeacherCang實現Girl * @author leeSamll * @date 2018年11月24日 * */ public class TeacherCang implements Girl { public boolean dating(float length) { if (length >= 1.7F) { System.out.println("身高能夠,能夠約!"); return true; } System.out.println("身高不能夠,不可約!"); return false; } }
TeacherChen
package com.study.design.mode.samples.proxy; /** * * @Description: 被代理的目標對象TeacherChen實現Boy * @author leeSamll * @date 2018年11月24日 * */ public class TeacherChen implements Boy { public boolean dating(char cup) { if (cup == 'E') { System.out.println("這個女老闆品德正好,能夠約!"); return true; } System.out.println("這個女老闆品德不行,不能夠約!"); return false; } public void show() { System.out.println("開始進入拍攝模式。。。。。。。。"); } }
JDK動態代理
package com.study.design.mode.samples.proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * * @Description: JDK動態代理 * @author leeSamll * @date 2018年11月24日 * */ public class TonyCompany { //動態生成代理對象 傳入target的是被代理的類 public static Object proxy(Object target) { return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new MyInvationHandler(target)); } //特定的功能加強實現 private static class MyInvationHandler implements InvocationHandler { //被被代理的目標對象 private Object target; public MyInvationHandler(Object target) { super(); this.target = target; } public Object getTarget() { return target; } public void setTarget(Object target) { this.target = target; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 前置加強 doSomethingBefore(); // 調用被代理對象的方法 Object res = method.invoke(target, args); // 後置加強 doSomethingAfter(); return res; } private void doSomethingAfter() { System.out.println("老闆,你以爲怎樣,歡迎下次再約!"); } private void doSomethingBefore() { System.out.println("老闆,這個我試過了,很不錯,推薦給你!"); } } }
調用示例:
package com.study.design.mode.samples.proxy; /** * * @Description: 調用示例 * @author leeSamll * @date 2018年11月24日 * */ public class PlayGame { public static void main(String[] args) { System.out.println("----------------1.靜態代理TeacherCang-----------------------"); //建立土豪(使用者)、蒼老師(目標對象)、tony(代理)三個對象 TuHao th = new TuHao(1.7F); Girl tc = new TeacherCang(); Tony tony = new Tony(); //tony對蒼老師進行代理 tony.setGirl(tc); //土豪和tony約 th.dating(tony); System.out.println("----------------2.JDK動態代理TeacherCang-----------------------"); //生成代理類$Proxy0 Girl tony1 = (Girl) TonyCompany.proxy(tc); //土豪直接和代理tony約 th.dating(tony1); System.out.println("----------------3.JDK動態代理TeacherChen,橫向縱向擴展:代理更多的類和方法-----------------------"); //代理另一個目標對象TeacherChen Boy tcc = new TeacherChen(); //生成代理類$Proxy0 Boy tony2 = (Boy) TonyCompany.proxy(tcc); //tony2約TeacherChen 縱向擴展:加強更多的方法 System.out.println("----------------3.1 JDK動態代理TeacherChen,調用TeacherChen的dating方法-----------------------"); tony2.dating('E'); System.out.println("----------------3.2 JDK動態代理TeacherChen,調用TeacherChen的show方法-----------------------"); tony2.show(); } }
輸出結果:
----------------1.靜態代理TeacherCang----------------------- 老闆,這個我試過了,很不錯,推薦給你! 身高能夠,能夠約! 老闆,你以爲怎樣,歡迎下次再約! ----------------2.JDK動態代理TeacherCang----------------------- 老闆,這個我試過了,很不錯,推薦給你! 身高能夠,能夠約! 老闆,你以爲怎樣,歡迎下次再約! ----------------3.JDK動態代理TeacherChen,橫向縱向擴展:代理更多的類和方法----------------------- ----------------3.1 JDK動態代理TeacherChen,調用TeacherChen的dating方法----------------------- 老闆,這個我試過了,很不錯,推薦給你! 這個女老闆品德正好,能夠約! 老闆,你以爲怎樣,歡迎下次再約! ----------------3.2 JDK動態代理TeacherChen,調用TeacherChen的show方法----------------------- 老闆,這個我試過了,很不錯,推薦給你! 開始進入拍攝模式。。。。。。。。 老闆,你以爲怎樣,歡迎下次再約!
cglib是什麼?
cglib( Byte Code Generation Library),一個高層次的java字節碼生成和轉換的api庫.
ASM:一個低層次的字節碼操做庫
它的主要用途
在運行期爲類、接口生成動態代理對象。 以達到不改動原類代碼而實現功能加強的目的
常在哪裏用它?
常在 AOP、test、orm框架中用來生成動態代理對象、攔截屬性訪問
如何使用它?
1)引入它的jar
<!-- https://mvnrepository.com/artifact/cglib/cglib --> <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.2.6</version> </dependency>
2)學習它的API
https://github.com/cglib/cglib/wiki
cglib動態代理-類圖和API
說明:
實現思想和前面的JDK動態代理同樣,只是使用了不一樣的API。
代理類由Enhancer生成,代理類實現被代理的類或者接口,特定的功能加強的實現MyMethodInterceptor實現MethodInterceptor接口,特定的功能加強實現MyMethodInterceptor裏面持有被代理的類或者接口target
eg2:cglib動態代理-代碼示例
被代理對象的接口:
package com.study.design.mode.samples.proxy; /** * * @Description: 被代理控制加強的行爲生成接口Girl * @author leeSamll * @date 2018年11月24日 * */ public interface Girl { boolean dating(float length); }
被代理對象:
package com.study.design.mode.samples.proxy; /** * * @Description: 被代理的目標對象TeacherCang實現Girl * @author leeSamll * @date 2018年11月24日 * */ public class TeacherCang implements Girl { public boolean dating(float length) { if (length >= 1.7F) { System.out.println("身高能夠,能夠約!"); return true; } System.out.println("身高不能夠,不可約!"); return false; } }
cglib動態代理主類:
package com.study.design.mode.samples.proxy; import java.lang.reflect.Method; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; /** * * @Description: cglib動態代理 * @author leeSamll * @date 2018年11月24日 * */ public class CglibDemo { // 特定的功能加強的實現 static class MyMethodInterceptor implements MethodInterceptor { //特定的功能加強實現MyMethodInterceptor裏面持有被代理的類或者接口target private Object target; public MyMethodInterceptor(Object target) { this.target = target; } //在intercept方法進行調用被代理類或者接口的方法以前進行攔截實現前置、後置、環繞、異常處理等功能的加強 public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { System.out.println("**************** " + method.getName()); // 前置加強 doSomethingBefore(); // 返回值 Object res = null; // 這裏能夠調用父類的該方法,當是生成接口的代理時不可調用。 // Object res = methodProxy.invokeSuper(proxy, args); // 經過method來調用被代理對象的方法 if (this.target != null) { res = method.invoke(target, args); } // 後置加強 doSomethingAfter(); return res; } private void doSomethingBefore() { System.out.println("老闆你好,這個我試過了,很不錯,推薦給你!"); } private void doSomethingAfter() { System.out.println("老闆你以爲怎樣? 歡迎下次....."); } }; public static void main(String[] args) { //建立Enhancer對象用來生成代理類 Enhancer e = new Enhancer(); //建立須要被代理的類TeacherCang TeacherCang tc = new TeacherCang(); // 設置加強回調 e.setCallback(new MyMethodInterceptor(tc)); //對接口生成代理對象 System.out.println("--------------------cglib動態代理:對接口Girl進行代理----------------------"); //設置要代理的接口 e.setInterfaces(new Class[] { Girl.class }); //生成代理的接口的動態代理對象 Girl g = (Girl) e.create(); //調用被代理的接口的dating方法 g.dating(1.8f); // 對類生成代理對象 System.out.println("--------------------cglib動態代理:對類TeacherCang進行代理----------------------"); //設置要代理的類 e.setSuperclass(TeacherCang.class); //把前面的設置的接口Girl置爲空 e.setInterfaces(null); //當有多個callback時,須要經過callbackFilter來指定被代理方法使用第幾個callback /* e.setCallbackFilter(new CallbackFilter() { @Override public int accept(Method method) { return 0; } });*/ //生成代理的類TeacherCang的動態代理對象 TeacherCang proxy = (TeacherCang) e.create(); //調用代理的類TeacherCang的dating方法 proxy.dating(1.8f); } }
輸出結果:
--------------------cglib動態代理:對接口Girl進行代理---------------------- **************** dating 老闆你好,這個我試過了,很不錯,推薦給你! 身高能夠,能夠約! 老闆你以爲怎樣? 歡迎下次..... --------------------cglib動態代理:對類TeacherCang進行代理---------------------- **************** dating 老闆你好,這個我試過了,很不錯,推薦給你! 身高能夠,能夠約! 老闆你以爲怎樣? 歡迎下次.....
http web請求處理,請求過來後將通過轉碼、解析、參數封裝、鑑權......一系列的處理(責任),並且要通過多少處理是能夠靈活調整的。
將全部的處理都寫在一個類中能否?
不行
分紅多個類如何靈活組合在一塊兒?
責任鏈:全部的處理者都加入到這個鏈式,一個處理完後,轉給下一個。
責任鏈模式具體實現步驟:
1)抽象出責任接口,具體責任邏輯實現責任接口。
2)根據處理過程須要,將具體責任實現邏輯組合成鏈
3)使用者使用鏈
典型表明:Filter(過濾器)、Intercept(攔截器)
責任鏈模式類圖:
和裝飾者模式的區別在哪裏?
裝飾者模式意在功能的疊加,責任鏈模式意在鏈式的處理
eg:責任鏈模式代碼示例
抽象出責任接口:
/** * * @Description: 責任接口 * @author leeSamll * @date 2018年11月25日 * */ public interface Responsibility { void process(Request request, ResponsibilityChain chain); }
具體的責任邏輯實現責任接口:
ResponsibilityA
package com.study.design.mode.samples.responsibility; /** * * @Description: 具體的責任邏輯實現 * @author leeSamll * @date 2018年11月25日 * */ public class ResponsibilityA implements Responsibility { @Override public void process(Request request, ResponsibilityChain chain) { //前置加強 System.out.println("Before Responsibility-A done something..."); //ResponsibilityA處理完之後調用ResponsibilityChain的process方法交給下一個責任邏輯處理 chain.process(request); //後置加強 } }
ResponsibilityB
package com.study.design.mode.samples.responsibility; /** * * @Description: 具體的責任邏輯實現 * @author leeSamll * @date 2018年11月25日 * */ public class ResponsibilityB implements Responsibility { @Override public void process(Request request, ResponsibilityChain chain) { //前置加強 System.out.println("Before Responsibility-B done something..."); //ResponsibilityB處理完之後調用ResponsibilityChain的process方法交給下一個責任邏輯處理 chain.process(request); //後置加強 } }
責任鏈:
ResponsibilityChain
package com.study.design.mode.samples.responsibility; import java.util.ArrayList; import java.util.List; /** * * @Description: 責任鏈,全部的責任加到責任鏈裏面進行處理 * @author leeSamll * @date 2018年11月25日 * */ public class ResponsibilityChain { //存放具體的責任邏輯 private List<Responsibility> responsibilitys; private int index = 0; public ResponsibilityChain() { this.responsibilitys = new ArrayList<>(); } //順序調用加入的責任邏輯,一個處理完之後交給下一個繼續處理,下一個處理完之後會經過this回調process看是否有下一個繼續處理 public void process(Request request) { if (this.index < this.responsibilitys.size()) { this.responsibilitys.get(index++).process(request, this); } } //加入具體的責任邏輯 public void register(Responsibility res) { this.responsibilitys.add(res); } }
請求接口:
package com.study.design.mode.samples.responsibility; /** * * @Description: 請求接口 * @author leeSamll * @date 2018年11月25日 * */ public interface Request { }
調用者調用示例
package com.study.design.mode.samples.responsibility; /** * * @Description: 調用者調用示例 * @author leeSamll * @date 2018年11月25日 * */ public class PlayGame { public static void main(String[] args) { //建立一個責任鏈 ResponsibilityChain chain = new ResponsibilityChain(); //往責任鏈裏面加入具體的責任邏輯 chain.register(new ResponsibilityA()); chain.register(new ResponsibilityB()); //開始處理 chain.process(new Request() { }); } }
輸出結果:
Before Responsibility-A done something...
Before Responsibility-B done something...
使用者依賴的接口與提供者的接口不匹配時,就加一層適配,而不改兩端的代碼。
適配器模式類圖:
說明:
使用者使用Target接口,可是提供者Provider又沒有實現Target接口,這個時候就須要加一層適配Adaper,Adaper裏面持有Provider,在Adapter的methodA()方法裏面調用Provider的methodB方法
和代理、裝飾的區別在哪裏?
適配器模式不進行功能加強
使用方要完成一個功能,須要調用提供方的多個接口、方法,調用過程複雜時,咱們能夠再提供一個高層接口(新的外觀),將複雜的調用過程向使用方隱藏。
外觀(門面)模式類圖:
這裏使用了設計原則:最少知識原則(迪米特原則)
觀察者模式類圖:
說明:
主題Subject面向觀察者接口Observer編程,主題裏面能夠添加、刪除和通知觀察者Observer;
注意每一個觀察者都有一個回調方法update,若是有變化就會在主題的notifyObservers()方法裏面調用update方法,把最新的變化給到觀察者
變化之處:觀察者會變,觀察者的數量會變。
不變:主題的代碼要不受觀察者變化的影響。
觀察者模式定義:
定義了對象之間一對多的依賴關係,當一端對象改變狀態時,它的全部依賴者都會收到通知並自動更新(被調用更新方法)。也稱爲:監聽模式、發佈訂閱模式。提供一種對象之間鬆耦合的設計方式。
設計原則:爲了交互對象之間的鬆耦合設計而努力!
Java.util. Observable 可被觀察的(主題),具體主題擴展它。
java.util.Observer 觀察者接口,具體觀察者實現該接口。
主題Observable:
觀察者接口Observer
使用代碼示例:
package com.study.design.mode.samples; /** * * @Description: java中提供的觀察者設計模式 * @author leeSamll * @date 2018年11月25日 * */ import java.util.Observable; import java.util.Observer; public class ObserverSample { public static void main(String[] args) { //建立主題 Observable subject1 = new Observable() { //通知觀察者變化的數據data public synchronized void notifyObservers(Object data) { //設置 java.util.Observable.changed = true表示發生了改變 setChanged(); //調用父類的notifyObservers方法通知觀察者發生變化 //調用鏈java.util.Observable.notifyObservers(Object)->java.util.Observer.update(Observable, Object) super.notifyObservers(data); } }; //添加觀察者 subject1.addObserver(new Observer() { //主題回調觀察者的update方法通知改變 @Override public void update(Observable o, Object arg) { System.out.println("觀察者1收到通知被更新了..." + arg); } }); //添加觀察者 subject1.addObserver(new Observer() { //主題回調觀察者的update方法通知改變 @Override public void update(Observable o, Object arg) { System.out.println("觀察者2收到通知被更新了..." + arg); } }); //通知改變 subject1.notifyObservers("change1"); subject1.notifyObservers("change2"); } }
輸出結果:
觀察者2收到通知被更新了...change1
觀察者1收到通知被更新了...change1
觀察者2收到通知被更新了...change2
觀察者1收到通知被更新了...change2
示例:
請爲你的系統設計一個命令行界面,用戶可輸入命令來執行某項功能。
系統的功能會不斷增長,命令也會不斷的增長。
如何將一項一項的加入到這個命令行界面?
如何讓咱們的命令程序寫好之後,不由於功能的添加而修改,又可靈活的加入命令、功能。
命令模式類圖:
命令模式的定義:
以命令的方式,解耦調用者與功能的具體實現者,下降系統耦合度,提供了靈活性。
適用場景:Servlet、Controller、線程池
命令模式僞代碼示例:
package com.study.design.mode.samples.command; /** * * @Description: 命令模式 * @author liguangsheng * @date 2018年11月25日 * */ public class Receiver { //存放具體的命令實現 private Map<String,Command> commands; //把具體的命令和對應的實現加入commands public void register(String strCommand,Command command) { commands.put(strCommand,command); } //使用者調用receive方法傳入命令去執行 public void receive(String command) { Command commandObj = commands.get(command); if(null != commandObj) { commandObj.exceute(); return; } System.out.println("不支持此命令" + command); } }
命令模式與策略模式的區別:
命令模式類圖:
策略模式類圖:
區別:
策略模式側重的是一個行爲的多個算法的實現,可互換算法。
命令模式側重的是爲多個行爲提供靈活的執行方式
示例:一個類對外提供了多個行爲,同時該類對象有多種狀態,不一樣的狀態下對外的行爲表現不一樣,咱們該如何來設計該類,讓它對狀態能夠靈活擴展?
如請爲無人自動咖啡售賣機開發一個控制程序。
說明:用戶能夠在咖啡機上進行支付、退款、購買、取咖啡等操做
咖啡機狀態轉換圖:
說明:
不一樣的狀態下這四種操做將有不一樣的表現。如在沒有支付的狀態下,用戶在咖啡機上點退款、購買、取咖啡,和在已支付的狀態下作這三個操做。
普通實現:
package com.study.design.mode.samples.state; /** * * @Description: 普通的咖啡機: 沒有使用狀態模式的咖啡機 * @author liguangsheng * @date 2018年11月25日 * */ public class CoffeeMachine { final static int NO_PAY = 0; final static int PAY = 1; final static int SOLD = 2; final static int SOLD_OUT = 4; private int state = SOLD_OUT; private int store; public CoffeeMachine(int store) { this.store = store; if (this.store > 0) { this.state = NO_PAY; } } public void pay() { switch (this.state) { case NO_PAY: System.out.println("支付成功,請肯定購買咖啡。"); this.state = PAY; break; case PAY: System.out.println("已支付成功,請肯定購買咖啡。"); break; case SOLD: System.out.println("待取咖啡中,請稍後購買!"); break; case SOLD_OUT: System.out.println("咖啡已售罄,不可購買!"); } } public void refund() { switch (this.state) { case NO_PAY: System.out.println("你還沒有支付,請不要亂按!"); break; case PAY: System.out.println("退款成功!"); this.state = NO_PAY; break; case SOLD: System.out.println("已購買,請取用!"); break; case SOLD_OUT: System.out.println("咖啡已售罄,不可購買!"); } } // 購買 public void buy() { switch (this.state) { case NO_PAY: System.out.println("你還沒有支付,請不要亂按!"); break; case PAY: System.out.println("購買成功,請取用!"); this.state = SOLD; break; case SOLD: System.out.println("已購買,請取用!"); break; case SOLD_OUT: System.out.println("咖啡已售罄,不可購買!"); } } // 取coffee public void getCoffee() { switch (this.state) { case NO_PAY: System.out.println("你還沒有支付,請不要亂按!"); break; case PAY: System.out.println("已購買,請取用!"); break; case SOLD: System.out.println("請放好杯子,3秒後將出咖啡!"); this.store--; if (this.store == 0) { this.state = SOLD_OUT; } else { this.state = NO_PAY; } break; case SOLD_OUT: System.out.println("咖啡已售罄,不可購買!"); } } }
如何讓狀態能夠靈活擴展?
從分析能夠看出,變化的是狀態,同時不一樣的狀態同一個行爲的表現不一樣,這樣的話就能夠把變化的狀態抽象生成接口,而後不一樣的狀態行爲實現狀態接口作該狀態下的具體行爲。這裏能夠採用狀態模式
狀態模式類圖:
狀態模式代碼示例:
把變化的狀態抽象生成接口State,裏面含有不一樣狀態下的行爲方法
package com.study.design.mode.samples.state; /** * * @Description: 狀態接口 * @author liguangsheng * @date 2018年11月25日 * */ public interface State { void pay(); void refund(); void buy(); void getCoffee(); }
不一樣的狀態實現狀態接口State
沒有支付狀態
package com.study.design.mode.samples.state; /** * * @Description: 沒有支付狀態 * @author liguangsheng * @date 2018年11月25日 * */ public class NoPayState implements State { private NewCoffeeMachine machine; public NoPayState(NewCoffeeMachine machine) { this.machine = machine; } @Override public void pay() { System.out.println("支付成功,請去肯定購買咖啡。"); this.machine.state = this.machine.PAY; } @Override public void refund() { System.out.println("你還沒有支付,請不要亂按!"); } @Override public void buy() { System.out.println("你還沒有支付,請不要亂按!"); } @Override public void getCoffee() { System.out.println("你還沒有支付,請不要亂按!"); } }
已支付狀態
package com.study.design.mode.samples.state; /** * * @Description: 已支付狀態 * @author liguangsheng * @date 2018年11月25日 * */ public class PayState implements State { private NewCoffeeMachine machine; public PayState(NewCoffeeMachine machine) { this.machine = machine; } @Override public void pay() { System.out.println("您已支付,請去肯定購買!"); } @Override public void refund() { System.out.println("退款成功,請收好!"); this.machine.state = this.machine.NO_PAY; } @Override public void buy() { System.out.println("購買成功,請取用"); this.machine.state = this.machine.SOLD; } @Override public void getCoffee() { System.out.println("請先肯定購買!"); } }
售出狀態
package com.study.design.mode.samples.state; /** * * @Description: 售出狀態 * @author liguangsheng * @date 2018年11月25日 * */ public class SoldOutState implements State { private NewCoffeeMachine machine; public SoldOutState(NewCoffeeMachine machine) { this.machine = machine; } @Override public void pay() { System.out.println("當前狀態爲售出,請取咖啡!"); } @Override public void refund() { System.out.println("當前狀態爲售出,不能退款!"); } @Override public void buy() { System.out.println("當前狀態爲售出,請取咖啡!"); } @Override public void getCoffee() { System.out.println("咖啡已出,請取咖啡!"); } }
售罄狀態
package com.study.design.mode.samples.state; /** * * @Description: 售罄狀態 * @author liguangsheng * @date 2018年11月25日 * */ public class SoldState implements State { private NewCoffeeMachine machine; public SoldState(NewCoffeeMachine machine) { this.machine = machine; } @Override public void pay() { System.out.println("咖啡已賣完,不能支付!"); } @Override public void refund() { System.out.println("不能退款!"); } @Override public void buy() { System.out.println("咖啡已賣完,不能購買!"); } @Override public void getCoffee() { System.out.println("咖啡已賣完!"); } }
使用了狀態模式的咖啡機
package com.study.design.mode.samples.state; /** * * @Description: 使用了狀態模式的咖啡機 * @author liguangsheng * @date 2018年11月25日 * */ public class NewCoffeeMachine { final State NO_PAY, PAY, SOLD, SOLD_OUT; State state; int store; //初始化狀態 public NewCoffeeMachine(int store) { NO_PAY = new NoPayState(this); PAY = new PayState(this); SOLD = new SoldState(this); SOLD_OUT = new SoldOutState(this); this.store = store; if (this.store > 0) { this.state = NO_PAY; } } //支付行爲委託給當前狀態實例 public void pay() { this.state.pay(); } //退款行爲委託給當前狀態實例 public void refund() { this.state.refund(); } //買咖啡行爲委託給當前狀態實例 public void buy() { this.state.buy(); } //取咖啡行爲委託給當前狀態實例 public void getCoffee() { this.state.getCoffee(); } }
狀態模式、命令模式、策略模式的區別
狀態模式類圖:
命令模式類圖:
策略模式類圖:
區別:
狀態模式應用於狀態機的狀況
策略模式側重的是一個行爲的多個算法的實現,可互換算法。
命令模式側重的是爲多個行爲提供靈活的執行方式
請開發一個畫圖程序,能夠畫各類顏色不一樣形狀的圖像,請用面向對象的思想設計圖像
分析:
1)好比有紅、黃、藍三種顏色
2)形狀有方形、圓、三角形
3)圓能夠是紅圓、黃圓、藍圓
變化:
會從兩個維度發生變化:形狀、顏色
任其在這兩個維度各自變化,爲這兩個維度搭個橋,讓他們能夠融合在一塊兒:橋接模式
橋接模式的實現步驟:
1)抽象:分別對各自的維度進行抽象,將共同部分抽取出來
2)組合:將抽象組合在一塊兒(橋接)
橋接模式的定義:將多個維度的變化以抽象的方式組合在一塊兒。使用者面向抽象。個維度間解耦,可自由變化。
飢漢式1——可用
package com.study.design.mode.service; public class Singleton { private final static Singleton INSTANCE = new Singleton(); private Singleton() { } public static Singleton getInstance() { return INSTANCE; } }
飢漢式2——可用
package com.study.design.mode.service; public class Singleton { private static Singleton instance; static { instance = new Singleton(); } private Singleton() { } public static Singleton getInstance() { return instance; } }
懶漢式1——不可用
package com.study.design.mode.service; public class Singleton { private static Singleton singleton; private Singleton() { } public static Singleton getInstance() { if (singleton == null) { singleton = new Singleton(); } return singleton; } }
當兩個線程同時進入if裏面時就會建立兩個實例,不是單例,線程不安全,因此不可用
懶漢式2——不推薦使用
package com.study.design.mode.service; public class Singleton { private static Singleton singleton; private Singleton() { } public static synchronized Singleton getInstance() { if (singleton == null) { singleton = new Singleton(); } return singleton; } }
線程安全,但不推薦使用。缺點是實例化後就不該該再同步了,效率低
懶漢式3——不可用
package com.study.design.mode.service; public class Singleton { private static Singleton singleton; private Singleton() { } public static Singleton getInstance() { if (singleton == null) { synchronized (Singleton.class) { singleton = new Singleton(); } } return singleton; } }
當兩個線程同時進入if裏面時就會產生兩個實例,作不到單例
懶漢式4——雙重檢查——推薦使用
package com.study.design.mode.service; public class Singleton { private static volatile Singleton singleton; private Singleton() { } public static Singleton getInstance() { if (singleton == null) { synchronized (Singleton.class) { if (singleton == null) { singleton = new Singleton(); } } } return singleton; } }
注意:volatile關鍵字修飾很關鍵,保證可見性,一個線程先建立了,其餘線程就就會看到這個改變,不會再建立,若是沒有這個關鍵字仍是不能保證單例。
優勢:線程安全;延遲加載;效率較高
懶漢式5——靜態內部類方式——推薦使用
package com.study.design.mode.service; public class Singleton { private Singleton() { } private static class SingletonInstance { private static final Singleton INSTANCE = new Singleton(); } public static Singleton getInstance() { return SingletonInstance.INSTANCE; } }
優勢:避免了線程不安全,延遲加載,效率高
原理:類的靜態屬性只會在第一次加載類的時候初始化。在這裏,JVM的加載機制幫助咱們保證了線程安全性,在類進行初始化時,別的線程是沒法進入的
懶漢式6——用枚舉——推薦使用
package com.study.design.mode.service; public enum Singleton { INSTANCE; public void whateverMethod() { } }
示例:
當咱們設計一個類時,咱們能明確它對外提供的某個方法的內部執行步驟,但一些步驟,不一樣的子類有不一樣的行爲時,咱們該如何來設計該類?
能夠用模板方法設計模式
優勢:
1)封裝不變的部分,擴展可變的部分
2)提取公共代碼,便於維護。
3)行爲由父控制,子類實現。
適用場景:
1)有多個子類共有的方法,且邏輯相同
2)重要的、複雜的方法,能夠考慮做爲模板方法
模板方法設計模式代碼示例:
package com.study.design.mode.service; public abstract class Game { protected abstract void initialize(); protected abstract void startPlay(); protected abstract void endPlay(); // 模板方法 public final void play() { // 初始化遊戲 initialize(); // 開始遊戲 startPlay(); // 結束遊戲 endPlay(); } }
設計模式總結
這些設計模式提供了一種在建立對象的同時隱藏建立邏輯的方式,而不是使用新的運算符直接實例化對象。這使得程序在判斷針對某個給定實例須要建立哪些對象時更加靈活。
這些設計模式關注類和對象的組合。繼承的概念被用來組合接口和定義組合對象得到新功能的方式。
這些設計模式特別關注對象之間的通訊。
設計原則總結
1. 變化隔離原則:找出變化,分開變化和不變的
隔離,封裝變化的部分,讓其餘部分不受它的影響。
2. 面向接口編程 ——隔離變化的方式
使用者使用接口,提供者實現接口。「接口」能夠是超類!
3. 依賴倒置原則(里氏替換原則)——隔離變化的方式
依賴抽象,不要依賴具體類!
4. 開閉原則:對修改閉合,對擴展開放——隔離變化的方式
能夠繼承一個類或者接口擴展功能,可是不能修改類或者接口的原有功能
5. 最少知道原則,又稱迪米特法則
6. 多用組合,少用繼承——靈活變化的方式
「有一個」可能比「是一個」更好。
7. 單一職責原則——方法設計的原則
每一個方法只負責一個功能,不要把不少功能寫在一個方法裏面
說明:若是前面的設計思想和設計原則都忘記了,就要找出變化,區分出不變的和變化的,把變化的部分獨立出接口,或者使用組合
示例代碼獲取地址:
https://github.com/leeSmall/FrameSourceCodeStudy/tree/master/design-mode-study
參考文章: