生活随笔
收集整理的這篇文章主要介紹了
设计模式面试题(总结最全面的面试题!!!)
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
文章目錄 設計模式 什么是設計模式 為什么要學習設計模式 設計模式分類 設計模式的六大原則 開放封閉原則(Open Close Principle) 里氏代換原則(Liskov Substitution Principle) 依賴倒轉原則(Dependence Inversion Principle) 接口隔離原則(Interface Segregation Principle) 迪米特法則(最少知道原則)(Demeter Principle) 單一職責原則(Principle of single responsibility) 單例模式 1.什么是單例 2.那些地方用到了單例模式 3.單例優缺點 4.單例模式使用注意事項: 5.單例防止反射漏洞攻擊 6.如何選擇單例創建方式 7.單例創建方式 1.餓漢式 2.懶漢式 3.靜態內部類 4.枚舉單例式 5.雙重檢測鎖方式 工廠模式 1.什么是工廠模式 2.工廠模式好處 3.為什么要學習工廠設計模式 4.Spring開發中的工廠設計模式 5.工廠模式分類 5.1 簡單工廠模式 5.2 工廠方法模式 5.3 抽象工廠模式 代理模式 1.什么是代理模式 2.代理模式應用場景 3.代理的分類 4.三種代理的區別 5.用代碼演示三種代理 5.1.靜態代理 2.2.動態代理 5.3.CGLIB動態代理 建造者模式 1.什么是建造者模式 2.建造者模式的使用場景 3.代碼案例 模板方法模式 1.什么是模板方法 2.什么時候使用模板方法 3.實際開發中應用場景哪里用到了模板方法 4.現實生活中的模板方法 5.代碼實現模板方法模式 外觀模式 原型模式 1.什么是原型模式 2.原型模式的應用場景 3.原型模式的使用方式 4.代碼演示 策略模式 1.什么是策略模式 2.策略模式應用場景 3.策略模式的優點和缺點 4.代碼演示 觀察者模式 1.什么是觀察者模式 2.模式的職責 3.觀察者模式應用場景 4.代碼實現觀察者模式 文章就到這了,沒錯,沒了 文章就到這了,沒錯,沒了
設計模式
什么是設計模式
設計模式,是一套被反復使用、多數人知曉的、經過分類編目的、代碼設計經驗的總結。使用設計模式是為了可重用代碼、讓代碼更容易被他人理解、保證代碼可靠性、程序的重用性。
為什么要學習設計模式
看懂源代碼:如果你不懂設計模式去看Jdk、Spring、SpringMVC、IO等等等等的源碼,你會很迷茫,你會寸步難行 看看前輩的代碼:你去個公司難道都是新項目讓你接手?很有可能是接盤的,前輩的開發難道不用設計模式? 編寫自己的理想中的好代碼:我個人反正是這樣的,對于我自己開發的項目我會很認真,我對他比對我女朋友還好,把項目當成自己的兒子一樣
設計模式分類
創建型模式,共五種:工廠方法模式、抽象工廠模式 、單例模式 、建造者模式、原型模式。
結構型模式,共七種:適配器模式、裝飾器模式、代理模式、外觀模式、橋接模式、組合模式、享元模式。
行為型模式,共十一種:策略模式、模板方法模式、觀察者模式、迭代子模式、責任鏈模式、命令模式、備忘錄模式、狀態模式、訪問者模式、中介者模式、解釋器模式。
設計模式的六大原則
開放封閉原則(Open Close Principle)
原則思想:盡量通過擴展軟件實體來解決需求變化,而不是通過修改已有的代碼來完成變化 描述:一個軟件產品在生命周期內,都會發生變化,既然變化是一個既定的事實,我們就應該在設計的時候盡量適應這些變化,以提高項目的穩定性和靈活性。 優點:單一原則告訴我們,每個類都有自己負責的職責,里氏替換原則不能破壞繼承關系的體系。
里氏代換原則(Liskov Substitution Principle)
原則思想:使用的基類可以在任何地方使用繼承的子類,完美的替換基類。 大概意思是:子類可以擴展父類的功能,但不能改變父類原有的功能。子類可以實現父類的抽象方法,但不能覆蓋父類的非抽象方法,子類中可以增加自己特有的方法。 優點:增加程序的健壯性,即使增加了子類,原有的子類還可以繼續運行,互不影響。
依賴倒轉原則(Dependence Inversion Principle)
接口隔離原則(Interface Segregation Principle)
這個原則的意思是:使用多個隔離的接口,比使用單個接口要好。還是一個降低類之間的耦合度的意思,從這兒我們看出,其實設計模式就是一個軟件的設計思想,從大型軟件架構出發,為了升級和維護方便。所以上文中多次出現:降低依賴,降低耦合。 例如:支付類的接口和訂單類的接口,需要把這倆個類別的接口變成倆個隔離的接口
迪米特法則(最少知道原則)(Demeter Principle)
原則思想:一個對象應當對其他對象有盡可能少地了解,簡稱類間解耦 大概意思就是一個類盡量減少自己對其他對象的依賴,原則是低耦合,高內聚,只有使各個模塊之間的耦合盡量的低,才能提高代碼的復用率。 優點:低耦合,高內聚。
單一職責原則(Principle of single responsibility)
原則思想:一個方法只負責一件事情。 描述:單一職責原則很簡單,一個方法 一個類只負責一個職責,各個職責的程序改動,不影響其它程序。 這是常識,幾乎所有程序員都會遵循這個原則。 優點:降低類和類的耦合,提高可讀性,增加可維護性和可拓展性,降低可變性的風險。
單例模式
1.什么是單例
保證一個類只有一個實例,并且提供一個訪問該全局訪問點
2.那些地方用到了單例模式
網站的計數器,一般也是采用單例模式實現,否則難以同步。 應用程序的日志應用,一般都是單例模式實現,只有一個實例去操作才好,否則內容不好追加顯示。 多線程的線程池的設計一般也是采用單例模式,因為線程池要方便對池中的線程進行控制 Windows的(任務管理器)就是很典型的單例模式,他不能打開倆個 windows的(回收站)也是典型的單例應用。在整個系統運行過程中,回收站只維護一個實例。
3.單例優缺點
優點:
在單例模式中,活動的單例只有一個實例,對單例類的所有實例化得到的都是相同的一個實例。這樣就防止其它對象對自己的實例化,確保所有的對象都訪問一個實例 單例模式具有一定的伸縮性,類自己來控制實例化進程,類就在改變實例化進程上有相應的伸縮性。 提供了對唯一實例的受控訪問。 由于在系統內存中只存在一個對象,因此可以節約系統資源,當需要頻繁創建和銷毀的對象時單例模式無疑可以提高系統的性能。 允許可變數目的實例。 避免對共享資源的多重占用。
缺點:
不適用于變化的對象,如果同一類型的對象總是要在不同的用例場景發生變化,單例就會引起數據的錯誤,不能保存彼此的狀態。 由于單利模式中沒有抽象層,因此單例類的擴展有很大的困難。 單例類的職責過重,在一定程度上違背了“單一職責原則”。 濫用單例將帶來一些負面問題,如為了節省資源將數據庫連接池對象設計為的單例類,可能會導致共享連接池對象的程序過多而出現連接池溢出;如果實例化的對象長時間不被利用,系統會認為是垃圾而被回收,這將導致對象狀態的丟失。
4.單例模式使用注意事項:
使用時不能用反射模式創建單例,否則會實例化一個新的對象 使用懶單例模式時注意線程安全問題 餓單例模式和懶單例模式構造方法都是私有的,因而是不能被繼承的,有些單例模式可以被繼承(如登記式模式)
5.單例防止反射漏洞攻擊
private static boolean flag
= false ; private Singleton ( ) { if ( flag
== false ) { flag
= ! flag
; } else { throw new RuntimeException ( "單例模式被侵犯!" ) ; }
} public static void main ( String
[ ] args
) { }
6.如何選擇單例創建方式
如果不需要延遲加載單例,可以使用枚舉或者餓漢式,相對來說枚舉性好于餓漢式。 如果需要延遲加載,可以使用靜態內部類或者懶漢式,相對來說靜態內部類好于懶韓式。 最好使用餓漢式
7.單例創建方式
(主要使用懶漢和懶漢式)
餓漢式:類初始化時,會立即加載該對象,線程天生安全,調用效率高。 懶漢式: 類初始化時,不會初始化該對象,真正需要使用的時候才會創建該對象,具備懶加載功能。 靜態內部方式:結合了懶漢式和餓漢式各自的優點,真正需要對象的時候才會加載,加載類是線程安全的。 枚舉單例: 使用枚舉實現單例模式 優點:實現簡單、調用效率高,枚舉本身就是單例,由jvm從根本上提供保障!避免通過反射和反序列化的漏洞, 缺點沒有延遲加載。 雙重檢測鎖方式 (因為JVM本質重排序的原因,可能會初始化多次,不推薦使用)
1.餓漢式
餓漢式:類初始化時,會立即加載該對象,線程天生安全,調用效率高。
package com
. lijie
;
public class Demo1 { private static Demo1 demo1
= new Demo1 ( ) ; private Demo1 ( ) { System
. out
. println ( "私有Demo1構造參數初始化" ) ; } public static Demo1
getInstance ( ) { return demo1
; } public static void main ( String
[ ] args
) { Demo1 s1
= Demo1
. getInstance ( ) ; Demo1 s2
= Demo1
. getInstance ( ) ; System
. out
. println ( s1
== s2
) ; }
}
2.懶漢式
懶漢式: 類初始化時,不會初始化該對象,真正需要使用的時候才會創建該對象,具備懶加載功能。
package com
. lijie
;
public class Demo2 { private static Demo2 demo2
; private Demo2 ( ) { System
. out
. println ( "私有Demo2構造參數初始化" ) ; } public synchronized static Demo2
getInstance ( ) { if ( demo2
== null
) { demo2
= new Demo2 ( ) ; } return demo2
; } public static void main ( String
[ ] args
) { Demo2 s1
= Demo2
. getInstance ( ) ; Demo2 s2
= Demo2
. getInstance ( ) ; System
. out
. println ( s1
== s2
) ; }
}
3.靜態內部類
靜態內部方式:結合了懶漢式和餓漢式各自的優點,真正需要對象的時候才會加載,加載類是線程安全的。
package com
. lijie
;
public class Demo3 { private Demo3 ( ) { System
. out
. println ( "私有Demo3構造參數初始化" ) ; } public static class SingletonClassInstance { private static final Demo3 DEMO_3
= new Demo3 ( ) ; } public static Demo3
getInstance ( ) { return SingletonClassInstance
. DEMO_3
; } public static void main ( String
[ ] args
) { Demo3 s1
= Demo3
. getInstance ( ) ; Demo3 s2
= Demo3
. getInstance ( ) ; System
. out
. println ( s1
== s2
) ; }
}
4.枚舉單例式
枚舉單例: 使用枚舉實現單例模式 優點:實現簡單、調用效率高,枚舉本身就是單例,由jvm從根本上提供保障!避免通過反射和反序列化的漏洞, 缺點沒有延遲加載。
package com
. lijie
;
public class Demo4 { public static Demo4
getInstance ( ) { return Demo
. INSTANCE
. getInstance ( ) ; } public static void main ( String
[ ] args
) { Demo4 s1
= Demo4
. getInstance ( ) ; Demo4 s2
= Demo4
. getInstance ( ) ; System
. out
. println ( s1
== s2
) ; } private static enum Demo
{ INSTANCE
; private Demo4 demo4
; private Demo ( ) { System
. out
. println ( "枚舉Demo私有構造參數" ) ; demo4
= new Demo4 ( ) ; } public Demo4
getInstance ( ) { return demo4
; } }
}
5.雙重檢測鎖方式
雙重檢測鎖方式 (因為JVM本質重排序的原因,可能會初始化多次,不推薦使用)
package com
. lijie
;
public class Demo5 { private static Demo5 demo5
; private Demo5 ( ) { System
. out
. println ( "私有Demo4構造參數初始化" ) ; } public static Demo5
getInstance ( ) { if ( demo5
== null
) { synchronized ( Demo5
. class ) { if ( demo5
== null
) { demo5
= new Demo5 ( ) ; } } } return demo5
; } public static void main ( String
[ ] args
) { Demo5 s1
= Demo5
. getInstance ( ) ; Demo5 s2
= Demo5
. getInstance ( ) ; System
. out
. println ( s1
== s2
) ; }
}
工廠模式
1.什么是工廠模式
它提供了一種創建對象的最佳方式。在工廠模式中,我們在創建對象時不會對客戶端暴露創建邏輯,并且是通過使用一個共同的接口來指向新創建的對象。實現了創建者和調用者分離,工廠模式分為簡單工廠、工廠方法、抽象工廠模式
2.工廠模式好處
工廠模式是我們最常用的實例化對象模式了,是用工廠方法代替new操作的一種模式。 利用工廠模式可以降低程序的耦合性,為后期的維護修改提供了很大的便利。 將選擇實現類、創建對象統一管理和控制。從而將調用者跟我們的實現類解耦。
3.為什么要學習工廠設計模式
不知道你們面試題問到過源碼沒有,你知道Spring的源碼嗎,MyBatis的源碼嗎,等等等 如果你想學習很多框架的源碼,或者你想自己開發自己的框架,就必須先掌握設計模式(工廠設計模式用的是非常非常廣泛的)
4.Spring開發中的工廠設計模式
1.Spring IOC
看過Spring源碼就知道,在Spring IOC容器創建bean的過程是使用了工廠設計模式
Spring中無論是通過xml配置還是通過配置類還是注解進行創建bean,大部分都是通過簡單工廠來進行創建的。
當容器拿到了beanName和class類型后,動態的通過反射創建具體的某個對象,最后將創建的對象放到Map中。
2.為什么Spring IOC要使用工廠設計模式創建Bean呢
在實際開發中,如果我們A對象調用B,B調用C,C調用D的話我們程序的耦合性就會變高。(耦合大致分為類與類之間的依賴,方法與方法之間的依賴。)
在很久以前的三層架構編程時,都是控制層調用業務層,業務層調用數據訪問層時,都是是直接new對象,耦合性大大提升,代碼重復量很高,對象滿天飛
為了避免這種情況,Spring使用工廠模式編程,寫一個工廠,由工廠創建Bean,以后我們如果要對象就直接管工廠要就可以,剩下的事情不歸我們管了。Spring IOC容器的工廠中有個靜態的Map集合,是為了讓工廠符合單例設計模式,即每個對象只生產一次,生產出對象后就存入到Map集合中,保證了實例不會重復影響程序效率。
5.工廠模式分類
簡單工廠 :用來生產同一等級結構中的任意產品。(不支持拓展增加產品)
工廠方法 :用來生產同一等級結構中的固定產品。(支持拓展增加產品)
抽象工廠 :用來生產不同產品族的全部產品。(不支持拓展增加產品;支持增加產品族)
我下面來使用代碼演示一下:
5.1 簡單工廠模式
什么是簡單工廠模式
簡單工廠模式相當于是一個工廠中有各種產品,創建在一個類中,客戶無需知道具體產品的名稱,只需要知道產品類所對應的參數即可。但是工廠的職責過重,而且當類型過多時不利于系統的擴展維護。
代碼演示:
創建工廠
package com
. lijie
; public interface Car { public void run ( ) ;
}
創建工廠的產品(寶馬)
package com
. lijie
; public class Bmw implements Car { public void run ( ) { System
. out
. println ( "我是寶馬汽車..." ) ; }
}
創建工另外一種產品(奧迪)
package com
. lijie
; public class AoDi implements Car { public void run ( ) { System
. out
. println ( "我是奧迪汽車.." ) ; }
}
創建核心工廠類,由他決定具體調用哪產品
package com
. lijie
; public class CarFactory { public static Car
createCar ( String name
) { if ( "" . equals ( name
) ) { return null
; } if ( name
. equals ( "奧迪" ) ) { return new AoDi ( ) ; } if ( name
. equals ( "寶馬" ) ) { return new Bmw ( ) ; } return null
; }
}
演示創建工廠的具體實例
package com
. lijie
; public class Client01 { public static void main ( String
[ ] args
) { Car aodi
= CarFactory
. createCar ( "奧迪" ) ; Car bmw
= CarFactory
. createCar ( "寶馬" ) ; aodi
. run ( ) ; bmw
. run ( ) ; }
}
單工廠的優點/缺點
優點:簡單工廠模式能夠根據外界給定的信息,決定究竟應該創建哪個具體類的對象。明確區分了各自的職責和權力,有利于整個軟件體系結構的優化。 缺點:很明顯工廠類集中了所有實例的創建邏輯,容易違反GRASPR的高內聚的責任分配原則
5.2 工廠方法模式
什么是工廠方法模式
工廠方法模式Factory Method,又稱多態性工廠模式。在工廠方法模式中,核心的工廠類不再負責所有的產品的創建,而是將具體創建的工作交給子類去做。該核心類成為一個抽象工廠角色,僅負責給出具體工廠子類必須實現的接口,而不接觸哪一個產品類應當被實例化這種細節
代碼演示:
創建工廠
package com
. lijie
; public interface Car { public void run ( ) ;
}
創建工廠方法調用接口(所有的產品需要new出來必須繼承他來實現方法)
package com
. lijie
; public interface CarFactory { Car
createCar ( ) ; }
創建工廠的產品(奧迪)
package com
. lijie
; public class AoDi implements Car { public void run ( ) { System
. out
. println ( "我是奧迪汽車.." ) ; }
}
創建工廠另外一種產品(寶馬)
package com
. lijie
; public class Bmw implements Car { public void run ( ) { System
. out
. println ( "我是寶馬汽車..." ) ; }
}
創建工廠方法調用接口的實例(奧迪)
package com
. lijie
; public class AoDiFactory implements CarFactory { public Car
createCar ( ) { return new AoDi ( ) ; }
}
創建工廠方法調用接口的實例(寶馬)
package com
. lijie
; public class BmwFactory implements CarFactory { public Car
createCar ( ) { return new Bmw ( ) ; } }
演示創建工廠的具體實例
package com
. lijie
; public class Client { public static void main ( String
[ ] args
) { Car aodi
= new AoDiFactory ( ) . createCar ( ) ; Car jili
= new BmwFactory ( ) . createCar ( ) ; aodi
. run ( ) ; jili
. run ( ) ; }
}
5.3 抽象工廠模式
什么是抽象工廠模式
抽象工廠簡單地說是工廠的工廠,抽象工廠可以創建具體工廠,由具體工廠來產生具體產品。 代碼演示:
創建第一個子工廠,及實現類
package com
. lijie
;
public interface Car { void run ( ) ;
} class CarA implements Car { public void run ( ) { System
. out
. println ( "寶馬" ) ; } } class CarB implements Car { public void run ( ) { System
. out
. println ( "摩拜" ) ; } }
創建第二個子工廠,及實現類
package com
. lijie
;
public interface Engine { void run ( ) ; } class EngineA implements Engine { public void run ( ) { System
. out
. println ( "轉的快!" ) ; } } class EngineB implements Engine { public void run ( ) { System
. out
. println ( "轉的慢!" ) ; } }
創建一個總工廠,及實現類(由總工廠的實現類決定調用那個工廠的那個實例)
package com
. lijie
; public interface TotalFactory { Car
createChair ( ) ; Engine
createEngine ( ) ;
}
class TotalFactoryReally implements TotalFactory { public Engine
createEngine ( ) { return new EngineA ( ) ; } public Car
createChair ( ) { return new CarA ( ) ; }
}
運行測試
package com
. lijie
; public class Test { public static void main ( String
[ ] args
) { TotalFactory totalFactory2
= new TotalFactoryReally ( ) ; Car car
= totalFactory2
. createChair ( ) ; car
. run ( ) ; TotalFactory totalFactory
= new TotalFactoryReally ( ) ; Engine engine
= totalFactory
. createEngine ( ) ; engine
. run ( ) ; }
}
代理模式
1.什么是代理模式
2.代理模式應用場景
Spring AOP、日志打印、異常處理、事務控制、權限控制等
3.代理的分類
靜態代理(靜態定義代理類) 動態代理(動態生成代理類,也稱為Jdk自帶動態代理) Cglib 、javaassist(字節碼操作庫)
4.三種代理的區別
靜態代理:簡單代理模式,是動態代理的理論基礎。常見使用在代理模式 jdk動態代理:使用反射完成代理。需要有頂層接口才能使用,常見是mybatis的mapper文件是代理。 cglib動態代理:也是使用反射完成代理,可以直接代理類(jdk動態代理不行),使用字節碼技術,不能對 final類進行繼承。(需要導入jar包)
5.用代碼演示三種代理
5.1.靜態代理
什么是靜態代理
由程序員創建或工具生成代理類的源碼,再編譯代理類。所謂靜態也就是在程序運行前就已經存在代理類的字節碼文件,代理類和委托類的關系在運行前就確定了。
代碼演示:
我有一段這樣的代碼:(如何能在不修改UserDao接口類的情況下開事務和關閉事務呢)
package com
. lijie
;
public class UserDao { public void save ( ) { System
. out
. println ( "保存數據方法" ) ; }
}
package com
. lijie
;
public class Test { public static void main ( String
[ ] args
) { UserDao userDao
= new UserDao ( ) ; userDao
. save ( ) ; }
}
修改代碼,添加代理類
package com
. lijie
;
public class UserDaoProxy extends UserDao { private UserDao userDao
; public UserDaoProxy ( UserDao userDao
) { this . userDao
= userDao
; } public void save ( ) { System
. out
. println ( "開啟事物..." ) ; userDao
. save ( ) ; System
. out
. println ( "關閉事物..." ) ; } }
public class Test { public static void main ( String
[ ] args
) { UserDao userDao
= new UserDao ( ) ; UserDaoProxy userDaoProxy
= new UserDaoProxy ( userDao
) ; userDaoProxy
. save ( ) ; }
}
缺點:每個需要代理的對象都需要自己重復編寫代理,很不舒服, 優點:但是可以面相實際對象或者是接口的方式實現代理
2.2.動態代理
什么是動態代理
package com
. lijie
;
public interface UserDao { void save ( ) ;
}
package com
. lijie
;
public class UserDaoImpl implements UserDao { public void save ( ) { System
. out
. println ( "保存數據方法" ) ; }
}
//下面是代理類,可重復使用,不像靜態代理那樣要自己重復編寫代理
package com
. lijie
; import java
. lang
. reflect
. InvocationHandler
;
import java
. lang
. reflect
. Method
;
public class InvocationHandlerImpl implements InvocationHandler { private Object target
; public InvocationHandlerImpl ( Object target
) { this . target
= target
; } public Object
invoke ( Object proxy
, Method method
, Object
[ ] args
) throws Throwable
{ System
. out
. println ( "調用開始處理" ) ; Object result
= method
. invoke ( target
, args
) ; System
. out
. println ( "調用結束處理" ) ; return result
; }
}
package com
. lijie
; import java
. lang
. reflect
. Proxy
; public class Test { public static void main ( String
[ ] args
) { UserDao userDaoImpl
= new UserDaoImpl ( ) ; InvocationHandlerImpl invocationHandlerImpl
= new InvocationHandlerImpl ( userDaoImpl
) ; ClassLoader loader
= userDaoImpl
. getClass ( ) . getClassLoader ( ) ; Class
< ? > [ ] interfaces
= userDaoImpl
. getClass ( ) . getInterfaces ( ) ; UserDao newProxyInstance
= ( UserDao
) Proxy
. newProxyInstance ( loader
, interfaces
, invocationHandlerImpl
) ; newProxyInstance
. save ( ) ; }
}
缺點:必須是面向接口,目標業務類必須實現接口 優點:不用關心代理類,只需要在運行階段才指定代理哪一個對象
5.3.CGLIB動態代理
CGLIB動態代理原理:
利用asm開源包,對代理對象類的class文件加載進來,通過修改其字節碼生成子類來處理。
什么是CGLIB動態代理
CGLIB動態代理和jdk代理一樣,使用反射完成代理,不同的是他可以直接代理類(jdk動態代理不行,他必須目標業務類必須實現接口),CGLIB動態代理底層使用字節碼技術,CGLIB動態代理不能對 final類進行繼承。(CGLIB動態代理需要導入jar包)
代碼演示:
package com
. lijie
;
public interface UserDao { void save ( ) ;
}
package com
. lijie
;
public class UserDaoImpl implements UserDao { public void save ( ) { System
. out
. println ( "保存數據方法" ) ; }
}
package com
. lijie
; import org
. springframework
. cglib
. proxy
. Enhancer
;
import org
. springframework
. cglib
. proxy
. MethodInterceptor
;
import org
. springframework
. cglib
. proxy
. MethodProxy
;
import java
. lang
. reflect
. Method
;
public class CglibProxy implements MethodInterceptor { private Object targetObject
; public Object
getInstance ( Object target
) { this . targetObject
= target
; Enhancer enhancer
= new Enhancer ( ) ; enhancer
. setSuperclass ( target
. getClass ( ) ) ; enhancer
. setCallback ( this ) ; return enhancer
. create ( ) ; } public Object
intercept ( Object obj
, Method method
, Object
[ ] args
, MethodProxy proxy
) throws Throwable
{ System
. out
. println ( "開啟事物" ) ; Object result
= proxy
. invoke ( targetObject
, args
) ; System
. out
. println ( "關閉事物" ) ; return result
; }
}
package com
. lijie
;
public class Test { public static void main ( String
[ ] args
) { CglibProxy cglibProxy
= new CglibProxy ( ) ; UserDao userDao
= ( UserDao
) cglibProxy
. getInstance ( new UserDaoImpl ( ) ) ; userDao
. save ( ) ; }
}
建造者模式
1.什么是建造者模式
建造者模式通常包括下面幾個角色:
uilder:給出一個抽象接口,以規范產品對象的各個組成成分的建造。這個接口規定要實現復雜對象的哪些部分的創建,并不涉及具體的對象部件的創建。 ConcreteBuilder:實現Builder接口,針對不同的商業邏輯,具體化復雜對象的各部分的創建。 在建造過程完成后,提供產品的實例。 Director:調用具體建造者來創建復雜對象的各個部分,在指導者中不涉及具體產品的信息,只負責保證對象各部分完整創建或按某種順序創建。 Product:要創建的復雜對象。
2.建造者模式的使用場景
使用場景:
需要生成的對象具有復雜的內部結構。 需要生成的對象內部屬性本身相互依賴。
與工廠模式的區別是:建造者模式更加關注與零件裝配的順序。
JAVA 中的 StringBuilder就是建造者模式創建的,他把一個單個字符的char數組組合起來
Spring不是建造者模式,它提供的操作應該是對于字符串本身的一些操作,而不是創建或改變一個字符串。
3.代碼案例
建立一個裝備對象Arms
package com
. lijie
;
public class Arms { private String helmet
; private String armor
; private String weapon
;
}
創建Builder接口(給出一個抽象接口,以規范產品對象的各個組成成分的建造,這個接口只是規范)
package com
. lijie
; public interface PersonBuilder { void builderHelmetMurder ( ) ; void builderArmorMurder ( ) ; void builderWeaponMurder ( ) ; void builderHelmetYanLong ( ) ; void builderArmorYanLong ( ) ; void builderWeaponYanLong ( ) ; Arms
BuilderArms ( ) ;
}
創建Builder實現類(這個類主要實現復雜對象創建的哪些部分需要什么屬性)
package com
. lijie
; public class ArmsBuilder implements PersonBuilder { private Arms arms
; public ArmsBuilder ( ) { arms
= new Arms ( ) ; } public void builderHelmetMurder ( ) { arms
. setHelmet ( "奪命頭盔" ) ; } public void builderArmorMurder ( ) { arms
. setArmor ( "奪命鎧甲" ) ; } public void builderWeaponMurder ( ) { arms
. setWeapon ( "奪命寶刀" ) ; } public void builderHelmetYanLong ( ) { arms
. setHelmet ( "炎龍頭盔" ) ; } public void builderArmorYanLong ( ) { arms
. setArmor ( "炎龍鎧甲" ) ; } public void builderWeaponYanLong ( ) { arms
. setWeapon ( "炎龍寶刀" ) ; } public Arms
BuilderArms ( ) { return arms
; }
}
Director(調用具體建造者來創建復雜對象的各個部分,在指導者中不涉及具體產品的信息,只負責保證對象各部分完整創建或按某種順序創建)
package com
. lijie
; public class PersonDirector { public Arms
constructPerson ( PersonBuilder pb
) { pb
. builderHelmetYanLong ( ) ; pb
. builderArmorMurder ( ) ; pb
. builderWeaponMurder ( ) ; return pb
. BuilderArms ( ) ; } public static void main ( String
[ ] args
) { PersonDirector pb
= new PersonDirector ( ) ; Arms arms
= pb
. constructPerson ( new ArmsBuilder ( ) ) ; System
. out
. println ( arms
. getHelmet ( ) ) ; System
. out
. println ( arms
. getArmor ( ) ) ; System
. out
. println ( arms
. getWeapon ( ) ) ; }
}
模板方法模式
1.什么是模板方法
模板方法模式:定義一個操作中的算法骨架(父類),而將一些步驟延遲到子類中。 模板方法使得子類可以不改變一個算法的結構來重定義該算法的
2.什么時候使用模板方法
實現一些操作時,整體步驟很固定,但是呢。就是其中一小部分需要改變,這時候可以使用模板方法模式,將容易變的部分抽象出來,供子類實現。
3.實際開發中應用場景哪里用到了模板方法
其實很多框架中都有用到了模板方法模式 例如:數據庫訪問的封裝、Junit單元測試、servlet中關于doGet/doPost方法的調用等等
4.現實生活中的模板方法
例如:
去餐廳吃飯,餐廳給我們提供了一個模板就是:看菜單,點菜,吃飯,付款,走人 (這里 “點菜和付款 ” 是不確定的由子類來完成的,其他的則是一個模板。)
5.代碼實現模板方法模式
先定義一個模板。把模板中的點菜和付款,讓子類來實現。
package com
. lijie
;
public abstract class RestaurantTemplate { public void menu ( ) { System
. out
. println ( "看菜單" ) ; } abstract void spotMenu ( ) ; public void havingDinner ( ) { System
. out
. println ( "吃飯" ) ; } abstract void payment ( ) ; public void GoR ( ) { System
. out
. println ( "走人" ) ; } public void process ( ) { menu ( ) ; spotMenu ( ) ; havingDinner ( ) ; payment ( ) ; GoR ( ) ; }
}
具體的模板方法子類 1
package com
. lijie
; public class RestaurantGinsengImpl extends RestaurantTemplate { void spotMenu ( ) { System
. out
. println ( "人參" ) ; } void payment ( ) { System
. out
. println ( "5快" ) ; }
}
具體的模板方法子類 2
package com
. lijie
; public class RestaurantLobsterImpl extends RestaurantTemplate { void spotMenu ( ) { System
. out
. println ( "龍蝦" ) ; } void payment ( ) { System
. out
. println ( "50塊" ) ; }
}
客戶端測試
package com
. lijie
; public class Client { public static void main ( String
[ ] args
) { RestaurantTemplate restaurantTemplate
= new RestaurantGinsengImpl ( ) ; restaurantTemplate
. process ( ) ; }
}
外觀模式
1.什么是外觀模式
外觀模式:也叫門面模式,隱藏系統的復雜性,并向客戶端提供了一個客戶端可以訪問系統的接口。
它向現有的系統添加一個接口,用這一個接口來隱藏實際的系統的復雜性。
使用外觀模式,他外部看起來就是一個接口,其實他的內部有很多復雜的接口已經被實現
2.外觀模式例子
用戶注冊完之后,需要調用阿里短信接口、郵件接口、微信推送接口。
創建阿里短信接口
package com
. lijie
;
public interface AliSmsService { void sendSms ( ) ;
}
package com
. lijie
; public class AliSmsServiceImpl implements AliSmsService { public void sendSms ( ) { System
. out
. println ( "阿里短信消息" ) ; } }
創建郵件接口
package com
. lijie
;
public interface EamilSmsService { void sendSms ( ) ;
}
package com
. lijie
; public class EamilSmsServiceImpl implements EamilSmsService { public void sendSms ( ) { System
. out
. println ( "發送郵件消息" ) ; }
}
創建微信推送接口
package com
. lijie
;
public interface WeiXinSmsService { void sendSms ( ) ;
}
package com
. lijie
; public class WeiXinSmsServiceImpl implements WeiXinSmsService { public void sendSms ( ) { System
. out
. println ( "發送微信消息推送" ) ; }
}
創建門面(門面看起來很簡單使用,復雜的東西以及被門面給封裝好了)
package com
. lijie
; public class Computer { AliSmsService aliSmsService
; EamilSmsService eamilSmsService
; WeiXinSmsService weiXinSmsService
; public Computer ( ) { aliSmsService
= new AliSmsServiceImpl ( ) ; eamilSmsService
= new EamilSmsServiceImpl ( ) ; weiXinSmsService
= new WeiXinSmsServiceImpl ( ) ; } public void sendMsg ( ) { aliSmsService
. sendSms ( ) ; eamilSmsService
. sendSms ( ) ; weiXinSmsService
. sendSms ( ) ; }
}
啟動測試
package com
. lijie
; public class Client { public static void main ( String
[ ] args
) { AliSmsService aliSmsService
= new AliSmsServiceImpl ( ) ; EamilSmsService eamilSmsService
= new EamilSmsServiceImpl ( ) ; WeiXinSmsService weiXinSmsService
= new WeiXinSmsServiceImpl ( ) ; aliSmsService
. sendSms ( ) ; eamilSmsService
. sendSms ( ) ; weiXinSmsService
. sendSms ( ) ; new Computer ( ) . sendMsg ( ) ; }
}
原型模式
1.什么是原型模式
2.原型模式的應用場景
類初始化需要消化非常多的資源,這個資源包括數據、硬件資源等。這時我們就可以通過原型拷貝避免這些消耗。 通過new產生的一個對象需要非常繁瑣的數據準備或者權限,這時可以使用原型模式。 一個對象需要提供給其他對象訪問,而且各個調用者可能都需要修改其值時,可以考慮使用原型模式拷貝多個對象供調用者使用,即保護性拷貝。
我們Spring框架中的多例就是使用原型。
3.原型模式的使用方式
實現Cloneable接口。在java語言有一個Cloneable接口,它的作用只有一個,就是在運行時通知虛擬機可以安全地在實現了此接口的類上使用clone方法。在java虛擬機中,只有實現了這個接口的類才可以被拷貝,否則在運行時會拋出CloneNotSupportedException異常。
重寫Object類中的clone方法。Java中,所有類的父類都是Object類,Object類中有一個clone方法,作用是返回對象的一個拷貝,但是其作用域protected類型的,一般的類無法調用,因此Prototype類需要將clone方法的作用域修改為public類型。
3.1原型模式分為淺復制和深復制
(淺復制)只是拷貝了基本類型的數據,而引用類型數據,只是拷貝了一份引用地址。
(深復制)在計算機中開辟了一塊新的內存地址用于存放復制的對象。
4.代碼演示
創建User類
package com
. lijie
; import java
. util
. ArrayList
; public class User implements Cloneable { private String name
; private String password
; private ArrayList
< String> phones
; protected User
clone ( ) { try { User user
= ( User
) super . clone ( ) ; user
. phones
= ( ArrayList
< String> ) this . phones
. clone ( ) ; return user
; } catch ( CloneNotSupportedException e
) { e
. printStackTrace ( ) ; } return null
; }
}
測試復制
package com
. lijie
; import java
. util
. ArrayList
; public class Client { public static void main ( String
[ ] args
) { User user
= new User ( ) ; user
. setName ( "李三" ) ; user
. setPassword ( "123456" ) ; ArrayList
< String> phones
= new ArrayList < > ( ) ; phones
. add ( "17674553302" ) ; user
. setPhones ( phones
) ; User user2
= user
. clone ( ) ; user2
. setPassword ( "654321" ) ; System
. out
. println ( user
== user2
) ; System
. out
. println ( user
. getName ( ) + " | " + user2
. getName ( ) ) ; System
. out
. println ( user
. getPassword ( ) + " | " + user2
. getPassword ( ) ) ; System
. out
. println ( user
. getPhones ( ) == user2
. getPhones ( ) ) ; }
}
如果不需要深復制,需要刪除User 中的
user
. phones
= ( ArrayList
< String> ) this . phones
. clone ( ) ;
策略模式
1.什么是策略模式
2.策略模式應用場景
策略模式的用意是針對一組算法或邏輯,將每一個算法或邏輯封裝到具有共同接口的獨立的類中,從而使得它們之間可以相互替換。
例如:我要做一個不同會員打折力度不同的三種策略,初級會員,中級會員,高級會員(三種不同的計算)。
例如:我要一個支付模塊,我要有微信支付、支付寶支付、銀聯支付等
3.策略模式的優點和缺點
4.代碼演示
定義抽象的公共方法
package com
. lijie
;
abstract class PayStrategy { abstract void algorithmInterface ( ) ; }
定義實現微信支付
package com
. lijie
; class PayStrategyA extends PayStrategy { void algorithmInterface ( ) { System
. out
. println ( "微信支付" ) ; }
}
定義實現支付寶支付
package com
. lijie
; class PayStrategyB extends PayStrategy { void algorithmInterface ( ) { System
. out
. println ( "支付寶支付" ) ; }
}
定義實現銀聯支付
package com
. lijie
; class PayStrategyC extends PayStrategy { void algorithmInterface ( ) { System
. out
. println ( "銀聯支付" ) ; }
}
定義下文維護算法策略
package com
. lijie
; class Context { PayStrategy strategy
; public Context ( PayStrategy strategy
) { this . strategy
= strategy
; } public void algorithmInterface ( ) { strategy
. algorithmInterface ( ) ; } }
運行測試
package com
. lijie
; class ClientTestStrategy { public static void main ( String
[ ] args
) { Context context
; context
= new Context ( new PayStrategyA ( ) ) ; context
. algorithmInterface ( ) ; context
= new Context ( new PayStrategyB ( ) ) ; context
. algorithmInterface ( ) ; context
= new Context ( new PayStrategyC ( ) ) ; context
. algorithmInterface ( ) ; }
}
觀察者模式
1.什么是觀察者模式
2.模式的職責
觀察者模式主要用于1對N的通知。當一個對象的狀態變化時,他需要及時告知一系列對象,令他們做出相應。
實現有兩種方式:
推:每次都會把通知以廣播的方式發送給所有觀察者,所有的觀察者只能被動接收。 拉:觀察者只要知道有情況即可,至于什么時候獲取內容,獲取什么內容,都可以自主決定。
3.觀察者模式應用場景
關聯行為場景,需要注意的是,關聯行為是可拆分的,而不是“組合”關系。事件多級觸發場景。 跨系統的消息交換場景,如消息隊列、事件總線的處理機制。
4.代碼實現觀察者模式
定義抽象觀察者,每一個實現該接口的實現類都是具體觀察者。
package com
. lijie
;
public interface Observer { void update ( int state
) ;
}
定義具體觀察者
package com
. lijie
;
public class ObserverImpl implements Observer { private int myState
; public void update ( int state
) { myState
= state
; System
. out
. println ( "收到消息,myState值改為:" + state
) ; } public int getMyState ( ) { return myState
; }
}
定義主題。主題定義觀察者數組,并實現增、刪及通知操作。
package com
. lijie
; import java
. util
. Vector
;
public class Subjecct { private Vector
< Observer> list
= new Vector < > ( ) ; public void registerObserver ( Observer obs
) { list
. add ( obs
) ; } public void removeObserver ( Observer obs
) { list
. remove ( obs
) ; } public void notifyAllObserver ( int state
) { for ( Observer observer
: list
) { observer
. update ( state
) ; } }
}
定義具體的,他繼承繼承Subject類,在這里實現具體業務,在具體項目中,該類會有很多。
package com
. lijie
;
public class RealObserver extends Subjecct { private int state
; public int getState ( ) { return state
; } public void setState ( int state
) { this . state
= state
; this . notifyAllObserver ( state
) ; }
}
運行測試
package com
. lijie
; public class Client { public static void main ( String
[ ] args
) { RealObserver subject
= new RealObserver ( ) ; ObserverImpl obs1
= new ObserverImpl ( ) ; ObserverImpl obs2
= new ObserverImpl ( ) ; ObserverImpl obs3
= new ObserverImpl ( ) ; subject
. registerObserver ( obs1
) ; subject
. registerObserver ( obs2
) ; subject
. registerObserver ( obs3
) ; subject
. setState ( 300 ) ; System
. out
. println ( "obs1觀察者的MyState狀態值為:" + obs1
. getMyState ( ) ) ; System
. out
. println ( "obs2觀察者的MyState狀態值為:" + obs2
. getMyState ( ) ) ; System
. out
. println ( "obs3觀察者的MyState狀態值為:" + obs3
. getMyState ( ) ) ; subject
. setState ( 400 ) ; System
. out
. println ( "obs1觀察者的MyState狀態值為:" + obs1
. getMyState ( ) ) ; System
. out
. println ( "obs2觀察者的MyState狀態值為:" + obs2
. getMyState ( ) ) ; System
. out
. println ( "obs3觀察者的MyState狀態值為:" + obs3
. getMyState ( ) ) ; }
}
文章就到這了,沒錯,沒了
察者方法 public void removeObserver(Observer obs) { list.remove(obs); }
// 通知所有的觀察者更新
public void notifyAllObserver(int state) {for (Observer observer : list) {observer.update(state);}
}
}
4. 定義具體的,他繼承繼承Subject類,在這里實現具體業務,在具體項目中,該類會有很多。
```java
package com.lijie;//具體主題
public class RealObserver extends Subjecct {//被觀察對象的屬性private int state;public int getState(){return state;}public void setState(int state){this.state=state;//主題對象(目標對象)值發生改變this.notifyAllObserver(state);}
}
運行測試
package com
. lijie
; public class Client { public static void main ( String
[ ] args
) { RealObserver subject
= new RealObserver ( ) ; ObserverImpl obs1
= new ObserverImpl ( ) ; ObserverImpl obs2
= new ObserverImpl ( ) ; ObserverImpl obs3
= new ObserverImpl ( ) ; subject
. registerObserver ( obs1
) ; subject
. registerObserver ( obs2
) ; subject
. registerObserver ( obs3
) ; subject
. setState ( 300 ) ; System
. out
. println ( "obs1觀察者的MyState狀態值為:" + obs1
. getMyState ( ) ) ; System
. out
. println ( "obs2觀察者的MyState狀態值為:" + obs2
. getMyState ( ) ) ; System
. out
. println ( "obs3觀察者的MyState狀態值為:" + obs3
. getMyState ( ) ) ; subject
. setState ( 400 ) ; System
. out
. println ( "obs1觀察者的MyState狀態值為:" + obs1
. getMyState ( ) ) ; System
. out
. println ( "obs2觀察者的MyState狀態值為:" + obs2
. getMyState ( ) ) ; System
. out
. println ( "obs3觀察者的MyState狀態值為:" + obs3
. getMyState ( ) ) ; }
}
文章就到這了,沒錯,沒了
如果不是必要,準備上面那九個設計模式就好了,全部記住有點難
總結
以上是生活随笔 為你收集整理的设计模式面试题(总结最全面的面试题!!!) 的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網站內容還不錯,歡迎將生活随笔 推薦給好友。