【設計模式面試】結構性設計模式你清楚幾個?
【金三銀四】設計模式篇
1.談談你對設計模式的理解
1.首先談設計模式的作用:經驗的傳承,提高了軟件復用的水平,最終達到提高軟件開發效率
2.設計模式的分類
3.創建型模式:都是用來幫助我們創建對象的!
4.結構性模式:關注對象和類的組織
5.行為型模式:關注系統中對象之間的相互交換,研究系統在運行時對象之間的相互通信和協作,進一步明確對象的職責,共有11中模式
2.談談你對創建型模式的理解
Java的23種設計模式分為3類,分別是
而創建型模式中有包含的如下的相關模式:
而每個設計模式的作用如下:
3.談談你對代理模式的理解
3.1 代理模式的作用
代理模式的作用是通過代理對象來增強目標對象的功能。利用的是AOP橫切的思想。
3.2 代理模式的實現方式
代理模式的實現方式有三種:靜態代理,動態代理(JDK動態代理和CGLIB動態代理)
3.2.1 靜態代理
我們先聲明接口和目標實現類
/** * 定義公共接口 */ public interface SomeService { String doSome(); }
1
2
3
4
5
6
目標類
/** * 目標對象 target */ public class SomeServiceImpl implements SomeService { @Override public String doSome() { System.out.println("目標對象:doSome()" ); return "hello ..."; } }
1
2
3
4
5
6
7
8
9
10
然后創建對應的代理類
/** * 代理類 * 增強實現類 * 和實現類實現同一個接口 */ public class SomeProxy implements SomeService{ private SomeService target; public SomeProxy(SomeService target){ this.target = target; } /** * 增強的方法 * @return */ @Override public String doSome() { System.out.println("目標方法執行之前..."); String s = target.doSome(); System.out.println("目標方法執行之后..."); return s.toUpperCase(); } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
然后測試實現
public class MainTest { public static void main(String[] args) { SomeService some = new SomeServiceImpl(); SomeProxy proxy = new SomeProxy(some); System.out.println(proxy.doSome()); } }
1
2
3
4
5
6
7
對應的輸出結果
目標方法執行之前... 目標對象:doSome() 目標方法執行之后... HELLO ...
1
2
3
4
可以看到代理對象實現了目標對象的調用,同時增強了目標對象的功能。
3.2.2 JDK動態代理
上面的靜態代理我們需要手動的創建一個對應的代理來實現,不是太靈活,針對目標對象有實現相關接口的情況,我們可以使用JDK動態代理。
public class JdkDynamicProxy { /** * JDK動態代理:目標對象必須實現相關的接口 * @param args */ public static void main(String[] args) { SomeService target = new SomeServiceImpl(); SomeService proxy = (SomeService) Proxy.newProxyInstance(JdkDynamicProxy.class.getClassLoader(), // 類加載器 target.getClass().getInterfaces() // 目標對象實現的相關接口 , new InvocationHandler() { // 代理對象的回調方法 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("before..."); Object invoke = method.invoke(target, args); System.out.println("end..."); if (invoke != null) { return invoke.toString().toUpperCase(); } return null; } }); // 通過代理對象來執行 System.out.println("proxy.doSome() = " + proxy.doSome()); } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
輸出結構
before... 目標對象:doSome() end... proxy.doSome() = HELLO ...
1
2
3
4
3.2.3 CGLIB動態代理
如果目標對象實現了對應的接口我們可以通過JDK動態代理的方式來實現,但如果目標對象沒有實現任何的接口,這時我們只能通過CGLIB動態代理來實現了,這時我們需要單獨引入cglib的依賴
public class CGLIBDynamicProxy { /** * CGLIB動態代理 * @param args */ public static void main(String[] args) { SomeService target = new SomeServiceImpl(); SomeServiceImpl proxy = new MethodInterceptor() { /** * 創建 CGLIB 代理對象的方法 * @return */ public SomeServiceImpl createProxy() { // 創建增強器 Enhancer e = new Enhancer(); // 指定父類 e.setSuperclass(target.getClass()); // 指定回調接口對象 e.setCallback(this); // 創建CGLIB代理對象 return (SomeServiceImpl) e.create(); } /** * 攔截回調的方法 */ @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("cglib -- befor" ); Object res = method.invoke(target, args); System.out.println("cglib -- end"); return res.toString().toUpperCase(); } }.createProxy(); System.out.println("proxy.doSome() = " + proxy.doSome()); } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
輸出的結果
cglib -- befor 目標對象:doSome() cglib -- end proxy.doSome() = HELLO ...
1
2
3
4
4.談談你對適配器模式的理解
4.1 適配器的作用
適配器模式的作用是把兩個不兼容的對象通過適配器能夠連接起來工作。
4.2 具體案例分析
以MyBatis中的日志模塊為例來介紹。常見的日志框架有log4j,log4j2,slf4j,logbak等,但是每種日志框架中的日志級別都有差異。
log4j2的接口:
slf4j的接口
也就是可以看到不同的日志框架里面所定義的日志級別和對應的方法都有區別,那么我們的框架怎么來統一使用這些日志框架呢?在MyBatis中通過定義了一個日志接口,定義了日志具有的級別和方法。
那這時候我們就發現具體的日志框架和這個接口其實是沒有辦法直接來使用的。
這時我們就需要通過對應的適配器來處理這種情況,以Slf4J為例。
5.談談你對裝飾者模式的理解
5.1 裝飾者模式的作用
裝飾者模式又稱為包裝模式(Wrapper),作用是用來動態的為一個對象增加新的功能。裝飾模式是一種用于代替繼承的技術, 無須通過繼承增加子類就能擴展對象的新功能 。使用對象的關聯關系代替繼承關系,更加靈活,同時避免類型體系的快速膨脹。
5.2 裝飾者模式的應用
裝飾者模式的應用場景還是非常多的,比如
IO流中的FileInputStream,FileOutputStream等
Spring中的各種Wrapper
MyBatis中的緩存設計
我們以MyBatis中的緩存實例為例來看看其具體的實現。
首先是Cache接口
然后是PerpetualCache實現:僅僅實現了數據基于內存的讀寫操作。功能單一。
裝飾類:然后在MyBatis中給我們提供了很多的裝飾類。
每個裝飾類都有自己的作用
BlockingCache:阻塞的
LruCache:根據Lru規則來淘汰緩存數據
FifoCache:根據FIFO規則來淘汰緩存數據
…
源碼中的裝飾:
6.談談你對組合模式的理解
6.1 組合模式的作用
其實解決的是對象與對象之間的包含關系。也就是 部分-整體 的層次結構。
6.2 組合模式的應用
組合模式在配置文件的加載解析中其實會用的相對比較多。以SpringSecurity的配置文件為例
上面是具體的定義
應用
7.談談你對門面模式的理解
門面模式也稱為外觀模式,他隱藏了系統的復雜性,并向客戶端提供了一個可以訪問系統的接口。這種類型的設計模式屬于結構性模式。為子系統中的一組接口提供了一個統一的訪問接口,這個接口使得子系統更容易被訪問或者使用。
具體的例子比如:MyBatis中的SqlSession接口,對外提供了數據庫操作的相關功能,具體的實現細節對調用者是隱藏的,這種模式在實際項目和框架中很頻繁
8.談談你對橋接模式的理解
橋接模式的出現是替代掉多層繼承的問題。提高了系統的擴展性。
具體的應用比如JDBC中的DriverManager其實有用到橋接模式,不同的數據庫廠商對應不同的驅動和連接
9.談談你對享元模式的理解
這個問題相對來說比較冷門,用到的也比較少,主要是針對內存這塊的節省處理,如果有很多個完全相同或相似的對象,我們可以通過享元模式,節省內存.
享元模式以共享的方式高效地支持大量細粒度對象的重用。
享元對象能做到共享的關鍵是區分了內部狀態和外部狀態。
? 內部狀態 :可以共享,不會隨環境變化而改變
? 外部狀態 :不可以共享,會隨環境變化而改變
比如以圍棋為例:
Spring 日志分析服務 LOG
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。