網(wǎng)站icp備案 年檢2345網(wǎng)址導(dǎo)航中國最好
Java設(shè)計模式筆記(一)
(23種設(shè)計模式由于篇幅較大分為兩篇展示)
一、設(shè)計模式介紹
1、設(shè)計模式的目的
讓程序具有更好的:
- 代碼重用性
- 可讀性
- 可擴(kuò)展性
- 可靠性
- 高內(nèi)聚,低耦合
2、設(shè)計模式的七大原則
-
單一職責(zé)原則
一個類只負(fù)責(zé)一項職責(zé),降低類的復(fù)雜度,提高類的可讀性和可維護(hù)性,降低變更引起的風(fēng)險,
-
接口隔離原則
一個類對另一個類的依賴應(yīng)該建立在最小接口上
上述圖片,類A只需要接口中的1方法,但是卻要實(shí)現(xiàn)2345方法,這就違背了最小接口原則,對其進(jìn)行改進(jìn):
對接口拆分成幾個獨(dú)立的接口,采用接口隔離原則
-
依賴倒置原則
-
高層模塊不應(yīng)該依賴底層模塊,二者都應(yīng)該依賴其抽象
-
抽象不應(yīng)該依賴細(xì)節(jié),細(xì)節(jié)應(yīng)該依賴抽象
-
依賴倒轉(zhuǎn)(倒置)的中心思想是面向接口編程
-
使用接口或抽象類的目的是制定好規(guī)范,而不涉及任何具體的操作,把展現(xiàn)細(xì)節(jié)的任務(wù)交給他們的實(shí)現(xiàn)類去完成
如圖所示:
如果之后有個短信業(yè)務(wù),則需要再person中在加個短信的業(yè)務(wù),對其進(jìn)行改進(jìn)
引入接口IReceiver,Person類與接口IReceiver發(fā)生依賴關(guān)系,及時之后又短信業(yè)務(wù)也只需要實(shí)現(xiàn)IReceiver接口即可。
-
-
里氏替換原則
繼承性說明:
? 父類中凡是已經(jīng)實(shí)現(xiàn)好的方法,實(shí)際上是在設(shè)定規(guī)范和契約,雖然它不強(qiáng)制要求所有的子類必須遵循這些契約,但是如果子類對這些已經(jīng)實(shí)現(xiàn)的方法任意修改,就會對整個繼承體系造成破壞。
繼承性弊端:
? 使用繼承會給程序帶來侵入性,程序的可移植性降低增加對象間的耦合性,如果一個類被其他的類所繼承,則當(dāng)這個類需要修改時,必須考慮到所有的子類,并且父類修改后,所有涉及到子類的功能都有可能產(chǎn)生
介紹:
? 簡單說就是:所有引用基類的地方必須透明的使用其子類的對象。
? 在繼承時,子類中盡量不要重寫父類中的方法
上圖中,父類中的add方法是a+b,但是子類B重寫了父類的add方法將其改為了a-b,當(dāng)程序調(diào)用的時候可能會出現(xiàn)錯誤。
采用依賴、聚合、組合的方法替換,抽取一個共同的父類,在使用C的時候還可使用B的原始方法。
1、創(chuàng)建一個基類
class Base {}
2、A類
class A extends Base {public int fun1(int a, int b) {return a+b;} }
3、B類
class B extends Base {private A a = new A();// 寫自己的方法public int fun1(int a, int b) {return a+b;}// 仍然使用A的方法public int fun2(int a, int b) {return this.a.fun1(a,b);} }
-
開閉原則
? 是編程中最基礎(chǔ)、最重要的設(shè)計原則;當(dāng)軟件需要修改時,盡量通過擴(kuò)展軟件實(shí)體的行為來實(shí)現(xiàn)變化,而不是通過修改已有的代碼來實(shí)現(xiàn)變化。
代碼演示:
//這是一個用于繪圖的類 class GraphicEditor {//接收 Shape 對象,然后根據(jù) type,來繪制不同的圖形public void drawShape(Shape s){if(s.m_type=-1)drawRectangle(s);else if(s.m type-2)drawCircle(s);else if(s.m_type - 3)drawTriangle(s)}//繪制矩形public void drawRectangle(Shape r){System.out.printn(”繪制矩形");}//繪制圓形public void drawCircle(Shape r){System.out.println("繪制圓形");}//繪制三角形public void drawTriangle(Shape r){System.out.println(”繪制三角形");} }//Shape 類,基類 class Shape {int m_type; }class Rectangle extends Shape {Rectangle(){super.m_type=1;} }class Circle extends Shape {Circle() {super.m_type =2;} }//新增畫三角形 class Triangle extends Shape {Triangle() {super.m_type = 3;} }// 開始調(diào)用 public class Ocp {public static void main(Stringll args){//使用看看存在的問題GraphicEditor graphicEditor = new GraphicEditor();graphicEditor.drawShape(new Rectangle());graphicEditor.drawShape(new Circle());graphicEditor.drawShape(new Triangle());} }
上述代碼的優(yōu)缺點(diǎn):
? 1、比較好理解
? 2、缺點(diǎn)就是違反了設(shè)計模式的ocp原則。即對擴(kuò)展開放,對修改關(guān)閉,當(dāng)要新增功能時GraphicEditor需要做修改。
改進(jìn):
? 把創(chuàng)建 Shape 類做成抽象類,并提供一個抽象的 draw方法,讓子類去實(shí)現(xiàn)即可,這樣我們有新的圖形種類時,只需要讓新的圖形類繼承 Shape,并實(shí)現(xiàn) draw方法即可,使用方的代碼就不需要修 -> 滿足了開閉原則
//這是一個用于繪圖的類 class GraphicEditor {//接收 Shape 對象調(diào)用draw方法public void drawShape(Shape s){s.draw();} }//Shape 類,基類 abstract class Shape {int m_type;public abstract void draw();//抽象方法 }class Rectangle extends Shape {Rectangle(){super.m_type=1;}@Overridepublic void draw(){// TODO Auto-generated method stubSystem.out.println("繪制矩形");} }class Circle extends Shape {Circle() {super.m_type =2;}@Overridepublic void draw(){// TODO Auto-generated method stubSystem.out.println("繪制圓形");} }//新增畫三角形 class Triangle extends Shape {Triangle() {super.m_type = 3;}@Overridepublic void draw(){// TODO Auto-generated method stubSystem.out.println("繪制三角形");} }// 開始調(diào)用 public class Ocp {public static void main(Stringll args){//使用看看存在的問題GraphicEditor graphicEditor = new GraphicEditor();graphicEditor.drawShape(new Rectangle());graphicEditor.drawShape(new Circle());graphicEditor.drawShape(new Triangle());} }
-
迪米特法則
? 迪米特法則(Demeter Principle)又叫最少知道原則,即一個類對自己依賴的類知道的越少越好。也就是說,對于被依賴的類不管多么復(fù)雜,都盡量將邏輯封裝在類的內(nèi)部。對外除了提供的 public 方法,不對外泄露任何信息。
-
合成復(fù)用原則
盡量使用合成/聚合的方式,而不是使用繼承;
二、設(shè)計模式概述
1、介紹
? 設(shè)計模式是程序員在面對同類軟件工程設(shè)計問題所總結(jié)出來的有用的經(jīng)驗(yàn),模式不是代碼,而是某類問題的通用解決方案,設(shè)計模式(Design patern)代表了最佳的實(shí)踐。這些解決方案是眾多軟件開發(fā)人員經(jīng)過相當(dāng)長的一段時間的試驗(yàn)和錯誤總結(jié)出來的。
? 設(shè)計模式的本質(zhì)提高軟件的維護(hù)性,通用性和擴(kuò)展性,并降低軟件的復(fù)雜度,
2、設(shè)計模式類型
設(shè)計模式分為三種類型,共 23種
1)創(chuàng)建型模式:單例模式、抽象工廠模式、原型模式、建造者模式、工廠模式。
2)結(jié)構(gòu)型模式:適配器模式、橋接模式、裝飾模式、組合模式、外觀模式、享元模式、代理模式。
3)行為型模式:模版方法模式、命令模式、訪問者模式、迭代器模式、觀察者模式、中介者模式、備忘錄模式.解釋器模式(Interpreter 模式)、狀態(tài)模式、策略模式、職責(zé)鏈模式(責(zé)任鏈模式)。
三、單例設(shè)計模式
1、介紹
? 所謂類的單例設(shè)計模式,就是采取一定的方法保證在整個的軟件系統(tǒng)中,對某個類只能存在一個對象實(shí)例,并且該類只提供一個取得其對象實(shí)例的方法(靜態(tài)方法)。
2、單例模式的8種方式
- 餓漢式(靜態(tài)常量)
- 餓漢式(靜態(tài)代碼塊)
- 懶漢式(線程不安全)
- 懶漢式(線程安全,同步方法)
- 懶漢式(線程安全,同步代碼塊)
- 雙重檢查
- 靜態(tài)內(nèi)部類
- 枚舉
(1)餓漢式(靜態(tài)常量)
實(shí)現(xiàn)步驟:
-
構(gòu)造器私有化(防止 new )
-
類的內(nèi)部創(chuàng)建對象
-
向外暴露一個靜態(tài)的公共方法。getlnstance
-
代碼實(shí)現(xiàn)
//餓漢式(靜態(tài)變量)
class Singleton {//1.構(gòu)造器私有化,外部能 newprivate Singleton() {}//2.本類內(nèi)部創(chuàng)建對象實(shí)例private final static Singleton instance = new Singleton().//3.提供一個公有的靜態(tài)方法,返回實(shí)例對象public static Singleton getInstance(){return instance;}
}public class SingletonTest {public static void main(String|largs){//測試Singleton instance = Singleton.getInstance();Singleton instance2 = Singleton.getInstance();System.out.println(instance == instance2); // trueSystem.out.println("instance.hashCode=" + instance .hashCode());System.out.println("instance2.hashCode=" + instance2.hashCode()):}
}
說明:
1)優(yōu)點(diǎn):這種寫法比較簡單,就是在類裝載的時候就完成實(shí)例化。避免了線程同步問題。
2)缺點(diǎn):在類裝載的時候就完成實(shí)例化,沒有達(dá)到LazyLoading 的效果。如果從始至終從未使用過這個實(shí)例,則 會造成內(nèi)存的浪費(fèi)
3)這種方式基于 classloder 機(jī)制避免了多線程的同步問題,不過,instance 在類裝載時就實(shí)例化,在單例模式中大多數(shù)都是調(diào)用 getnstance方法,但是導(dǎo)致類裝載的原因有很多種,因此不能確定有其他的方式(或者其他的靜態(tài)方法)導(dǎo)致類裝載,這時候初始化instance 就沒有達(dá)到 lazy loading 的效果
(2)餓漢式(靜態(tài)代碼塊)
public class Singleton {//1.構(gòu)造器私有化,外部能 newprivate Singleton(){}//2.本類內(nèi)部創(chuàng)建實(shí)例對象private static Singleton instance;//3. 在靜態(tài)代碼塊中創(chuàng)建單例對象static {instance = new Singleton();}// 4.提供一個公有的靜態(tài)方法,返回實(shí)例對象public static Singleton getInstance (){return instance;}}
說明:
1)這種方式和上面的方式其實(shí)類似,只不過將類實(shí)例化的過程放在了靜態(tài)代碼塊中,也是在類裝載的時候,就執(zhí)行靜態(tài)代碼塊中的代碼,初始化類的實(shí)例。優(yōu)缺點(diǎn)和上面是一樣的。
2)這種單例模式可用,但是可能造成內(nèi)存浪費(fèi)
(3)懶漢式(線程不安全)
public class Singleton {//1.構(gòu)造器私有化,外部能 newprivate Singleton(){}//2.本類內(nèi)部創(chuàng)建實(shí)例對象private static Singleton instance;//3. 提供一個靜態(tài)公有方法,當(dāng)使用到該方法時,才去創(chuàng)建instancepublic static Singleton getInstance (){if (instance == null) {instance = new Singleton();}return instance;}}
說明:
1)起到了 Lazy Loading的效果,但是只能在單線程下使用。
2)如果在多線程下,一個線程進(jìn)入了 if(singleton ==nul)判斷語句塊,還未來得及往下執(zhí)行,另一個線程也通過了這個判斷語句,這時便會產(chǎn)生多個實(shí)例。所以在多線程環(huán)境下不可使用這種方式
3)在實(shí)際開發(fā)中,不要使用這種方式
(4)懶漢式(線程安全,同步方法)
public class Singleton {//1.構(gòu)造器私有化,外部能 newprivate Singleton(){}//2.本類內(nèi)部創(chuàng)建實(shí)例對象private static Singleton instance;//3. 提供一個靜態(tài)公有方法,加synchronized關(guān)鍵詞保證線安全,當(dāng)使用到該方法時,才去創(chuàng)建instancepublic static synchronized Singleton getInstance (){if (instance == null) {instance = new Singleton();}return instance;}}
說明:
1)解決了線程安全問題
2)效率太低了,每個線程在想獲得類的實(shí)例時候,執(zhí)行 getinstance()方法都要進(jìn)行同步。而其實(shí)這個方法只執(zhí)行一次實(shí)例化代碼就夠了,后面的想獲得該類實(shí)例,直接retumn 就行了。方法進(jìn)行同步效率太低
3)在實(shí)際開發(fā)中,不推薦使用這種方式
(5)懶漢式(線程安全,同步代碼塊)
public class Singleton {//1.構(gòu)造器私有化,外部能 newprivate Singleton(){}//2.本類內(nèi)部創(chuàng)建實(shí)例對象private static Singleton instance;//3. 提供一個靜態(tài)公有方法,當(dāng)使用到該方法時,才去創(chuàng)建instancepublic static Singleton getInstance (){if (instance == null) {// 添加同步代碼塊synchronized (Singleton.class) {instance = new Singleton();}}return instance;}}
不推薦使用
(6)雙重檢查
public class Singleton {//1.構(gòu)造器私有化,外部能 newprivate Singleton(){}//2.本類內(nèi)部創(chuàng)建實(shí)例對象private static volatile Singleton instance;//3.提供一個靜態(tài)的公有方法,加入雙重檢査代碼,解決線程安全問題,同時解決懶加載問題,同同時保證了效率,推薦使用public static Singleton getInstance (){if (instance == null) {// 添加同步代碼塊synchronized (Singleton.class) {if (instance == null) {instance = new Singleton();}}}return instance;}}
說明:
1)Double-Check概念是多線程開發(fā)中常使用到的,如代碼中所示,我們進(jìn)行了兩次 if(singleton == nul)檢查,這樣就可以保證線程安全了。
2)實(shí)例化代碼只用執(zhí)行一次,后面再次訪問時,判斷if(singleton= null),直接retum 實(shí)例化對象,也避免反復(fù)進(jìn)行方法同步
3)線程安全;延遲加載;效率較高
4)在實(shí)際開發(fā)中推薦使用這種單例設(shè)計模式
(7)靜態(tài)內(nèi)部類
public class Singleton {//1.構(gòu)造器私有化,外部能 newprivate Singleton(){}//2.靜態(tài)內(nèi)部類private static class SingletonInstance {private static final Singleton INSTANCE = new Singleton();}//3.提供一個靜態(tài)的公有方法,直接返回INSTANCE,推薦使用public static Singleton getInstance (){return SingletonInstance.INSTANCE;}}
說明:
1)這種方式采用了類裝載的機(jī)制來保證初始化實(shí)例時只有一個線程。
2)靜態(tài)內(nèi)部類方式在 Singleton 類被裝載時并不會立即實(shí)例化,而是在需要實(shí)例化時,調(diào)用 getnstance 方法,才會裝載 SingletonInstance 類,從而完成 Singleton 的實(shí)例化。
3)類的靜態(tài)屬性只會在第一次加載類的時候初始化,所以在這里,JVM 幫助我們保證了線程的安全性,在類進(jìn)行初始化時,別的線程是無法進(jìn)入的。
4)避免了線程不安全,利用靜態(tài)內(nèi)部類特點(diǎn)實(shí)現(xiàn)延遲加載,效率高
5)推薦使用
(8)枚舉
// 枚舉形式
public enum Singleton2 {INSTANCE;
}public static void main(String[] args) {Singleton2 instance = Singleton2.INSTANCE;Singleton2 instance2 = Singleton2.INSTANCE;System.out.println(instance == instance2);System.out.println(instance.hashCode());System.out.println(instance2.hashCode());
}
說明:
1)這借助JDK15 中添加的枚舉來實(shí)現(xiàn)單例模式。不僅能避免多線程同步問題,而且還能防止反序列化重新創(chuàng)建1)新的對象。
2)這種方式是 Effective Java 作者 Josh Bloch 提倡的方式
3)推薦使用
3、單例模式在jdk中的使用
java.lang.RunTime就是經(jīng)典的單例惡漢模式
4、注意事項
1)單例模式保證了 系統(tǒng)內(nèi)存中該類只存在一個對象,節(jié)省了系統(tǒng)資源,對于一些需要頻繁創(chuàng)建銷毀的對象,使用單例模式可以提高系統(tǒng)性能
2)當(dāng)想實(shí)例化一個單例類的時候,必須要記住使用相應(yīng)的獲取對象的方法,而不是使用 new
3)單例模式使用的場景:
? 需要頻繁的進(jìn)行創(chuàng)建和銷毀的對象、創(chuàng)建對象時耗時過多或耗費(fèi)資源過多(即:重量級對象),但又經(jīng)常用到的對象、工具類對象、頻繁訪問數(shù)據(jù)庫或文件的對象(比如數(shù)據(jù)源、session 工廠等)
四、工廠模式
1、分類
- 簡單工廠模式
- 工廠方法模式
- 抽象工廠模式
2、簡單工廠模式
(1)介紹
1)簡單工廠模式是屬于創(chuàng)建型模式,是工廠模式的一種。簡單工廠模式是由一個工廠對象決定創(chuàng)建出哪一種產(chǎn)品類的實(shí)例。簡單工廠模式是工廠模式家族中最簡單實(shí)用的模式
2)簡單工廠模式定義了一個創(chuàng)建對象的類,由這個類來封裝實(shí)例化對象的行為(代碼)
3)在軟件開發(fā)中,當(dāng)我們會用到大量的創(chuàng)建某種類或者某批對象時,就會使用到工廠模式
(2)案例
披薩的項目:要便于披薩種類的擴(kuò)展,要便于維護(hù)
- 披薩的種類很多(比如 GreekPizz、CheesePizz 等)
- 披薩的制作有 prepare,bake,cut, box
- 完成披薩店訂購功能。
1、創(chuàng)建一個pizza類
//將Pizza 類做成抽象
public abstract class Pizza {protected String name; //名字//準(zhǔn)備原材料, 不同的披薩不一樣,因此,我們做成抽象方法public abstract void prepare();public void bake() {System.out.println(name + " baking;");}public void cut() {System.out.println(name + " cutting;");}//打包public void box() {System.out.println(name + " boxing;");}public void setName(String name) {this.name = name;}
}
2、希臘披薩類
public class GreekPizza extends Pizza {@Overridepublic void prepare() {// TODO Auto-generated method stubSystem.out.println("給【希臘披薩】準(zhǔn)備原材料");}}
3、奶酪pizza
public class CheesePizza extends Pizza {@Overridepublic void prepare() {// TODO Auto-generated method stubSystem.out.println(" 給制作奶酪披薩 準(zhǔn)備原材料 ");}}
4、簡單工廠類
public class SimpleFactory {//根據(jù)orderType 返回對應(yīng)的Pizza 對象public Pizza createPizza(String orderType) {Pizza pizza = null;System.out.println("使用簡單工廠模式");if (orderType.equals("greek")){pizza = new GreekPizza();pizza.setName(" 希臘披薩 ");} else if (orderType.equals("cheese")){pizza = new CheesePizza();pizza.setName(" 奶酪披薩 ");}return pizza;}//簡單工廠模式 也叫 靜態(tài)工廠模式public static Pizza createPizza2(String orderType) {Pizza pizza = null;System.out.println("使用簡單工廠模式2");if (orderType.equals("greek")) {pizza = new GreekPizza();pizza.setName(" 希臘披薩 ");} else if (orderType.equals("cheese")) {pizza = new CheesePizza();pizza.setName(" 奶酪披薩 ");}return pizza;}
}
5、訂購pizza
public class OrderPizza {//定義一個簡單工廠對象SimpleFactory simpleFactory;Pizza pizza;String orderType;//構(gòu)造器public OrderPizza(SimpleFactory simpleFactory, String orderType){this.simpleFactory = simpleFactory;this.orderType = orderType;setFactory();}private void setFactory() {// 根據(jù)用戶輸入的類型獲取隊形的pizza對象pizza = simpleFactory.createPizza(orderType);//輸出pizzaif(pizza != null) { //訂購成功pizza.prepare();pizza.bake();pizza.cut();pizza.box();} else {System.out.println(" 訂購披薩失敗 ");}}}
6、測試
public static void main(String[] args) {//使用簡單工廠模式new OrderPizza(new SimpleFactory(), "greek");}
輸出:使用簡單工廠模式
給【希臘披薩】準(zhǔn)備原材料希臘披薩 baking;希臘披薩 cutting;希臘披薩 boxing;
如果之后需要增加pizza類型,只需要在工廠中追加即可
3、工廠方法模式
(1)介紹
工廠方法模式:定義了一個創(chuàng)建對象的抽象方法,由子類決定要實(shí)例化的類。工廠方法模式將對象的實(shí)例化推遲到子類。
(2)案例
披薩項目新的需求:客戶在點(diǎn)披薩時,可以點(diǎn)不同口味的披薩,比如 北京的奶酪 pizza、北京的胡椒 pizza 或者是倫敦的奶酪 pizza、倫敦的胡椒 pizza
1、將Pizza 類做成抽象
//將Pizza 類做成抽象
public abstract class Pizza {protected String name; //名字//準(zhǔn)備原材料, 不同的披薩不一樣,因此,我們做成抽象方法public abstract void prepare();public void bake() {System.out.println(name + " baking;");}public void cut() {System.out.println(name + " cutting;");}//打包public void box() {System.out.println(name + " boxing;");}public void setName(String name) {this.name = name;}
}
2、北京奶酪pizza
public class BJCheesePizza extends Pizza {@Overridepublic void prepare() {setName("北京的奶酪pizza");System.out.println(" 北京的奶酪pizza 準(zhǔn)備原材料");}}
3、北京胡椒pizza
public class BJPepperPizza extends Pizza {@Overridepublic void prepare() {// TODO Auto-generated method stubsetName("北京的胡椒pizza");System.out.println(" 北京的胡椒pizza 準(zhǔn)備原材料");}
}
4、倫敦奶酪pizza
public class LDCheesePizza extends Pizza{@Overridepublic void prepare() {// TODO Auto-generated method stubsetName("倫敦的奶酪pizza");System.out.println(" 倫敦的奶酪pizza 準(zhǔn)備原材料");}
}
5、倫敦胡椒pizza
public class LDPepperPizza extends Pizza{@Overridepublic void prepare() {// TODO Auto-generated method stubsetName("倫敦的胡椒pizza");System.out.println(" 倫敦的胡椒pizza 準(zhǔn)備原材料");}
}
6、訂購pizza抽象類
public abstract class OrderPizza {//定義一個抽象方法,createPizza , 讓各個工廠子類自己實(shí)現(xiàn)abstract Pizza createPizza(String orderType);// 構(gòu)造器public OrderPizza(String orderType) {Pizza pizza = createPizza(orderType); //抽象方法,由工廠子類完成//輸出pizza 制作過程pizza.prepare();pizza.bake();pizza.cut();pizza.box();}}
7、訂購北京pizza
public class BJOrderPizza extends OrderPizza {public BJOrderPizza(String orderType) {super(orderType);}@OverridePizza createPizza(String orderType) {Pizza pizza = null;if(orderType.equals("cheese")) {pizza = new BJCheesePizza();} else if (orderType.equals("pepper")) {pizza = new BJPepperPizza();}// TODO Auto-generated method stubreturn pizza;}}
8、訂購倫敦pizza
public class LDOrderPizza extends OrderPizza {public LDOrderPizza(String orderType) {super(orderType);}@OverridePizza createPizza(String orderType) {Pizza pizza = null;if(orderType.equals("cheese")) {pizza = new LDCheesePizza();} else if (orderType.equals("pepper")) {pizza = new LDPepperPizza();}// TODO Auto-generated method stubreturn pizza;}}
9、測試
public static void main(String[] args) {new LDOrderPizza("cheese");}
// ==================================================倫敦的奶酪pizza 準(zhǔn)備原材料
倫敦的奶酪pizza baking;
倫敦的奶酪pizza cutting;
倫敦的奶酪pizza boxing;
說明:
? 如果后期要加一個北京西紅柿pizza,只需要先實(shí)現(xiàn)Pizza抽象類,可以寫具體的實(shí)現(xiàn)過程,再在BJOrderPizza類中加一個判斷條件。
4、抽象工廠模式
(1)介紹
1)抽象工廠模式:定義了一個interface 用于創(chuàng)建相關(guān)或有依賴關(guān)系的對象簇,而無需指明具體的類
2)抽象工廠模式可以將簡單工廠模式和工廠方法模式進(jìn)行整合。
3)從設(shè)計層面看,抽象工廠模式就是對簡單工廠模式的改進(jìn)(或者稱為進(jìn)一步的抽象)。
4)將工廠抽象成兩層,AbsFactory(抽象工廠)和 具體實(shí)現(xiàn)的工廠子類。根據(jù)創(chuàng)建對象類型使用對應(yīng)的工廠子類。這樣將單個的簡單工廠類變成了工廠簇,更利于代碼的維護(hù)和擴(kuò)展。
(2)案例
12345使用工廠方法模式代碼
6、抽象工廠模式抽象層
//一個抽象工廠模式的抽象層(接口)
public interface AbsFactory {//讓下面的工廠子類來 具體實(shí)現(xiàn)public Pizza createPizza(String orderType);
}
7、北京工廠類
//這是工廠子類
public class BJFactory implements AbsFactory {public Pizza createPizza(String orderType) {System.out.println("~使用的是抽象工廠模式~");// TODO Auto-generated method stubPizza pizza = null;if(orderType.equals("cheese")) {pizza = new BJCheesePizza();} else if (orderType.equals("pepper")){pizza = new BJPepperPizza();}return pizza;}
}
8、倫敦工廠類
public class LDFactory implements AbsFactory {public Pizza createPizza(String orderType) {System.out.println("~使用的是抽象工廠模式~");Pizza pizza = null;if (orderType.equals("cheese")) {pizza = new LDCheesePizza();} else if (orderType.equals("pepper")) {pizza = new LDPepperPizza();}return pizza;}}
9、訂購pizza類
public class OrderPizza {AbsFactory factory;// 構(gòu)造器public OrderPizza(AbsFactory factory, String orderType) {setFactory(factory,orderType);}private void setFactory(AbsFactory factory, String orderType) {Pizza pizza = null;this.factory = factory;// factory 可能是北京的工廠子類,也可能是倫敦的工廠子類pizza = factory.createPizza(orderType);if (pizza != null) { // 訂購okpizza.prepare();pizza.bake();pizza.cut();pizza.box();} else {System.out.println("訂購失敗");}}}
10、測試
public static void main(String[] args) {new OrderPizza(new BJFactory(), "cheese");}
// ===========================================================
~使用的是抽象工廠模式~北京的奶酪pizza 準(zhǔn)備原材料
北京的奶酪pizza baking;
北京的奶酪pizza cutting;
北京的奶酪pizza boxing;
5、總結(jié)
1)將實(shí)例化對象的代碼提取出來,放到一個類中統(tǒng)一管理和維護(hù),達(dá)到和主項目的依賴關(guān)系的解耦。從而提高項目的擴(kuò)展和維護(hù)性。
2)創(chuàng)建對象實(shí)例時,不要直接 new 類,而是把這個 new 類的動作放在一個工廠的方法中,并返回。
3)不要讓類繼承具體類,而是繼承抽象類或者是實(shí)現(xiàn)interface(接口)不要覆蓋基類中已經(jīng)實(shí)現(xiàn)的方法。
五、原型模式
1、介紹
1)原型模式(Prototype 模式)是指:用原型實(shí)例指定創(chuàng)建對象的種類,并且通過拷貝這些原型,創(chuàng)建新的對象
2)原型模式是一種創(chuàng)建型設(shè)計模式,允許一個對象再創(chuàng)建另外一個可定制的對象,無需知道如何創(chuàng)建的細(xì)節(jié)
3)工作原理是:將一個原型對象傳給那個要發(fā)動創(chuàng)建的對象,這個要發(fā)動創(chuàng)建的對象通過請求原型對象拷貝它們自己來實(shí)施創(chuàng)建,即 對象.clone()
2、案例
基礎(chǔ)類
public class Sheep implements Cloneable {private String name;private int age;private String color;public Sheep friend; //是對象, 克隆是會如何處理public Sheep(String name, int age, String color) {super();this.name = name;this.age = age;this.color = color;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public String getColor() {return color;}public void setColor(String color) {this.color = color;}@Overridepublic String toString() {return "Sheep [name=" + name + ", age=" + age + ", color=" + color + "]";}//克隆該實(shí)例,使用默認(rèn)的clone方法來完成@Overrideprotected Object clone() {Sheep sheep = null;try {sheep = (Sheep)super.clone();} catch (Exception e) {// TODO: handle exceptionSystem.out.println(e.getMessage());}// TODO Auto-generated method stubreturn sheep;}}
測試類
public static void main(String[] args) {Sheep sheep = new Sheep("白羊", 5, "白色");sheep.friend = new Sheep("jack", 2, "黑色");Sheep sheep1 = (Sheep) sheep.clone();Sheep sheep2 = (Sheep) sheep.clone();Sheep sheep3 = (Sheep) sheep.clone();System.out.println(sheep1 + " friend-hash: " + sheep1.friend.hashCode());System.out.println(sheep2 + " friend-hash: " + sheep2.friend.hashCode());System.out.println(sheep3 + " friend-hash: " + sheep3.friend.hashCode());}
輸出:
Sheep [name=白羊, age=5, color=白色] friend-hash: 460141958
Sheep [name=白羊, age=5, color=白色] friend-hash: 460141958
Sheep [name=白羊, age=5, color=白色] friend-hash: 460141958
發(fā)現(xiàn)在克隆sheep時,應(yīng)用類型的friend只是做了淺拷貝
3、淺拷貝介紹
1)對于數(shù)據(jù)類型是基本數(shù)據(jù)類型的成員變量,淺拷貝會直接進(jìn)行值傳遞,也就是將該屬性值復(fù)制一份給新的對象。
2)對于數(shù)據(jù)類型是引用數(shù)據(jù)類型的成員變量,比如說成員變量是某個數(shù)組、某個類的對象等,那么淺拷貝會進(jìn)行引用傳遞,也就是將該成員變量的引用值(內(nèi)存地址)復(fù)制一份給新的對象。因?yàn)閷?shí)際上兩個對象的該成員變量都指向同一個實(shí)例。在這種情況下,在一個對象中修改該成員變量會影響到另一個對象的該成員變量值;在上述案例中就是淺拷貝。
3)淺拷貝是使用默認(rèn)的 clone()方法來實(shí)現(xiàn)
? sheep=(Sheep) super.clone():
4、深拷貝介紹
1)復(fù)制對象的所有基本數(shù)據(jù)類型的成員變量值
2)為所有引用數(shù)據(jù)類型的成員變量申請存儲空間,并復(fù)制每個引用數(shù)據(jù)類型成員變量所引用的對象,直到該對象可達(dá)的所有對象。也就是說,對象進(jìn)行深拷貝要對整個對象(包括對象的引用類型)進(jìn)行拷貝
3)深拷貝實(shí)現(xiàn)方式 :重寫clone方法來實(shí)現(xiàn)深拷貝
4)深拷貝實(shí)現(xiàn)方式:通過對象序列化實(shí)現(xiàn)深拷貝(推薦)
5) 案例
基本類型
public class DeepCloneableTarget implements Serializable, Cloneable {private static final long serialVersionUID = 1L;private String cloneName;private String cloneClass;//構(gòu)造器public DeepCloneableTarget(String cloneName, String cloneClass) {this.cloneName = cloneName;this.cloneClass = cloneClass;}//因?yàn)樵擃惖膶傩?#xff0c;都是String , 因此我們這里使用默認(rèn)的clone完成即可@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}
}
引用類型
public class DeepProtoType implements Serializable, Cloneable{public String name; //String 屬性public DeepCloneableTarget deepCloneableTarget;// 引用類型public DeepProtoType() {super();}//深拷貝 - 方式 1 使用clone 方法// 缺陷:如果引用類型屬性的內(nèi)部屬性還是應(yīng)用類型。則其內(nèi)部的應(yīng)用類型也要重寫clone@Overrideprotected Object clone() throws CloneNotSupportedException {Object deep = null;//這里完成對基本數(shù)據(jù)類型(屬性)和String的克隆deep = super.clone(); //對引用類型的屬性,進(jìn)行單獨(dú)處理DeepProtoType deepProtoType = (DeepProtoType)deep;deepProtoType.deepCloneableTarget = (DeepCloneableTarget)deepCloneableTarget.clone();// TODO Auto-generated method stubreturn deepProtoType;}//深拷貝 - 方式2 通過對象的序列化實(shí)現(xiàn) (推薦)public Object deepClone() {//創(chuàng)建流對象ByteArrayOutputStream bos = null;ObjectOutputStream oos = null;ByteArrayInputStream bis = null;ObjectInputStream ois = null;try {//序列化bos = new ByteArrayOutputStream();oos = new ObjectOutputStream(bos);oos.writeObject(this); //當(dāng)前這個對象以對象流的方式輸出//反序列化bis = new ByteArrayInputStream(bos.toByteArray());ois = new ObjectInputStream(bis);DeepProtoType copyObj = (DeepProtoType)ois.readObject();return copyObj;} catch (Exception e) {// TODO: handle exceptione.printStackTrace();return null;} finally {//關(guān)閉流try {bos.close();oos.close();bis.close();ois.close();} catch (Exception e2) {// TODO: handle exceptionSystem.out.println(e2.getMessage());}}}}
測試
public static void main(String[] args) {// TODO Auto-generated method stubDeepProtoType p = new DeepProtoType();p.name = "李四";p.deepCloneableTarget = new DeepCloneableTarget("大牛", "小牛");//方式2 完成深拷貝DeepProtoType p2 = (DeepProtoType) p.deepClone();System.out.println("p.name=" + p.name + " p.deepCloneableTarget=" + p.deepCloneableTarget.hashCode());System.out.println("p2.name=" + p.name + " p2.deepCloneableTarget=" + p2.deepCloneableTarget.hashCode());}
結(jié)果
p.name=李四 p.deepCloneableTarget=1836019240
p2.name=李四 p2.deepCloneableTarget=363771819
5、總結(jié)
1)創(chuàng)建新的對象比較復(fù)雜時,可以利用原型模式簡化對象的創(chuàng)建過程,同時也能夠提高效率
2)不用重新初始化對象,而是動態(tài)地獲得對象運(yùn)行時的狀態(tài)
3)如果原始對象發(fā)生變化(增加或者減少屬性),其它克隆對象的也會發(fā)生相應(yīng)的變化,無需修改代碼
4)在實(shí)現(xiàn)深克隆的時候可能需要比較復(fù)雜的代碼
5)缺點(diǎn):需要為每一個類配備一個克隆方法,這對全新的類來說不是很難,但對已有的類進(jìn)行改造時,需要修改其源代碼,違背了 ocp 原則。
六、建造者模式
1、介紹
1)建造者模式(Builder Pattern) 又叫生成器模式,是一種對象構(gòu)建模式。它可以將復(fù)雜對象的建造過程抽象出來(抽象類別),使這個抽象過程的不同實(shí)現(xiàn)方法可以構(gòu)造出不同表現(xiàn)(屬性)的對象。
2)建造者模式 是一步一步創(chuàng)建一個復(fù)雜的對象,它允許用戶只通過指定復(fù)雜對象的類型和內(nèi)容就可以構(gòu)建它們,用戶不需要知道內(nèi)部的具體構(gòu)建細(xì)節(jié)。
2、四個角色
1)Product(產(chǎn)品角色):一個具體的產(chǎn)品對象。
2)Builder(抽象建造者):創(chuàng)建一個 Product 對象的各個部件指定的 接口/抽象類。
3)ConcreteBuilder(具體建造者):實(shí)現(xiàn)接口,構(gòu)建和裝配各個部件。
4)Director(指揮者):構(gòu)建一個使用 Builder 接口的對象。它主要是用于創(chuàng)建一個復(fù)雜的對象。它主要有兩個作用,一是隔離了客戶與對象的生產(chǎn)過程,二是負(fù)責(zé)控制產(chǎn)品對象的生產(chǎn)過程。
3、案例
建房子需要打樁、砌墻、封頂?shù)?#xff0c;不管普通房、別墅都需要這個過程。
1、House類
//產(chǎn)品->Product
public class House {private String baise;private String wall;private String roofed;public String getBaise() {return baise;}public void setBaise(String baise) {this.baise = baise;}public String getWall() {return wall;}public void setWall(String wall) {this.wall = wall;}public String getRoofed() {return roofed;}public void setRoofed(String roofed) {this.roofed = roofed;}}
2、HouseBuilder抽象類
// 抽象的建造者
public abstract class HouseBuilder {protected House house = new House();//將建造的流程寫好, 抽象的方法public abstract void buildBasic();public abstract void buildWalls();public abstract void roofed();//建造房子好, 將產(chǎn)品(房子) 返回public House buildHouse() {return house;}}
3、CommonHouse類
public class CommonHouse extends HouseBuilder {@Overridepublic void buildBasic() {// TODO Auto-generated method stubSystem.out.println(" 普通房子打地基5米 ");}@Overridepublic void buildWalls() {// TODO Auto-generated method stubSystem.out.println(" 普通房子砌墻10cm ");}@Overridepublic void roofed() {// TODO Auto-generated method stubSystem.out.println(" 普通房子屋頂 ");}}
4、HighBuilding
public class HighBuilding extends HouseBuilder {@Overridepublic void buildBasic() {// TODO Auto-generated method stubSystem.out.println(" 高樓的打地基100米 ");}@Overridepublic void buildWalls() {// TODO Auto-generated method stubSystem.out.println(" 高樓的砌墻20cm ");}@Overridepublic void roofed() {// TODO Auto-generated method stubSystem.out.println(" 高樓的透明屋頂 ");}}
5、HouseDirector 類
public class HouseDirector {HouseBuilder houseBuilder = null;//構(gòu)造器傳入 houseBuilderpublic HouseDirector(HouseBuilder houseBuilder) {this.houseBuilder = houseBuilder;}//通過setter 傳入 houseBuilderpublic void setHouseBuilder(HouseBuilder houseBuilder) {this.houseBuilder = houseBuilder;}//如何處理建造房子的流程,交給指揮者public House constructHouse() {houseBuilder.buildBasic();houseBuilder.buildWalls();houseBuilder.roofed();return houseBuilder.buildHouse();}}
7、測試
public static void main(String[] args) {//蓋普通房子CommonHouse commonHouse = new CommonHouse();//準(zhǔn)備創(chuàng)建房子的指揮者HouseDirector houseDirector = new HouseDirector(commonHouse);//完成蓋房子,返回產(chǎn)品(普通房子)House house = houseDirector.constructHouse();System.out.println("--------------------------");//蓋高樓HighBuilding highBuilding = new HighBuilding();//重置建造者h(yuǎn)ouseDirector.setHouseBuilder(highBuilding);//完成蓋房子,返回產(chǎn)品(高樓)houseDirector.constructHouse();}
輸出:
普通房子打地基5米
普通房子砌墻10cm
普通房子屋頂
高樓的打地基100米
高樓的砌墻20cm
高樓的透明屋頂
4、總結(jié)
1)客戶端(使用程序)不必知道產(chǎn)品內(nèi)部組成的細(xì)節(jié),將產(chǎn)品本身與產(chǎn)品的創(chuàng)建過程解耦,使得相同的創(chuàng)建過程可以創(chuàng)建不同的產(chǎn)品對象
2)每一個具體建造者都相對獨(dú)立,而與其他的具體建造者無關(guān),因此可以很方便地替換具體建造者或增加新的具體建造者,用戶使用不同的具體建造者即可得到不同的產(chǎn)品對象
3)可以更加精細(xì)地控制產(chǎn)品的創(chuàng)建過程 。將復(fù)雜產(chǎn)品的創(chuàng)建步驟分解在不同的方法中,使得創(chuàng)建過程更加清晰,
也更方便使用程序來控制創(chuàng)建過程
4)增加新的具體建造者無須修改原有類庫的代碼,指揮者類針對抽象建造者類編程,系統(tǒng)擴(kuò)展方便,符合“開閉原則”
5)建造者模式所創(chuàng)建的產(chǎn)品一般具有較多的共同點(diǎn),其組成部分相似,如果產(chǎn)品之間的差異性很大,則不適合使用建造者模式,因此其使用范圍受到一定的限制。
6)如果產(chǎn)品的內(nèi)部變化復(fù)雜,可能會導(dǎo)致需要定義很多具體建造者類來實(shí)現(xiàn)這種變化,導(dǎo)致系統(tǒng)變得很龐大,因此在這種情況下,要考慮是否選擇建造者模式。
7)抽象工廠模式 VS 建造者模式
抽象工廠模式實(shí)現(xiàn)對產(chǎn)品家族的創(chuàng)建,一個產(chǎn)品家族是這樣的一系列產(chǎn)品:具有不同分類維度的產(chǎn)品組合,采用抽象工廠模式不需要關(guān)心構(gòu)建過程,只關(guān)心什么產(chǎn)品由什么工廠生產(chǎn)即可。而建造者模式則是要求按照指定的藍(lán)圖建造產(chǎn)品,它的主要目的是通過組裝零配件而產(chǎn)生一個新產(chǎn)品。
七、適配器模式
1、介紹
1)適配器模式(Adapter Pattern)將某個類的接口轉(zhuǎn)換成客戶端期望的另一個接口表示,主的目的是兼容性,讓原本因接口不匹配不能一起工作的兩個類可以協(xié)同工作。其別名為包裝器(Wrapper)
2)適配器模式屬于結(jié)構(gòu)型模式
3)主要分為三類:類適配器模式、對象適配器模式、接口適配器模式
2、類適配器模式
(1)介紹
? Adapter 類,通過繼承 src 類,實(shí)現(xiàn) dst 類接口,完成 src->dst 的適配
(2)案例
以生活中充電器的例子來講解適配器,充電器本身相當(dāng)于 Adapter,220V 交流電相當(dāng)于 src (即被適配者),目的dst(即 目標(biāo))是 5V 直流電
1、適配器接口
//適配接口
public interface IVoltage5V {public int output5V();
}
2、被適配的類
//被適配的類
public class Voltage220V {//輸出220V的電壓,不變public int output220V() {int src = 220;System.out.println("電壓=" + src + "伏");return src;}
}
3、適配器類
//適配器類
public class VoltageAdapter extends Voltage220V implements IVoltage5V {@Overridepublic int output5V() {int srcV = output220V();//獲取220V 電壓int dst = srcV / 44; //轉(zhuǎn)成 5vreturn dst;}}
4、使用者
public class Phone {//充電public void charging(IVoltage5V iVoltage5V) {if(iVoltage5V.output5V() == 5) {System.out.println("電壓為5V, 可以充電~~");} else if (iVoltage5V.output5V() > 5) {System.out.println("電壓大于5V, 不能充電~~");}}
}
5、測試
public static void main(String[] args) {Phone phone = new Phone();phone.charging(new VoltageAdapter());}
電壓=220伏
電壓為5V, 可以充電~~
(3)總結(jié)
1)Java 是單繼承機(jī)制,所以類適配器需要繼承 src 類這一點(diǎn)算是一個缺點(diǎn),因?yàn)檫@要求 dst 必須是接口,有一定局限性;
2)src 類的方法在 Adapter 中都會暴露出來,也增加了使用的成本。
3)由于其繼承了 src 類,所以它可以根據(jù)需求重寫 src 類的方法,使得 Adapter 的靈活性增強(qiáng)了。
3、對象適配器模式
(1)介紹
1)基本思路和類的適配器模式相同,只是將 Adapter 類作修改,不是繼承 src 類,而是持有 src 類的實(shí)例,以解決兼容性的問題。 即:持有 src類,實(shí)現(xiàn) dst 類接口,完成 src->dst 的適配
2)根據(jù)“合成復(fù)用原則”,在系統(tǒng)中盡量使用關(guān)聯(lián)關(guān)系(聚合)來替代繼承關(guān)系。
3)對象適配器模式是適配器模式常用的一種
(2)案例
3、修改類適配器第三步改為對象適配
//適配器類
public class VoltageAdapter implements IVoltage5V {private Voltage220V voltage220V; // 關(guān)聯(lián)關(guān)系-聚合//通過構(gòu)造器,傳入一個 Voltage220V 實(shí)例public VoltageAdapter(Voltage220V voltage220v) {this.voltage220V = voltage220v;}@Overridepublic int output5V() {int dst = 0;if(null != voltage220V) {int src = voltage220V.output220V();//獲取220V 電壓System.out.println("使用對象適配器,進(jìn)行適配~~");dst = src / 44;System.out.println("適配完成,輸出的電壓為=" + dst);}return dst;}}
測試
public static void main(String[] args) {Phone phone = new Phone();phone.charging(new VoltageAdapter(new Voltage220V()));}
電壓=220伏
使用對象適配器,進(jìn)行適配~~
適配完成,輸出的電壓為=5
電壓為5V, 可以充電~~
(3)總結(jié)
1)對象適配器和類適配器其實(shí)算是同一種思想,只不過實(shí)現(xiàn)方式不同。根據(jù)合成復(fù)用原則,使用組合替代繼承,所以它解決了類適配器必須繼承 src 的局限性問題,也不再要求 dst必須是接口。
2)使用成本更低,更靈活。
4、接口適配器模式
(1)介紹
1)核心思路:當(dāng)不需要全部實(shí)現(xiàn)接口提供的方法時,可先設(shè)計一個抽象類實(shí)現(xiàn)接口,并為該接口中每個方法提供一個默認(rèn)實(shí)現(xiàn)(空方法),那么該抽象類的子類可有選擇地覆蓋父類的某些方法來實(shí)現(xiàn)需求。
2)適用于一個接口不想使用其所有的方法的情況。
(2)案例
1、接口
public interface Interface4 {public void m1();public void m2();public void m3();public void m4();
}
2、抽象類
//在AbsAdapter 我們將 Interface4 的方法進(jìn)行默認(rèn)實(shí)現(xiàn)
public abstract class AbsAdapter implements Interface4 {//默認(rèn)實(shí)現(xiàn)public void m1() {}public void m2() {}public void m3() {}public void m4() {}
}
3、測試
public static void main(String[] args) {AbsAdapter absAdapter = new AbsAdapter() {//只需要去覆蓋我們 需要使用 接口方法@Overridepublic void m1() {System.out.println("使用了m1的方法");}};absAdapter.m1();}
使用了m1的方法
(3)總結(jié)
1)三種命名方式,是根據(jù) src 是以怎樣的形式給到 Adapter(在 Adapter 里的形式)來命名的。
2)類適配器:以類給到,在 Adapter 里,就是將 src當(dāng)做類,繼承
? 對象適配器:以對象給到,在 Adapter里,將src 作為一個對象,持有
? 接口適配器:以接口給到,在Adapter 里,將src 作為一個接口,實(shí)現(xiàn)
3)Adapter 模式最大的作用還是將原本不兼容的接口融合在一起工作。
八、橋接模式
1、介紹
1)橋接模式(Bridge 模式)是指:將實(shí)現(xiàn)與抽象放在兩個不同的類層次中,使兩個層次可以獨(dú)立改變。是一種結(jié)構(gòu)型設(shè)計模式。
2)Bridge 模式基于類的最小設(shè)計原則,通過使用封裝、聚合及繼承等行為讓不同的類承擔(dān)不同的職責(zé)。它的主要特點(diǎn)是把抽象(Abstaction)與行為實(shí)現(xiàn)(Implementation)分離開來,從而可以保持各部分的獨(dú)立性以及應(yīng)對他們的功能擴(kuò)展。
2、原理說明
說明:
1)Client類:橋接模式的調(diào)用者
2)抽象類(Abstraction):維護(hù)了 Implementor/即它的實(shí)現(xiàn)類 ConcretelmplementorA…
二者是聚合關(guān)系,Abstraction充當(dāng)橋接類
3)RefinedAbstraction:是 Abstraction 抽象類的子類
4)Implementor:行為實(shí)現(xiàn)類的接口
5)ConcretelmplementorA/B :行為的具體實(shí)現(xiàn)類
6)從 UM 圖:這里的抽象類和接口是聚合的關(guān)系,其實(shí)調(diào)用和被調(diào)用關(guān)系
3、案例
手機(jī)打電話
傳統(tǒng)類圖:
? 擴(kuò)展性問題(類爆炸),如果我們再增加手機(jī)的樣式(旋轉(zhuǎn)式),就需要增加各個品牌手機(jī)的類,同樣如果我們增加一個手機(jī)品牌,也要在各個手機(jī)樣式類下增加。違反了單一職責(zé)原則,這樣增加了代碼維護(hù)成本
改進(jìn)類圖:
1、接口Brand
//接口
public interface Brand {void open();void close();void call();
}
2、vivo手機(jī)
public class Vivo implements Brand {@Overridepublic void open() {System.out.println(" Vivo手機(jī)開機(jī) ");}@Overridepublic void close() {System.out.println(" Vivo手機(jī)關(guān)機(jī) ");}@Overridepublic void call() {System.out.println(" Vivo手機(jī)打電話 ");}}
3、小米手機(jī)
public class XiaoMi implements Brand {@Overridepublic void open() {System.out.println(" 小米手機(jī)開機(jī) ");}@Overridepublic void close() {System.out.println(" 小米手機(jī)關(guān)機(jī) ");}@Overridepublic void call() {System.out.println(" 小米手機(jī)打電話 ");}}
4、手機(jī)抽象類
public abstract class Phone {//組合品牌private Brand brand;//構(gòu)造器public Phone(Brand brand) {super();this.brand = brand;}protected void open() {brand.open();}protected void close() {brand.close();}protected void call() {brand.call();}}
5、折疊手機(jī)
//折疊式手機(jī)類,繼承 抽象類 Phone
public class FoldedPhone extends Phone {//構(gòu)造器public FoldedPhone(Brand brand) {super(brand);}public void open() {super.open();System.out.println(" 折疊樣式手機(jī) ");}public void close() {super.close();System.out.println(" 折疊樣式手機(jī) ");}public void call() {super.call();System.out.println(" 折疊樣式手機(jī) ");}
}
6、直立手機(jī)
public class UpRightPhone extends Phone {//構(gòu)造器public UpRightPhone(Brand brand) {super(brand);}public void open() {super.open();System.out.println(" 直立樣式手機(jī) ");}public void close() {super.close();System.out.println(" 直立樣式手機(jī) ");}public void call() {super.call();System.out.println(" 直立樣式手機(jī) ");}
}
7、測試
public static void main(String[] args) {//獲取折疊式手機(jī) (樣式 + 品牌 )Phone phone1 = new FoldedPhone(new XiaoMi());phone1.open();phone1.call();phone1.close();System.out.println("=======================");Phone phone2 = new FoldedPhone(new Vivo());phone2.open();phone2.call();phone2.close();}
小米手機(jī)開機(jī)
折疊樣式手機(jī)
小米手機(jī)打電話
折疊樣式手機(jī)
小米手機(jī)關(guān)機(jī)
折疊樣式手機(jī)
Vivo手機(jī)開機(jī)
折疊樣式手機(jī)
Vivo手機(jī)打電話
折疊樣式手機(jī)
Vivo手機(jī)關(guān)機(jī)
折疊樣式手機(jī)
4、總結(jié)
1)實(shí)現(xiàn)了抽象和實(shí)現(xiàn)部分的分離,從而極大的提供了系統(tǒng)的靈活性,讓抽象部分和實(shí)現(xiàn)部分獨(dú)立開來,這有助于系統(tǒng)進(jìn)行分層設(shè)計,從而產(chǎn)生更好的結(jié)構(gòu)化系統(tǒng)。
2)對于系統(tǒng)的高層部分,只需要知道抽象部分和實(shí)現(xiàn)部分的接口就可以了,其它的部分由具體業(yè)務(wù)來完成。
3)橋接模式替代多層繼承方案,可以減少子類的個數(shù),降低系統(tǒng)的管理和維護(hù)成本。
4)橋接模式的引入增加了系統(tǒng)的理解和設(shè)計難度,由于聚合關(guān)聯(lián)關(guān)系建立在抽象層,要求開發(fā)者針對抽象進(jìn)行設(shè)
計和編程
5)橋接模式要求正確識別出系統(tǒng)中兩個獨(dú)立變化的維度(抽象、和實(shí)現(xiàn)),因此其使用范圍有一定的局限性,。
6)應(yīng)用場景
- JDBC 驅(qū)動程序
- 銀行轉(zhuǎn)賬系統(tǒng)
- 轉(zhuǎn)賬分類:網(wǎng)上轉(zhuǎn)賬,柜臺轉(zhuǎn)賬,AMT轉(zhuǎn)賬
- 轉(zhuǎn)賬用戶類型:普通用戶,銀卡用戶,金卡用戶
- 消息管理
- 消息類型:即時消息,延時消息
- 消息分類:手機(jī)短信,郵件消息,QQ消息
九、裝飾者模式
1、介紹
裝飾者模式:動態(tài)的將新功能附加到對象上。在對象功能擴(kuò)展方面,它比繼承更有彈性,裝飾者模式也體現(xiàn)了開閉原則(ocp)
2、案例
1)咖啡種類/單品咖啡:Espresso(意大利濃咖啡)、ShortBlack、LongBlack(美式咖啡)、Decaf(無因咖啡)
2)調(diào)料:Milk、Soy(豆?jié){)、Chocolate
3)要求在擴(kuò)展新的咖啡種類時,具有良好的擴(kuò)展性、改動方便、維護(hù)方便
4)使用 OO 來計算不同種類咖啡的費(fèi)用:客戶可以點(diǎn)單品咖啡,也可以單品咖啡+調(diào)料組合。
傳統(tǒng)類圖:
1)可以控制類的數(shù)量,不至于造成很多的類
2)在增加或者刪除調(diào)料種類時,代碼的維護(hù)量很大
3)考慮到用戶可以添加多份 調(diào)料時,可以將 hasMilk 返回一個對應(yīng) int
改進(jìn)類圖
1、drink抽象類
public abstract class Drink {public String des; // 描述private float price = 0.0f;public String getDes() {return des;}public void setDes(String des) {this.des = des;}public float getPrice() {return price;}public void setPrice(float price) {this.price = price;}//計算費(fèi)用的抽象方法//子類來實(shí)現(xiàn)public abstract float cost();}
2、coffee類
public class Coffee extends Drink {@Overridepublic float cost() {return super.getPrice();}}
3、濃縮咖啡
public class ShortBlack extends Coffee{public ShortBlack() {setDes(" shortblack ");setPrice(4.0f);}
}
4、無因咖啡
public class DeCaf extends Coffee {public DeCaf() {setDes(" 無因咖啡 ");setPrice(1.0f);}
}
5、意大利咖啡
public class Espresso extends Coffee {public Espresso() {setDes(" 意大利咖啡 ");setPrice(6.0f);}
}
6、美式咖啡
public class LongBlack extends Coffee {public LongBlack() {setDes(" longblack ");setPrice(5.0f);}
}
以上都是咖啡類型
7、裝飾類
public class Decorator extends Drink {private Drink obj;public Decorator(Drink obj) { //組合this.obj = obj;}@Overridepublic float cost() {// getPrice 自己價格return super.getPrice() + obj.cost();}@Overridepublic String getDes() {// obj.getDes() 輸出被裝飾者的信息return des + " " + getPrice() + " && " + obj.getDes();}}
8、巧克力
//具體的Decorator, 這里就是調(diào)味品
public class Chocolate extends Decorator {public Chocolate(Drink obj) {super(obj);setDes(" 巧克力 ");setPrice(3.0f); // 調(diào)味品 的價格}}
9、牛奶
public class Milk extends Decorator {public Milk(Drink obj) {super(obj);setDes(" 牛奶 ");setPrice(2.0f); }}
10、豆?jié){
public class Soy extends Decorator{public Soy(Drink obj) {super(obj);setDes(" 豆?jié){ ");setPrice(1.5f);}}
11、測試
public static void main(String[] args) {// 裝飾者模式下的訂單:2份巧克力+一份牛奶的LongBlack// 1. 點(diǎn)一份 LongBlackDrink order = new LongBlack();System.out.println("費(fèi)用1=" + order.cost());System.out.println("描述=" + order.getDes());// 2. order 加入一份牛奶order = new Milk(order);System.out.println("order 加入一份牛奶 費(fèi)用 =" + order.cost());System.out.println("order 加入一份牛奶 描述 = " + order.getDes());// 3. order 加入一份巧克力order = new Chocolate(order);System.out.println("order 加入一份牛奶 加入一份巧克力 費(fèi)用 =" + order.cost());System.out.println("order 加入一份牛奶 加入一份巧克力 描述 = " + order.getDes());// 3. order 加入一份巧克力order = new Chocolate(order);System.out.println("order 加入一份牛奶 加入2份巧克力 費(fèi)用 =" + order.cost());System.out.println("order 加入一份牛奶 加入2份巧克力 描述 = " + order.getDes());System.out.println("===========================");Drink order2 = new DeCaf();System.out.println("order2 無因咖啡 費(fèi)用 =" + order2.cost());System.out.println("order2 無因咖啡 描述 = " + order2.getDes());order2 = new Milk(order2);
x System.out.println("order2 無因咖啡 加入一份牛奶 費(fèi)用 =" + order2.cost());System.out.println("order2 無因咖啡 加入一份牛奶 描述 = " + order2.getDes());}
描述= longblack
order 加入一份牛奶 費(fèi)用 =7.0
order 加入一份牛奶 描述 = 牛奶 2.0 && longblack
order 加入一份牛奶 加入一份巧克力 費(fèi)用 =10.0
order 加入一份牛奶 加入一份巧克力 描述 = 巧克力 3.0 && 牛奶 2.0 && longblack
order 加入一份牛奶 加入2份巧克力 費(fèi)用 =13.0
order 加入一份牛奶 加入2份巧克力 描述 = 巧克力 3.0 && 巧克力 3.0 && 牛奶 2.0 && longblack
=============================================
order2 無因咖啡 費(fèi)用 =1.0
order2 無因咖啡 描述 = 無因咖啡
order2 無因咖啡 加入一份牛奶 費(fèi)用 =3.0
order2 無因咖啡 加入一份牛奶 描述 = 牛奶 2.0 && 無因咖啡
3、jdk中的使用
Java 的 IO 結(jié)構(gòu),FilterInputStream 就是一個裝飾者
public static void main(Stringl args) throws Exception{//說明//1.nputStream 是抽象類,類似我們前面講的 Drink//2. FilelnputStream 是InputStream 子類,類似我們前面的 DeCaf, LongBlack//3.FilterlnputStream 是 InputStream 子類:類似我們前面 的 Decorator 修飾者//4. DatalnputStream 是 FilterlnputStream 子類,具體的修飾者,類似前面的 Mik, Soy 等//5.FilterInputStream 類有protected volatile InputStream in;即含被裝飾者//6.分析得出在 jdk 的io體系中,就是使用裝飾者模式DatalnputStream dis = new DatalnputStream(new FileInputStream("d:\\abc.txt" ));System.out.println(dis.read());dis.close();
}
十、組合模式
1、介紹
1)組合模式(Composite Pattern),又叫部分整體模式,它創(chuàng)建了對象組的樹形結(jié)構(gòu),將對象組合成樹狀結(jié)構(gòu)以表示**“整體-部分”**的層次關(guān)系。
2)組合模式依據(jù)樹形結(jié)構(gòu)來組合對象,用來表示部分以及整體層次。
3)組合模式使得用戶對單個對象和組合對象的訪問具有一致性,即:組合能讓客戶以一致的方式處理個別對象以
及組合對象
2、原理
1)Component:這是組合中對象聲明接口,在適當(dāng)情況下,實(shí)現(xiàn)所有類共有的接口默認(rèn)行為,用于訪問和管理Component 子部件,Component 可以是抽象類或者接口
2)Leaf:在組合中表示葉子節(jié)點(diǎn),葉子節(jié)點(diǎn)沒有子節(jié)點(diǎn)
3)Composite:非葉子節(jié)點(diǎn), 用于存儲子部件, 在 Component 接口中實(shí)現(xiàn) 子部件的相關(guān)操作,比如增加(add),刪除。
3、案例
編寫程序展示一個學(xué)校院系結(jié)構(gòu):要在一個頁面中展示出學(xué)校的院系組成,一個學(xué)校有多個學(xué)院,個學(xué)院有多個系。
1、抽象類
public abstract class OrganizationComponent {private String name; // 名字private String des; // 說明protected void add(OrganizationComponent organizationComponent) {//默認(rèn)實(shí)現(xiàn)throw new UnsupportedOperationException();}protected void remove(OrganizationComponent organizationComponent) {//默認(rèn)實(shí)現(xiàn)throw new UnsupportedOperationException();}//構(gòu)造器public OrganizationComponent(String name, String des) {super();this.name = name;this.des = des;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getDes() {return des;}public void setDes(String des) {this.des = des;}//方法print, 做成抽象的, 子類都需要實(shí)現(xiàn)protected abstract void print();}
2、學(xué)校
//University 就是 Composite , 可以管理College
public class University extends OrganizationComponent {List<OrganizationComponent> organizationComponents = new ArrayList<OrganizationComponent>();// 構(gòu)造器public University(String name, String des) {super(name, des);}// 重寫add@Overrideprotected void add(OrganizationComponent organizationComponent) {organizationComponents.add(organizationComponent);}// 重寫remove@Overrideprotected void remove(OrganizationComponent organizationComponent) {organizationComponents.remove(organizationComponent);}@Overridepublic String getName() {return super.getName();}@Overridepublic String getDes() {return super.getDes();}// print方法,就是輸出University 包含的學(xué)院@Overrideprotected void print() {System.out.println("--------------" + getName() + "--------------");//遍歷 organizationComponents for (OrganizationComponent organizationComponent : organizationComponents) {organizationComponent.print();}}}
3、學(xué)院
public class College extends OrganizationComponent {//List 中 存放的DepartmentList<OrganizationComponent> organizationComponents = new ArrayList<OrganizationComponent>();// 構(gòu)造器public College(String name, String des) {super(name, des);}// 重寫add@Overrideprotected void add(OrganizationComponent organizationComponent) {// 將來實(shí)際業(yè)務(wù)中,Colleage 的 add 和 University add 不一定完全一樣organizationComponents.add(organizationComponent);}// 重寫remove@Overrideprotected void remove(OrganizationComponent organizationComponent) {organizationComponents.remove(organizationComponent);}@Overridepublic String getName() {return super.getName();}@Overridepublic String getDes() {return super.getDes();}// print方法,就是輸出University 包含的學(xué)院@Overrideprotected void print() {System.out.println("--------------" + getName() + "--------------");//遍歷 organizationComponents for (OrganizationComponent organizationComponent : organizationComponents) {organizationComponent.print();}}}
4、院系
public class Department extends OrganizationComponent {//沒有集合public Department(String name, String des) {super(name, des);}//add , remove 就不用寫了,因?yàn)樗侨~子節(jié)點(diǎn)@Overridepublic String getName() {return super.getName();}@Overridepublic String getDes() {return super.getDes();}@Overrideprotected void print() {System.out.println(getName());}}
5、測試
public static void main(String[] args) {//從大到小創(chuàng)建對象 學(xué)校OrganizationComponent university = new University("清華大學(xué)", " 中國頂級大學(xué) ");//創(chuàng)建 學(xué)院OrganizationComponent computerCollege = new College("計算機(jī)學(xué)院", " 計算機(jī)學(xué)院 ");OrganizationComponent infoEngineercollege = new College("信息工程學(xué)院", " 信息工程學(xué)院 ");//創(chuàng)建各個學(xué)院下面的系(專業(yè))computerCollege.add(new Department("軟件工程", " 軟件工程不錯 "));computerCollege.add(new Department("網(wǎng)絡(luò)工程", " 網(wǎng)絡(luò)工程不錯 "));computerCollege.add(new Department("計算機(jī)科學(xué)與技術(shù)", " 計算機(jī)科學(xué)與技術(shù)是老牌的專業(yè) "));infoEngineercollege.add(new Department("通信工程", " 通信工程不好學(xué) "));infoEngineercollege.add(new Department("信息工程", " 信息工程好學(xué) "));//將學(xué)院加入到 學(xué)校university.add(computerCollege);university.add(infoEngineercollege);university.print();
// infoEngineercollege.print();}
--------------清華大學(xué)--------------
--------------計算機(jī)學(xué)院--------------
軟件工程
網(wǎng)絡(luò)工程
計算機(jī)科學(xué)與技術(shù)
--------------信息工程學(xué)院--------------
通信工程
信息工程
4、總結(jié)
1)簡化客戶端操作??蛻舳酥恍枰鎸σ恢碌膶ο蠖挥每紤]整體部分或者節(jié)點(diǎn)葉子的問題。
2)具有較強(qiáng)的擴(kuò)展性。當(dāng)要更改組合對象時,我們只需要調(diào)整內(nèi)部的層次關(guān)系,客戶端不用做出任何改動.
3)方便創(chuàng)建出復(fù)雜的層次結(jié)構(gòu)??蛻舳瞬挥美頃M合里面的組成細(xì)節(jié),容易添加節(jié)點(diǎn)或者葉子從而創(chuàng)建出復(fù)雜的樹形結(jié)構(gòu)
4)需要遍歷組織機(jī)構(gòu),或者處理的對象具有樹形結(jié)構(gòu)時,非常適合使用組合模式
5)要求較高的抽象性,如果節(jié)點(diǎn)和葉子有很多差異性的話,比如很多方法和屬性都不一樣,不適合使用組合模式
十一、外觀模式
1、介紹
1)外觀模式(Facade),也叫“過程模式:外觀模式為子系統(tǒng)中的一組接口提供一個一致的界面,此模式定義了一個高層接口,這個接口使得這一子系統(tǒng)更加容易使用
2)外觀模式通過定義一個一致的接口,用以屏蔽內(nèi)部子系統(tǒng)的細(xì)節(jié),使得調(diào)用端只需跟這個接口發(fā)生調(diào)用,而無需關(guān)心這個子系統(tǒng)的內(nèi)部細(xì)節(jié)
2、案例
組建一個家庭影院:
DVD 播放器、投影儀、自動屏幕、環(huán)繞立體聲、爆米花機(jī),要求完成使用家庭影院的功能,其過程為:直接用遙控器:統(tǒng)籌各設(shè)備開關(guān)
開爆米花機(jī)
放下屏幕
開投影儀
開音響
開 DVD,選 dvd
去拿爆米花
調(diào)暗燈光
播放
觀影結(jié)束后,關(guān)閉各種設(shè)備
1、爆米花類
public class Popcorn {private static Popcorn instance = new Popcorn();public static Popcorn getInstance() {return instance;}public void on() {System.out.println(" popcorn on ");}public void off() {System.out.println(" popcorn ff ");}public void pop() {System.out.println(" popcorn is poping ");}
}
2、屏幕類
public class Screen {private static Screen instance = new Screen();public static Screen getInstance() {return instance;}public void up() {System.out.println(" Screen up ");}public void down() {System.out.println(" Screen down ");}}
3、投影儀類
public class Projector {private static Projector instance = new Projector();public static Projector getInstance() {return instance;}public void on() {System.out.println(" Projector on ");}public void off() {System.out.println(" Projector ff ");}public void focus() {System.out.println(" Projector is Projector ");}}
4、音響類
public class Stereo {private static Stereo instance = new Stereo();public static Stereo getInstance() {return instance;}public void on() {System.out.println(" Stereo on ");}public void off() {System.out.println(" Screen off ");}public void up() {System.out.println(" Screen up.. ");}}
5、dvd類
public class DVDPlayer {//使用單例模式, 使用餓漢式private static DVDPlayer instance = new DVDPlayer();public static DVDPlayer getInstanc() {return instance;}public void on() {System.out.println(" dvd on ");}public void off() {System.out.println(" dvd off ");}public void play() {System.out.println(" dvd is playing ");}public void pause() {System.out.println(" dvd pause ..");}
}
6、燈光類
public class TheaterLight {private static TheaterLight instance = new TheaterLight();public static TheaterLight getInstance() {return instance;}public void on() {System.out.println(" TheaterLight on ");}public void off() {System.out.println(" TheaterLight off ");}public void dim() {System.out.println(" TheaterLight dim.. ");}public void bright() {System.out.println(" TheaterLight bright.. ");}
}
7、統(tǒng)籌類(家庭影院)
public class HomeTheaterFacade {//定義各個子系統(tǒng)對象private TheaterLight theaterLight;private Popcorn popcorn;private Stereo stereo;private Projector projector;private Screen screen;private DVDPlayer dVDPlayer;//構(gòu)造器public HomeTheaterFacade() {super();this.theaterLight = TheaterLight.getInstance();this.popcorn = Popcorn.getInstance();this.stereo = Stereo.getInstance();this.projector = Projector.getInstance();this.screen = Screen.getInstance();this.dVDPlayer = DVDPlayer.getInstanc();}//操作分成 4 步public void ready() {popcorn.on();popcorn.pop();screen.down();projector.on();stereo.on();dVDPlayer.on();theaterLight.dim();}public void play() {dVDPlayer.play();}public void pause() {dVDPlayer.pause();}public void end() {popcorn.off();theaterLight.bright();screen.up();projector.off();stereo.off();dVDPlayer.off();}}
8、測試
public static void main(String[] args) {//這里直接調(diào)用。。 很麻煩HomeTheaterFacade homeTheaterFacade = new HomeTheaterFacade();// 開始System.out.println("==============開始播放=============");homeTheaterFacade.ready();// 暫停System.out.println("==============暫停=============");homeTheaterFacade.play();// 結(jié)束System.out.println("==============結(jié)束=============");homeTheaterFacade.end();
==開始播放=
popcorn on
popcorn is poping
Screen down
Projector on
Stereo on
dvd on
TheaterLight dim…
==暫停=
dvd is playing
==結(jié)束=
popcorn ff
TheaterLight bright…
Screen up
Projector ff
Screen off
dvd off
3、總結(jié)
1)外觀模式對外屏蔽了子系統(tǒng)的細(xì)節(jié),因此外觀模式降低了客戶端對子系統(tǒng)使用的復(fù)雜性
2)外觀模式對客戶端與子系統(tǒng)的耦合關(guān)系-解耦,讓子系統(tǒng)內(nèi)部的模塊更易維護(hù)和擴(kuò)展
3)通過合理的使用外觀模式,可以幫我們更好的劃分訪問的層次
4)當(dāng)系統(tǒng)需要進(jìn)行分層設(shè)計時,可以考慮使用Facade 模式
5)在維護(hù)一個遺留的大型系統(tǒng)時,可能這個系統(tǒng)已經(jīng)變得非常難以維護(hù)和擴(kuò)展,此時可以考慮為新系統(tǒng)開發(fā)一個Facade 類,來提供遺留系統(tǒng)的比較清晰簡單的接口,讓新系統(tǒng)與 Facade 類交互,提高復(fù)用性
十二、享元模式
1、介紹
1)享元模式(Flyweight Pattern) 也叫 蠅量模式:運(yùn)用共享技術(shù)有效地支持大量細(xì)粒度的對象
2)常用于系統(tǒng)底層開發(fā),解決系統(tǒng)的性能問題。像數(shù)據(jù)庫連接池,里面都是創(chuàng)建好的連接對象,在這些連接對象中有我們需要的則直接拿來用,避免重新創(chuàng)建,如果沒有我們需要的,則創(chuàng)建一個
3)享元模式能夠解決重復(fù)對象的內(nèi)存浪費(fèi)的問題,當(dāng)系統(tǒng)中有大量相似對象,需要緩沖池時。不需總是創(chuàng)建新對象,可以從緩沖池里拿。這樣可以降低系統(tǒng)內(nèi)存,同時提高效率
4)享元模式經(jīng)典的應(yīng)用場景就是池技術(shù)了,String 常量池、數(shù)據(jù)庫連接池、緩沖池等等都是享元模式的應(yīng)用,享
元模式是池技術(shù)的重要實(shí)現(xiàn)方式
2、內(nèi)部狀態(tài)與外部狀態(tài)
1)享元模式提出了兩個要求:細(xì)粒度和共享對象。即將對象的信息分為兩個部分:內(nèi)部狀態(tài)和外部狀態(tài)
2)內(nèi)部狀態(tài)指對象共享出來的信息,存儲在享元對象內(nèi)部且不會隨環(huán)境的改變而改變
3)外部狀態(tài)指對象得以依賴的一個標(biāo)記,是隨環(huán)境改變而改變的、不可共享的狀態(tài)。
3、案例
小型的外包項目,給客戶 A做一個產(chǎn)品展示網(wǎng)站,客戶A的朋友感覺效果不錯,也希望做這樣的產(chǎn)品展示網(wǎng)站,但是要求都有些不同:
1)有客戶要求以新聞的形式發(fā)布
2)有客戶人要求以博客的形式發(fā)布
3)有客戶希望以微信公眾號的形式發(fā)布
1、外部狀態(tài)user
public class User {private String name;public User(String name) {super();this.name = name;}public String getName() {return name;}public void setName(String name) {this.name = name;}}
2、抽象類
public abstract class WebSite {public abstract void use(User user);//抽象方法
}
3、具體網(wǎng)站
//具體網(wǎng)站
public class ConcreteWebSite extends WebSite {//共享的部分,內(nèi)部狀態(tài)private String type = ""; //網(wǎng)站發(fā)布的形式(類型)//構(gòu)造器public ConcreteWebSite(String type) {this.type = type;}@Overridepublic void use(User user) {System.out.println("網(wǎng)站的發(fā)布形式為:" + type + " 在使用中 .. 使用者是" + user.getName());}}
4、工廠類
// 網(wǎng)站工廠類,根據(jù)需要返回壓一個網(wǎng)站
public class WebSiteFactory {//集合, 充當(dāng)池的作用private HashMap<String, ConcreteWebSite> pool = new HashMap<>();//根據(jù)網(wǎng)站的類型,返回一個網(wǎng)站, 如果沒有就創(chuàng)建一個網(wǎng)站,并放入到池中,并返回public WebSite getWebSiteCategory(String type) {if(!pool.containsKey(type)) {//就創(chuàng)建一個網(wǎng)站,并放入到池中pool.put(type, new ConcreteWebSite(type));}return (WebSite)pool.get(type);}//獲取網(wǎng)站分類的總數(shù) (池中有多少個網(wǎng)站類型)public int getWebSiteCount() {return pool.size();}
}
5、測試
public static void main(String[] args) {// 創(chuàng)建一個工廠類WebSiteFactory factory = new WebSiteFactory();// 客戶要一個以新聞形式發(fā)布的網(wǎng)站WebSite webSite1 = factory.getWebSiteCategory("新聞");webSite1.use(new User("tom"));// 客戶要一個以博客形式發(fā)布的網(wǎng)站WebSite webSite2 = factory.getWebSiteCategory("博客");webSite2.use(new User("jack"));// 客戶要一個以博客形式發(fā)布的網(wǎng)站WebSite webSite3 = factory.getWebSiteCategory("博客");webSite3.use(new User("smith"));// 客戶要一個以博客形式發(fā)布的網(wǎng)站WebSite webSite4 = factory.getWebSiteCategory("博客");webSite4.use(new User("king"));System.out.println("網(wǎng)站的分類共=" + factory.getWebSiteCount());}
網(wǎng)站的發(fā)布形式為:新聞 在使用中 … 使用者是tom
網(wǎng)站的發(fā)布形式為:博客 在使用中 … 使用者是jack
網(wǎng)站的發(fā)布形式為:博客 在使用中 … 使用者是smith
網(wǎng)站的發(fā)布形式為:博客 在使用中 … 使用者是king
網(wǎng)站的分類共=2
4、總結(jié)
1)享元模式理解:“享”就表示共享,“元”表示對象
2)系統(tǒng)中有大量對象,這些對象消耗大量內(nèi)存,并且對象的狀態(tài)大部分可以外部化時,我們就可以考慮選用享元模式
3)用唯一標(biāo)識碼判斷,如果在內(nèi)存中有,則返回這個唯一標(biāo)識碼所標(biāo)識的對象,用 HashMap/HashTable
4)存儲享元模式大大減少了對象的創(chuàng)建,降低了程序內(nèi)存的占用,提高效率
5)享元模式提高了系統(tǒng)的復(fù)雜度。需要分離出內(nèi)部狀態(tài)和外部狀態(tài),而外部狀態(tài)具有固化特性,不應(yīng)該隨著內(nèi)部狀態(tài)的改變而改變,這是我們使用享元模式需要注意的地方
6)使用享元模式時,注意劃分內(nèi)部狀態(tài)和外部狀態(tài),并且需要有一個工廠類加以控制。
7)享元模式經(jīng)典的應(yīng)用場景是需要緩沖池的場景,比如String 常量池、數(shù)據(jù)庫連接池
十三、代理模式
1、介紹
1)代理模式:為一個對象提供一個替身,以控制對這個對象的訪問。即通過代理對象訪問目標(biāo)對象,優(yōu)點(diǎn)是:可以在目標(biāo)對象實(shí)現(xiàn)的基礎(chǔ)上,增強(qiáng)額外的功能操作,即擴(kuò)展目標(biāo)對象的功能。
2)被代理的對象可以是遠(yuǎn)程對象、創(chuàng)建開銷大的對象或需要安全控制的對象
3)代理模式有不同的形式。主要有三種 靜態(tài)代理、動態(tài)代理(JDK 代理、接口代理)和 Cglib 代理(可以在內(nèi)存動態(tài)的創(chuàng)建對象,而不需要實(shí)現(xiàn)接口,屬于動態(tài)代理的范疇)。
2、靜態(tài)代理
(1)介紹
? 靜態(tài)代理在使用時,需要定義接口或者父類,被代理對象(即目標(biāo)對象)與代理對象一起實(shí)現(xiàn)相同的接口或者是繼承相同父類。
(2)案例
1)定義一個接口:ITeacherDao
2)目標(biāo)對象 TeacherDA0 實(shí)現(xiàn)接口 ITeacherDAO
3)使用靜態(tài)代理方式,就需要在代理對象 TeacherDAOProxy 中也實(shí)現(xiàn) ITeacherDAO
4)調(diào)用的時候通過調(diào)用代理對象的方法來調(diào)用目標(biāo)對象
5)代理對象與目標(biāo)對象要實(shí)現(xiàn)相同的接口,然后通過調(diào)用相同的方法來調(diào)用目標(biāo)對象的方法
1、接口
//接口
public interface ITeacherDao {void teach(); // 授課的方法
}
2、目標(biāo)對象
public class TeacherDao implements ITeacherDao {@Overridepublic void teach() {System.out.println(" 老師授課中 。。。。。");}}
3、代理對象
//代理對象,靜態(tài)代理
public class TeacherDaoProxy implements ITeacherDao{private ITeacherDao target; // 目標(biāo)對象,通過接口來聚合//構(gòu)造器public TeacherDaoProxy(ITeacherDao target) {this.target = target;}@Overridepublic void teach() {System.out.println("開始代理 完成某些操作。。。。。 ");//方法target.teach();System.out.println("提交。。。。。");//方法}}
4、測試
public static void main(String[] args) {// TODO Auto-generated method stub//創(chuàng)建目標(biāo)對象(被代理對象)TeacherDao teacherDao = new TeacherDao();//創(chuàng)建代理對象, 同時將被代理對象傳遞給代理對象TeacherDaoProxy teacherDaoProxy = new TeacherDaoProxy(teacherDao);//通過代理對象,調(diào)用到被代理對象的方法//即:執(zhí)行的是代理對象的方法,代理對象再去調(diào)用目標(biāo)對象的方法 teacherDaoProxy.teach();}
開始代理 完成某些操作。。。。。
老師授課中 。。。。。
提交。。。。。
(3)總結(jié)
1)優(yōu)點(diǎn):在不修改目標(biāo)對象的功能前提下,能通過代理對象對目標(biāo)功能擴(kuò)展
2)缺點(diǎn):因?yàn)榇韺ο笮枰c目標(biāo)對象實(shí)現(xiàn)一樣的接口,所以會有很多代理類
3)一旦接口增加方法,目標(biāo)對象與代理對象都要維護(hù)
3、動態(tài)代理
(1)介紹
1)代理對象,不需要實(shí)現(xiàn)接口,但是目標(biāo)對象要實(shí)現(xiàn)接口,否則不能用動態(tài)代理
2)代理對象的生成,是利用JDK的API,動態(tài)的在內(nèi)存中構(gòu)建代理對象
3)動態(tài)代理也叫做:JDK 代理、接口代理
4)在jdk中生成代理對象api
-
代理類所在包:java.lang.reflect.Proxy
-
JDK 實(shí)現(xiàn)代理只需要使用 newProxyInstance 方法,但是該方法需要接收三個參數(shù),寫法是:
static Obiect newProxyInstance(ClassLoader loader. Class<?> interfaces.InvocationHandler h)
(2)案例
將靜態(tài)代理改為動態(tài)代理
1、接口
//接口
public interface ITeacherDao {void teach(); // 授課方法void sayHello(String name);
}
2、目標(biāo)對象實(shí)現(xiàn)
public class TeacherDao implements ITeacherDao {@Overridepublic void teach() {System.out.println(" 老師授課中.... ");}@Overridepublic void sayHello(String name) {System.out.println("hello " + name);}}
3、代理
public class ProxyFactory {//維護(hù)一個目標(biāo)對象 , Objectprivate Object target;//構(gòu)造器 , 對target 進(jìn)行初始化public ProxyFactory(Object target) {this.target = target;} //給目標(biāo)對象 生成一個代理對象public Object getProxyInstance() {//說明/** public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)//1. ClassLoader loader : 指定當(dāng)前目標(biāo)對象使用的類加載器, 獲取加載器的方法固定//2. Class<?>[] interfaces: 目標(biāo)對象實(shí)現(xiàn)的接口類型,使用泛型方法確認(rèn)類型//3. InvocationHandler h : 事情處理,執(zhí)行目標(biāo)對象的方法時,會觸發(fā)事情處理器方法, 會把當(dāng)前執(zhí)行的目標(biāo)對象方法作為參數(shù)傳入*/return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(), new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("JDK代理開始~~");//反射機(jī)制調(diào)用目標(biāo)對象的方法Object returnVal = method.invoke(target, args);System.out.println("JDK代理提交");return returnVal;}}); }}
4、測試
public static void main(String[] args) {//創(chuàng)建目標(biāo)對象ITeacherDao target = new TeacherDao();//給目標(biāo)對象,創(chuàng)建代理對象, 可以轉(zhuǎn)成 ITeacherDaoITeacherDao proxyInstance = (ITeacherDao)new ProxyFactory(target).getProxyInstance();// proxyInstance=class com.sun.proxy.$Proxy0 內(nèi)存中動態(tài)生成了代理對象System.out.println("proxyInstance=" + proxyInstance.getClass());//通過代理對象,調(diào)用目標(biāo)對象的方法//proxyInstance.teach();proxyInstance.sayHello(" tom ");}
proxyInstance=class com.sun.proxy.$Proxy0
JDK代理開始~~
hello tom
JDK代理提交
4、Cglib代理
(1)介紹
1)靜態(tài)代理和JDK 代理模式都要求目標(biāo)對象是實(shí)現(xiàn)一個接口,但是有時候目標(biāo)對象只是一個單獨(dú)的對象,并沒有實(shí)現(xiàn)任何的接口,這個時候可使用目標(biāo)對象子類來實(shí)現(xiàn)代理-這就是 Cglib 代理
2)Cglib代理也叫作子類代理,它是在內(nèi)存中構(gòu)建一個子類對象從而實(shí)現(xiàn)對目標(biāo)對象功能擴(kuò)展,有些書也將Cglib代理歸屬到動態(tài)代理。
3)Cglib 是一個強(qiáng)大的高性能的代碼生成包,它可以在運(yùn)行期擴(kuò)展java類與實(shí)現(xiàn) java接口,它廣泛的被許多 AOP 的框架使用,例如 Spring AOP,實(shí)現(xiàn)方法攔截
4)在 AOP 編程中如何選擇代理模式:
-
目標(biāo)對象需要實(shí)現(xiàn)接口,用 JDK 代理
-
目標(biāo)對象不需要實(shí)現(xiàn)接口,用 Cglib 代理
5)Cglib 包的底層是通過使用字節(jié)碼處理框架 ASM 來轉(zhuǎn)換字節(jié)碼并生成新的類
(2)實(shí)現(xiàn)步驟
1)引入cglib的jar包
2)在內(nèi)存中動態(tài)構(gòu)建子類,注意代理的類不能為fnal,否則報錯 java.lang.IllegalArgumentException:
3)目標(biāo)對象的方法如果為 final/static,那么就不會被攔截,即不會執(zhí)行目標(biāo)對象額外的業(yè)務(wù)方法
(3)案例
1、目標(biāo)對象
public class TeacherDao {public String teach() {System.out.println(" 老師授課中 , 我是cglib代理,不需要實(shí)現(xiàn)接口 ");return "hello";}
}
2、代理類
public class ProxyFactory implements MethodInterceptor {//維護(hù)一個目標(biāo)對象private Object target;//構(gòu)造器,傳入一個被代理的對象public ProxyFactory(Object target) {this.target = target;}//返回一個代理對象: 是 target 對象的代理對象public Object getProxyInstance() {//1. 創(chuàng)建一個工具類Enhancer enhancer = new Enhancer();//2. 設(shè)置父類enhancer.setSuperclass(target.getClass());//3. 設(shè)置回調(diào)函數(shù)enhancer.setCallback(this);//4. 創(chuàng)建子類對象,即代理對象return enhancer.create();}//重寫 intercept 方法,會調(diào)用目標(biāo)對象的方法@Overridepublic Object intercept(Object arg0, Method method, Object[] args, MethodProxy arg3) throws Throwable {System.out.println("Cglib代理模式 ~~ 開始");Object returnVal = method.invoke(target, args);System.out.println("Cglib代理模式 ~~ 提交");return returnVal;}}
3、測試
public static void main(String[] args) {//創(chuàng)建目標(biāo)對象TeacherDao target = new TeacherDao();//獲取到代理對象,并且將目標(biāo)對象傳遞給代理對象TeacherDao proxyInstance = (TeacherDao)new ProxyFactory(target).getProxyInstance();//執(zhí)行代理對象的方法,觸發(fā)intecept 方法,從而實(shí)現(xiàn) 對目標(biāo)對象的調(diào)用String res = proxyInstance.teach();System.out.println("res=" + res);
}
Cglib代理模式 ~~ 開始
老師授課中 , 我是cglib代理,不需要實(shí)現(xiàn)接口
Cglib代理模式 ~~ 提交
res=hello