前言:
????掌握常用的設計模式(單例模式、工廠模式),了解其他設計模式。
????一、學習設計模式的真正目的:
????????編程時,有意識地現象接口編程,多用封裝、多態、繼承、組合等OOP思想,而不僅僅是死記幾種設計模式。
????二、常用的設計模式分類
????????創建型(創建一個對象):單例模式、工廠模式、抽象工廠模式
????????結構型(將幾個對象組織成一個結構):橋接模式、外觀模式、代理模式
????????行為型(多個對象間的通信):觀察者模式、 策略模式
????三、設計模式的定義
????????被反復使用的,代碼設計經驗的總結。
????四、設計模式的原則
????????總結起來就是,多用接口/抽象類,從而增加代碼的可擴展性(減少修改代碼)。降低模塊間的依賴和聯系。
????????體現了OOP的模塊化、可擴展性等特征。
? ? 五、設計模式的六大原則
????????1、開閉原則:對擴展開放,對修改關閉。
????????????????在程序需要進行擴展時,不去修改原有的代碼。概括起來就是:為了使程序的擴展性好,易于維護和升級。想要達到這樣的效果,我們需要使用接口和抽象類。
????????2、里氏代換原則(Liskov Substitution Principle LSP):面向對象設計的基本原則之一。
????????????里氏代換原則中說,任何基類出現的地方,子類一定可以出現。LSP是繼承復用的基石,只有當衍生類可以替換掉基類,軟件單位的功能不受影響時,基類才能真正被復用,而衍生類也能夠在基類的基礎上增加新的行為。里氏代換原則是對“開閉原則”的補充。實現開閉原則的關鍵步驟就是抽象化。而基類與子類的繼承關系就是抽象化的具體實現,所以里氏代換原則是對實現抽象化的具體步驟的規范。
????????3、依賴倒轉原則:開閉原則的基礎
????????????對接口編程,依賴于抽象而不依賴于具體
????????4、接口隔離原則:使用多個隔離的接口比使用單個接口要好
????????????還是一個降低類之間耦合度的意思。其實設計模式就是一個軟件的設計思想,從大型軟件架構出發,為了升級和維護方便 。
????????5、迪米特法則(最少知道原則)
????????????一個實體應該盡可能少的與其他實體之間發生相互作用,使得系統功能模塊相互獨立。
????????6、合成復用原則
????????????盡量使用合成/聚合的方式,而不是使用繼承。
單例模式:
? ?一、概述
? ? ? ? 0、概念:在一個類的外部有且只有該類的一個對象,所有的請求都用這個對象來處理。
????????????實現: 構造方法私有化
????????????????通過靜態方法返回該類對象
????????????????私有化該類類型的靜態變量
????????1、優點:
????????????某些類創建比較頻繁,對于一些大型的對象,這是一筆很大的系統開銷
????????????省去了new操作符,降低了系統內存的使用頻率,減輕GC壓力
????????????有些類如交易所的核心交易引擎,控制著交易流程,如果該類可以創建多個的話,系統完全亂了,所以只有使用單例模式,才能保證核心交易服務器獨立控制整個流程。
????????2、缺點:沒有接口、不能繼承,與單一職責原則沖突,一個類應該之關心內部邏輯,而不關心外部怎么樣來實例化
????二、模式
????????1、餓漢模式:靜態變量先創建,拿空間換時間。類加載的時候立即實例化對象,僅實例化一次,線程安全。
????????????優點:僅實例化一次,線程是安全的,獲取實例的速度快。
????????????缺點:類加載的時候立即實例化對象,可能實例化的對象不被使用,造成內存浪費。
????????2、懶漢模式:使用的時候先判斷,在創建變量。拿時間換空間,不能保證實例的唯一性。
????????????優點:獲取實例的時候才進行實例初始化,節省了系統資源
????????????缺點:(1)如果獲取實例時,初始化的工作量較多,加載速度會變慢,影響系統性能。
? ? ? ? ? ? ? ? ? ? ? (2)每次獲取實例都要進行非空檢查,系統開銷大
? ? ? ? ? ? ? ? ? ? ? (3)非線程安全。當多個線程同時getInstance時,可能singleton實例化未完成,singleton==null 判斷均為true,造成對象重復實例化。
????????3、Holder方式(廣泛使用):聲明類的時候,成員變量中不聲明實例變量,而是放到內部靜態類中。
????????????優點:內部類只有在外部類被調用的時候才加載,從而實現了延遲加載。線程安全且不用加鎖。
????????4、枚舉:本質上跟餓漢模式沒有任何區別,只是采用了Enumeration實現的更巧妙了。
????????????優點:僅實例化一次,線程安全,獲取實例速度快。
????????????缺點:類加載的時候立即實例化對象,可能實例化的對象不被使用,造成內存浪費。
????????5、枚舉和懶漢模式相結合
????????????優點:線程安全且不用加鎖,實現了懶加載
????????????缺點:仍然需要實力非空判斷,耗費一定的資源
????????6、雙重檢查鎖DCL+volatile
????????????優點:線程安全。進行雙重檢查,保證只在實例未初始化前進行同步,效率高
????????線程安全:餓漢式在類創建的同時就已經創建好一個靜態的對象供系統使用,以后不再改變,所以線程是天生安全的。懶漢式線程不安全,并發環境下很可能出現多個singleton實例。
工廠模式:
一、定義及使用場合
????假如現在需要創建幾個對象,且這幾個對象有共同的特征,則不需要具體創建各個對象,而是創建對象工廠類即可。
????一般常用靜態工廠模式。
????工廠模式主要是為創建對象提供了接口。
????應用場景如下:
????????在編碼時不能預見需要創建哪種類的實例。
????????系統不應依賴于產品類實例如何被創建、組合和表達的細節。
????應用實例:
????????1、你需要一輛汽車,可以直接從工廠里面提貨,而不用去管這輛汽車是怎么做出來的,以及這個汽車里面的具體實現。
????????2、Hibernate換數據庫只需換方言和驅動即可。
二、優缺點
? ? 優點:一個調用者想要創建一個對象,只要知道其名稱就可以了。
????????擴展性高,如果想增加一個產品,只要擴展一個工廠類就可以了。
?????????屏蔽產品的具體實現,調用者只關心產品的接口。
????缺點:每增加一個產品時,都需要增加一個具體類和對象實現工廠,使得系統中類的個數成倍增長,在一定程度上增加了系統的復雜度,同時也增加了系統具體類的依賴。
????????注:作為一種創建類模式,在任何需要生成復雜類對象的地方,都需要使用工廠方法模式。有一點需要注意的地方就是,使用工廠模式,而簡單對象,特別是只需要通過new就可以完成創建對象,無需使用工廠模式。如果使用工廠模式就需要引入一個工廠類,會增加系統的復雜度。
?三、實現步驟
// 1、創建接口
public interface Shape { void draw(); }// 2、創建實現接口的實體類
public class Square implements Shape{
?????@Override
?????public void draw() {
?????????System.out.println("Inside Square::draw() method.");
?????}
?}public class Circle implements Shape{
?????@Override
?????public void draw() {
? ? ? ? ?System.out.println("Inside Circle::draw() method.");
?????}
?}// 3、創建一個工廠,生成基于給定信息的實體類的對象
public class ShapeFactory {
?????public Shape getShape(String shapeType) {
?????????if(shapeType == null) {
?????????????return null;
?????????}
?????????if("CIRCLE".equalsIgnoreCase(shapeType)) {
?????????????return new Circle();
?????????}else if("SQUARE".equalsIgnoreCase(shapeType)) {
?????????????return new Square();
?????????}else if("RECTANGLE".equalsIgnoreCase(shapeType)) {
?????????????return new Rectangle(); } return null;
?????????}
}// 4、使用該工廠,通過傳遞類型信息來獲取實體類的對象
public class FactoryPatternDemo {
?????public static void main(String[] args) {
?????????ShapeFactory shape = new ShapeFactory();
?????????Shape shape2 = shape.getShape("CIRCLE");
?????????shape2.draw();
?????????Shape shape3 = shape.getShape("SQUARE");
?????????shape3.draw();
?????}
}
適配器模式:
一、概念
????將一個類的接口轉換成客戶期望的另一個接口。適配器模式使得原本由于接口不匹配的而不能一起工作的那些類可以一起工作。。實例:插座轉換器。
????適配器模式是作為兩個不兼容的接口之間的橋梁。這種類型的設計模式屬于結構型模式,他結合了兩個獨立接口的功能。這種模式涉及到一個單一的類,該類負責加入的獨立的或不兼容的接口功能。
????1、主要解決的問題:在軟件系統中,常常要將一些“現存的對象”放到新的環境中,而新環境要求的接口是現對象不能滿足的。
????2、何時使用:系統需要使用現有的類,而此類的接口不符合系統的要求。
?????????想要建立一個可以重復使用的類,用于與一些彼此之間沒有太大關聯的一些類,包括一些可能在將來引進的類一起工作,這些源類不一定有一致的接口。
????????通過接口轉換,將一個類插入另一個類系中(比如老虎和飛禽,現在多了一個飛虎,在不增加實體的需求下,增加一個適配器,在里面包容一個虎對象,實現飛的接口。)
????3、優缺點:
????????優點:可以讓任何兩個沒有關聯的類一起運行;提高了類的復用;增加了類的透明度;靈活性好。
????????缺點:過多的使用適配器,會讓系統非常凌亂,不易整體進行把握。
????????????由于java至多繼承一個類,所以至多只能適配一個適配者類,而且目標類必須是抽象類。
二、類適配器
????1、通過一個適配器,將用到的功能通過extends、implements使它們全在適配器中實現或被調用。適配器繼承并實現目標接口和原始類。
????核心思想就是:有一個Source類,擁有一個方法,待適配,目標接口是Targetable,通過Adapter類,將Source的功能擴展到Targetable里。
public class Source {
????public void method1() {
????????System.out.println("this is original method!");
????}
}
?public interface Targetable {
????/**
????* 與原類中方法相同
????*/
????public void method1();
????/**
????* 新類的方法
????*/
????public void method2();
}
public class Adapter extends Source implements Targetable{
????@Override
????public void method2() {
????????System.out.println("this is the targetable method!");
????}
}
//這樣Targetable的實現接口就有了Source類的功能
public class AdapterTest {
????public static void main(String[] args) {
????????Targetable tag = new Adapter();
????????tag.method1();
????????tag.method2();
????}
}
三、對象適配器
????1、通過組合來實現適配器的功能。在適配器中,通過組合來引用被適配者,并用構造方法將參數傳進來,然后實現接口的方法,,只需實現接口,不必繼承類。
????2、優缺點:
????????優點:同一適配器可以把適配者和它的子類都適配到目標接口
????????缺點:與類適配器相比,想要置換適配者類的方法不容易。
????3、步驟:
????只改了Adapter類。
public class Wrapper implements Targetable{
????public Source source;
????public Wrapper(Source source) {
????????super();
????????this.source = source;
????}
????@Override
? ? public void method1() {
????????source.method1();
????}
????@Override
????public void method2() {
????????System.out.println("this is targetable method!");
????}
}
四、接口適配器
????1、通過抽象類來實現適配。
????????當一個接口有n個方法,而我們只需要其中的1,2種,這時可以定義一個抽象類來實現這個接口,這個抽象類就是適配器。然后定義一個實現類繼承這個適配器,并實現所需的方法。
????3、首先定義Sourceable接口,在其中寫上所需要的所有抽象方法,一個Wrapper抽象類,實現Sourceable接口及其中的所有方法,默認實現就行,然后定義一個SourceSub1類,實現繼承Wrapper,隨便實現哪一個方法都可以。抽象類的作用就是可以讓后面繼承它的實現類只實現其所需要的方法,不必每一個實現類都實現所有接口的方法,節約了系統資源的消耗。