《設計模式》3.結構型模式


點擊進入我的博客

3.1 適配器模式

適配器模式把一個類的接口變換成客戶端所期待的另一種接口,使得原本因接口不匹配而無法在一起工作的兩個類能夠在一起工作。

3.1.1 類的適配器結構

類的適配器

  • 目標(Target)角色:這就是所期待得到的接口,由于是類適配器模式,因此目標不可以是類。
  • 源(Adaptee)角色:現有需要適配的接口。
  • 適配器(Adapter)角色:適配器類是本模式的核心,必須是具體類。
interface Target {void operation1();void operation2();
}class Adaptee {public void operation1() {}
}class Adapter extends Adaptee implements Target {@Overridepublic void operation2() {}
}
類的適配器
  • 類的適配器模式把被的類的API轉換成目標類的API。
  • 是通過繼承實現的。
類的適配器效果
  • 使用一個具體類把源適配到目標中。這樣如果源以及源的子類都使用此類適配,就行不通了。
  • 由于適配器是源的子類,因此可以在適配器中重寫源的一些方法。
  • 由于引進了一個適配器類,因此只有一個線路到達目標類,是問題得到簡化。

3.1.2 對象的適配器結構

對象適配器

  • 目標(Target)角色:這就是所期待得到的接口,因此目標可以是具體或抽象的類
  • 源(Adaptee)角色:現有需要適配的接口。
  • 適配器(Adapter)角色:適配器類是本模式的核心,必須是具體類。
interface Target {void operation1();void operation2();
}class Adaptee {public void operation1() {}
}class Adapter implements Target {private Adaptee adaptee;public Adapter(Adaptee adaptee) {this.adaptee = adaptee;}@Overridepublic void operation1() {adaptee.operation1();}@Overridepublic void operation2() {// do something}
}
對象的適配器
  • 對象的適配器是通過依賴實現的
  • 推薦使用該方法
對象的適配器效果
  • 一個適配器可以把多種不同的源適配到同一個目標,即同一個適配器可以把源類和它的子類都適配到目標接口
  • 與類的適配器模式相比,要想置換源類的方法就不容易。
  • 增加新的方法方便的多

3.1.3 細節

使用場景
  1. 系統需要使用現有的類,而此類的接口不符合系統的需要
  2. 想要建立一個可以重復使用的類,用于與一些彼此間沒有太大關聯的一些類,包括一些可能在將來引進的類一起工作。
  3. 對對象的適配器兒模式而言,在設計里,需要改變多個已有的子類的接口,如果使用類的適配器模式,就需要針對每個子類做一個適配器類,這不太實際。
優點
  1. 可以讓任何兩個沒有關聯的類一起運行。
  2. 提高了類的復用。
  3. 增加了類的透明度。
  4. 靈活性好。
缺點
  1. 過多地使用適配器,會讓系統非常零亂,不易整體進行把握。比如,明明看到調用的是 A 接口,其實內部被適配成了 B 接口的實現,一個系統如果太多出現這種情況,無異于一場災難。因此如果不是很有必要,可以不使用適配器,而是直接對系統進行重構。
  2. 由于 JAVA 至多繼承一個類,所以至多只能適配一個適配者類,而且目標類必須是抽象類。
注意點
  1. 目標接口可以忽略,此時目標接口和源接口實際上是相同的
  2. 適配器類可以是抽象類
  3. 可以有帶參數的適配器模式

3.1.4 一個充電器案例

// 充電器只能接受USB接口
public class Charger {public static void main(String[] args) throws Exception{USB usb = new SuperAdapter(new TypeC());connect(usb);usb = new SuperAdapter(new Lightning());connect(usb);}public static void connect(USB usb) {usb.power();usb.data();}
}// 充電器的接口都是USB的,假設有兩個方法分別是電源和數據
interface USB {void power();void data();
}// IOS的Lightning接口
class Lightning {void iosPower() {System.out.println("IOS Power");}void iosData() {System.out.println("IOS Data");}
}// TYPE-C接口
class TypeC {void typeCPower() {System.out.println("TypeC Power");}void typeCData() {System.out.println("TypeC Data");}
}// 超級適配器,可以適配多種手機機型
class SuperAdapter implements USB {private Object obj;public SuperAdapter(Object obj) {this.obj = obj;}@Overridepublic void power() {if(obj.getClass() == Lightning.class) {((Lightning)obj).iosPower();} else if(obj.getClass() == TypeC.class) {((TypeC)obj).typeCPower();}}@Overridepublic void data() {if(obj.getClass() == Lightning.class) {((Lightning)obj).iosData();} else if(obj.getClass() == TypeC.class) {((TypeC)obj).typeCData();}}
}

3.2 缺省適配模式

缺省適配模式為一個接口提供缺省實現,這樣子類型可以從這個缺省實現進行擴展,而不必從原有接口進行擴展。

3.2.1 缺省適配模式結構

缺省適配模式

簡單的例子
  • 下面程序中,Monk接口定義了兩個方法,于是它的子類必須實現這兩個方法。
  • 但出現了一個LuZhiShen,他只能實現一部分方法,另一部分方法無法實現
  • 所以需要一個抽象的適配類MonkAdapter實現此Monk接口,此抽象類給接口所有方法都提供一個空的方法,LuZhiShen只需要繼承該適配類即可。
// 和尚
interface Monk {void practiceKungfu();void chantPrayer();
}abstract class MonkAdapter implements Monk {@Overridepublic void practiceKungfu() {}@Overridepublic void chantPrayer() {}
}class LuZhiShen extends MonkAdapter {@Overridepublic void practiceKungfu() {System.out.println("拳打鎮關西");}
}

3.2.2 細節

使用場景
  • 任何時候不準備實現一個接口中所有方法的時候
作用

缺省適配器模式可以使所需要的類不必實現不需要的接口。

核心點
  • 缺省適配的類必須是抽象類,因為這個類不應當被實例化
  • 缺省適配的類提供的方法必須是具體的方法,而不是抽象的方法。

3.3 組合模式

組合模式,就是在一個對象中包含其他對象,這些被包含的對象可能是終點對象(不再包含別的對象),也有可能是非終點對象(其內部還包含其他對象)。
我們將對象稱為節點,即一個根節點包含許多子節點,這些子節點有的不再包含子節點,而有的仍然包含子節點,以此類推。很明顯,這是樹形結構,終結點叫葉子節點,非終節點叫樹枝節點,第一個節點叫根節點。

3.3.1 安全式的合成模式結構

安全式的合成模式要求管理集合的方法只出現在樹枝結點(Composite)中,而不出現在樹葉結點中。
安全式的合成模式

  • 抽象構建(Component)角色:這是一個抽象角色,他給參加組合的對象定義出公共的接口及其默認行為,可以用來管理所有的子對象。
  • 樹葉(Leaf)角色:樹葉是沒有子對象的對象,定義出參加組合的原始對象的行為。
  • 樹枝(Composite)角色:代表參加組合的有下級子對象的對象。樹枝構件類給出所有管理子對象的方法。

3.3.2 透明的合成模式結構

透明的合成模式
透明的合成模式要求所有的具體構建類,都符合一個固定的接口。

3.4 裝飾器模式

裝飾器模式(Decorator)允許向一個現有的對象添加新的功能,同時又不改變其結構。這種類型的設計模式屬于結構型模式,它是作為現有的類的一個包裝。

3.4.1 裝飾器結構

裝飾器模式

  • 抽象構件(Component)角色:給出一個抽象結構,以規范準備接受附加責任的對象。
  • 具體構件(Concrete Component)角色:定義一個要接受附加責任的類
  • 裝飾(Decorator)角色:持有一個構件對象的實例,并定義一個與抽象構件接口一致的接口。
  • 具體裝飾(Concrete Decorator)角色:負責給構件對象“貼上”附加的責任。

3.4.2 裝飾器細節

使用場景
  1. 需要擴展一個類的功能
  2. 需要動態地給一個對象增加功能,這些功能可以再動態的插銷
  3. 需要增加由一些基本功能的排列組合而產生非常大量的功能
優點
  1. 更加靈活:裝飾模式和繼承關系的目的都是要擴展對象的功能,但是裝飾模式比繼承更加靈活
  2. 多樣性:通過使用不同具體裝飾類及其排列組合,可以創造出不同的行為
  3. 動態擴展:裝飾器可以動態擴展構件類
缺點
  1. 會產生比繼承關系更多的對象
  2. 比繼承更加容易出錯
注意點
  1. 裝飾類的接口必須與被裝飾類的接口相容。
  2. 盡量保持抽象構件(Component)簡單。
  3. 可以沒有抽象的(Component),此時裝飾器(Decorator)一般是具體構件(Concrete Component)的一個子類。
  4. 裝飾(Decorator)和具體裝飾(Concrete Decorator)可以合并。
InputStream及其子類

InputStream

  • 抽象構件(Component)角色:InputStream
  • 具體構件(Concrete Component)角色:ByteArrayInputStreamPipedInputStreamStringBufferInputStream等原始流處理器。
  • 裝飾(Decorator)角色:FilterInputStream
  • 具體裝飾(Concrete Decorator)角色:DateInputStreamBufferedInputStreamLineNumberInputStream
OutputStream及其子類

也用到類裝飾器模式

3.4.3 例子

// Component:一個藝人
interface Artist {void show();
}// Concrete Component:一個歌手
class Singer implements Artist {@Overridepublic void show() {System.out.println("Let It Go");}
}// 裝飾后的歌手:不僅會唱歌,還會講笑話和跳舞
class SuperSinger implements Artist {private Artist role;public SuperSinger(Artist role) {this.role = role;}@Overridepublic void show() {System.out.println("Tell Jokes!");role.show();System.out.println("Dance!");}
}

3.5 代理模式

代理模式給某一個對象提供一個代理對象,并由代理對象控制對原對象對引用

3.5.1 代理模式結構

代理模式

  • 抽象主題(Subject)角色:聲明了真實主題和代理主題的共同接口
  • 代理主題(Proxy)角色:代理主題角色內部含有對真實主題的引用,從而可以在任何時候操作真實主題對象;代理主題角色提供一個與真實主題角色相同的接口,以便可以在任何時候都可以替代真實主題控制對真實主題的引用,負責在需要的時候創建真實主題對象(和刪除真實主題對象);代理角色通常在將客戶端調用傳遞給真實的主題之前或之后,都要執行某個操作,而不是單純地將調用傳遞給真實主題對象。
  • 真實主題(RealSubject)角色:定義了代理角色所代表的真實對象。
public class Test {public static void main(String[] args) {Subject subject = new RealSubject();Subject proxy = new ProxySubject(subject);proxy.request(); // 此處通過代理類來執行}
}interface Subject {void request();
}class RealSubject implements Subject {@Overridepublic void request() {System.out.println("RealSubject");}
}class ProxySubject implements Subject {private Subject subject;public ProxySubject(Subject subject) {this.subject = subject;}@Overridepublic void request() {System.out.println("ProxySubject");}
}

3.5.2 動態代理

自從JDK 1.3以后,Java在java.lang.reflect庫中提供了一下三個類直接支持代理模式:ProxyInvocationHanderMethod

動態代理步驟
  1. 創建一個真實對象
  2. 創建一個與真實對象有關的調用處理器對象InvocationHandler
  3. 創建代理,把調用處理器和要代理的類聯系起來Proxy.newInstance()
  4. 在調用處理對象的invoke()方法中執行相應操作
public class Test {public static void main(String[] args) {// 創建要被代理的實例對象Subject subject = new RealSubject();// 創建一個與被代理實例對象有關的InvocationHandlerInvocationHandler handler = new ProxySubject(subject);// 創建一個代理對象來代理subject,被代理的對象subject的每個方法執行都會調用代理對象proxySubject的invoke方法Subject proxySubject = (Subject) Proxy.newProxyInstance(Subject.class.getClassLoader(), new Class[]{Subject.class}, handler);// 代理對象執行proxySubject.request();}
}interface Subject {void request();
}class RealSubject implements Subject {@Overridepublic void request() {System.out.println("RealSubject");}
}class ProxySubject implements InvocationHandler {private Subject subject;public ProxySubject(Subject subject) {this.subject = subject;}/*** @param proxy 要代理的* @param method* @param args* @return*/@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("Before Proxy");Object obj = method.invoke(subject, args);System.out.println("After Proxy");return obj;}
}
  • 可以使用范型來創建ProxySubject
  • 可以使用匿名內部類減少代碼數量請查看14.7節

3.5.3 細節

優點
  1. 代理類和真實類分離,職責清晰。
  2. 在不改變真是累代碼的基礎上擴展了功能。
缺點
  1. 由于在客戶端和真實主題之間增加了代理對象,因此有些類型的代理模式可能會造成請求的處理速度變慢。
  2. 實現代理模式需要額外的工作,有些代理模式的實現非常復雜。
和適配器模式的關系

適配器模式的用意是改變所考慮對象的接口,而代理模式不能改變。

和裝飾模式
  • 裝飾模式應當為所裝飾的對象提供增強功能
  • 代理模式對對象的使用施加控制,并不提供對象本身的增強功能
虛擬代理
  • 虛擬代理模式(Virtual PRoxy)會推遲真正所需對象實例化時間。在需要真正的對象工作之前,如果代理對象能夠處理,那么暫時不需要真正對象來出手。
  • 當一個真實主題對象的加載需要耗費資源時,一個虛擬代理對象可以代替真實對象接受請求,并展示“正在加載”的信息,并在適當的時候加載真實主題對象。

3.6 享元模式

享元模式以共享的方式高效地支持大量的細粒度對象。

3.6.1 單純享元模式

單純享元模式中,所有的享元對象都是可以共享的。
單純享元模式

  • 抽象享元(Flyweight)角色:是所有具體享元角色的超類,并為這些類規定公共接口。
  • 具體享元(Concrete Flyweight)角色:實現抽象享元的接口。如果由內蘊狀態的話,必須負責為內蘊狀態提供空間。
  • 享元工廠(Flyweight Factory)角色:負責創建和管理享元角色。如果系統中有了則返回該角色,沒有則創建。
  • 客戶端(Client)角色:維護一個所有享元對象的引用。存儲所有享元對象的外蘊狀態。

3.6.2 復合享元模式

復合享元模式

  • 抽象享元(Flyweight)角色 :給出一個抽象接口,以規定出所有具體享元角色需要實現的方法。
  • 具體享元(ConcreteFlyweight)角色:實現抽象享元角色所規定出的接口。如果有內蘊狀態的話,必須負責為內蘊狀態提供存儲空間。
  • 復合享元(ConcreteCompositeFlyweight)角色 :復合享元角色所代表的對象是不可以共享的,但是一個復合享元對象可以分解成為多個本身是單純享元對象的組合。復合享元角色又稱作不可共享的享元對象(UnsharedConcreteFlyweight)。
  • 享元工廠(FlyweightFactory)角色 :負責創建和管理享元角色。當一個客戶端對象調用一個享元對象的時候,如果已經有了,享元工廠角色就應當提供這個已有的享元對象;如果系統中沒有一個適當的享元對象的話,享元工廠角色就應當創建一個合適的享元對象。

3.6.3 細節

內蘊狀態和外蘊狀態

內蘊狀態:是存儲在享元對象內部的,不會隨環境改變而改變的。一個享元可以具有內蘊狀態并可以共享。
外蘊狀態:隨環境改變而改變、不可以共享的狀態。享元對象的外蘊狀態必須由客戶端保存,并在享元對象被創建之后,在需要使用的時候再傳入到享元對象內部。

不變模式

享元模式中的對象不一定非要是不變對象,但大多數享元對象的確是這么設計的。

享元工廠
  1. 使用單例模式:一般只需要一個享元工廠,可以設計成單例的。
  2. 備忘錄模式:享元工廠負責維護一個表,通過這個表把很多相同的實例與它們的一個對象聯系起來。
優點

減少對象的創建,降低內存消耗

缺點
  1. 提高了系統的復雜度,為了使對象可以共享,需要將一些狀態外部化
  2. 需要將一些狀態外部化,而讀取外部狀態是的運行時間稍微變長
使用場景
  1. 一個系統中有大量對象。
  2. 這些對象消耗大量內存。
  3. 這些對象的狀態大部分可以外部化。
  4. 這些對象可以按照內蘊狀態分為很多組,當把外蘊對象從對象中剔除出來時,每一組對象都可以用一個對象來代替。
  5. 系統不依賴于這些對象身份,換言之,這些對象是不可分辨的。
Java應用
  1. String對象,有則返回,沒有則創建一個字符串并保存
  2. 數據庫的連接池

3.6.4 案例

依舊是熟悉的KFC點餐為例:

  • 外蘊狀態:點餐的顧客
  • 內蘊狀態:顧客點的食物
  • 具體享元角色:維護內蘊狀態(客人要點的食物)。
public class KFC {public static void main(String[] args) {OrderFactory orderFactory = OrderFactory.getInstance();Order order = orderFactory.getOrder(Food.MiniBurger);order.operation("李雷");order = orderFactory.getOrder(Food.MiniBurger);order.operation("韓梅梅");}
}enum Food {MiniBurger,MexicanTwister,CornSalad,HotWing,PepsiCola
}// Flyweight角色
interface Order {// 傳入的是外蘊對象:顧客void operation(String customer);
}// ConcreteFlyweight角色
class FoodOrder implements Order {// 內蘊狀態private Food food;// 構造方法,傳入享元對象的內部狀態的數據public FoodOrder(Food food) {this.food = food;}@Overridepublic void operation(String customer) {System.out.println("顧客[" + customer + "]點的是" + food.toString());}
}// FlyweightFactory角色
class OrderFactory {private Map<Food, Order> orderPool = new HashMap<>();private static OrderFactory instance = new OrderFactory();private OrderFactory() {}public static OrderFactory getInstance() {return instance;}// 獲取Food對應的享元對象public Order getOrder(Food food) {Order order = orderPool.get(food);if (null == order) {order = new FoodOrder(food);orderPool.put(food, order);}return order;}
}

3.7 門面模式

門面模式(Facade Pattern)要求一個子系統的外部與其內部通信,必須通過一個統一的門面對象進行。

3.7.1 門面模式結構

門面模式沒有一個一般化的類圖描述,可以用下面的例子來說明。
門面模式

  • 門面(Facade)角色:外部可以調用這個角色的方法。此角色知道子系統的功能和責任。
  • 子系統(Subsystem)角色:可以有多個子系統,子系統不需要知道門面的存在。

3.7.2 細節

門面數量

通常只需要一個門面類,而且只有一個實例,因此可以設計稱單例模式。當然也可有多個類。

使用場景
  1. 為一個復雜的子系統提供一個簡單的接口
  2. 使子系統和外部分離開來
  3. 構建一個層次化系統時,可以使使用Facade模式定義系統中每一層,實現分層。
優點
  1. 減少系統之間的相互依賴。
  2. 提高了安全性。
缺點
  1. 不符合開閉原則
  2. 如果要改東西很麻煩,繼承重寫都不合適。
Java例子

MVC三層結構

3.7.3 KFC例子

假如沒有服務員(門面),顧客(外部系統)要點一個套餐需要知道每個套餐包含的食物(子系統)種類,這樣就會非常麻煩,所以最好的方式是直接告訴服務員套餐名稱就好了。

public class Customer {public static void main(String[] args) {Waiter waiter = new Waiter();List<Food> foodList = waiter.orderCombo("Combo1");}
}abstract class Food {}
class MiniBurger extends Food {}
class MexicanTwister extends Food {}
class CornSalad extends Food {}
class HotWing extends Food {}
class PepsiCola extends Food {}class Waiter {public List<Food> orderCombo(String comboName) {List<Food> foodList;switch (comboName) {case "Combo1" : foodList = Arrays.asList(new MiniBurger(), new CornSalad(), new PepsiCola()); break;case "Combo2":foodList = Arrays.asList(new MexicanTwister(), new HotWing(), new PepsiCola());break;default:foodList = new ArrayList<>();}return foodList;}
}

3.8 過濾器模式

過濾器模式使用不同的條件過濾一組對象,并通過邏輯操作以解耦方式將其鏈接。這種類型的設計模式屬于結構模式,因為該模式組合多個標準以獲得單個標準。

3.8.1 細節

步驟
  1. 創建一個要過濾的普通類,要有獲得其私有屬性的get方法
  2. 創建一個接口,規定過濾方法
  3. 實現接口,可以依需要重寫過濾方法,參數傳遞的一般是存儲過濾類的容器類
  4. 復雜過濾類可以通過設置傳遞接口參數(復用其他基礎過濾類)來實現多重過濾
Java8

Java8中的lambda表達式可以更簡單的實現過濾器

List<Movie> movies = Stream.of(new Movie("大話西游","comedy"),new Movie("泰囧", "comedy"),new Movie("禁閉島", "suspense")).filter(var -> "comedy".equals(var.getType())).collect(Collectors.toList());

3.8.2 電影的例子

  1. 創建被過濾的類Movie,根據它的type屬性實現過濾
  2. 創建接口Criteria,規定過濾方法
  3. 創建喜劇電影過濾器ComedyMovieCriteria,根據comedy==movie.type來過濾出需要的喜劇電影
public class Test {public static void main(String[] args) {List<Movie> movies = new ArrayList(){{add(new Movie("大話西游","comedy"));add(new Movie("泰囧", "comedy"));add(new Movie("禁閉島", "suspense"));}};System.out.println(new ComedyMovieCriteria().meetCriteria(movies));}
}// 被篩選的對象
class Movie {private String name;// 電影類型private String type;public Movie(String name, String type) {this.name = name;this.type = type;}// getters & setters & toString
}// 過濾器接口
interface Criteria {/*** @param movies 要被篩選的電影* @return 篩選后的結果*/List<Movie> meetCriteria(List<Movie> movies);
}// 過濾喜劇電影的過濾器,要求是movie.type==comedy
class ComedyMovieCriteria implements Criteria {@Overridepublic List<Movie> meetCriteria(List<Movie> movies) {List<Movie> result = new ArrayList<>();for (Movie movie : movies) {if ("comedy".equals(movie.getType())) {result.add(movie);}}return result;}
}

3.9 橋接模式

橋接模式是將抽象化實現化解耦,使兩者可以獨立地變化。橋接模式有助于理解面向對象的設計原則,包括開閉原則以及組合聚合復用原則

3.9.1 橋接模式結構

橋接模式

這個系統含有兩個等級結構
  • 由抽象化角色和修正抽象化角色組成的抽象化等級結構。
  • 由實現化角色和兩個具體實現化角色所組成的實現化等級結構。
橋接模式所涉及的角色
  • 抽象化(Abstraction)角色:抽象化給出的定義,并保存一個對實現化對象的引用。
  • 修正抽象化(Refined Abstraction)角色:擴展抽象化角色,改變和修正父類對抽象化的定義。
  • 實現化(Implementor)角色:這個角色給出實現化角色的接口,但不給出具體的實現。必須指出的是,這個接口不一定和抽象化角色的接口定義相同,實際上,這兩個接口可以非常不一樣。實現化角色應該只給出底層操作,而抽象化角色應該只給出基于底層操作的更高一層的操作。
  • 具體實現化(Concrete Implementor)角色:這個角色給出實現化角色接口的具體實現。

3.9.2 細節

抽象化、實現化、解耦

抽象化:存在于多個實體中的共同的概念性聯系;通過忽略一些信息,把不同的實體當作相同的實體來對待。
實現化:抽象化給出的具體實現就是實現化。一個類的實例就是這個類的實現化,一個子類就是它超類的實現化。
解耦:耦合就是兩個實體的某種強關聯,把它們的強關聯去掉就是解耦。
強關聯與弱關聯:所謂強關聯,就是在編譯期已經確定的,無法在運行期動態改變的關聯;所謂弱關聯,就是可以動態地確定并且可以在運行期動態地改變的關聯。繼承是強關聯,而聚合關系是弱關聯

核心理解

橋接模式中的脫耦,就是在抽象化和實現化之間使用組合關系而不是繼承關系,從而使兩者可以相對獨立的變化。

優點
  1. 實現抽象化和實現化的分離。
  2. 提高了代碼的擴展能力。
  3. 實現細節對客戶透明。
缺點
  1. 橋接模式的引入會增加系統的理解與設計難度
  2. 由于聚合關聯關系建立在抽象層,要求開發者針對抽象進行設計與編程。
使用場景
  1. 如果一個系統需要在構件的抽象化角色和具體化角色之間增加更多的靈活性,避免在兩個層次之間建立靜態的繼承聯系,通過橋接模式可以使它們在抽象層建立一個關聯關系。
  2. 抽象化角色和實現化角色可以以繼承的方式獨立擴展而互不影響,在程序運行時可以動態將一個抽象化子類的對象和一個實現化子類的對象進行組合,即系統需要對抽象化角色和實現化角色進行動態耦合。
  3. 一個類存在兩個獨立變化的維度,且這兩個維度都需要進行擴展。
  4. 雖然在系統中使用繼承是沒有問題的,但是由于抽象化角色和具體化角色需要獨立變化,設計要求需要獨立管理這兩者。
  5. 對于那些不希望使用繼承或因為多層次繼承導致系統類的個數急劇增加的系統,橋接模式尤為適用
Java例子

大多數的驅動器(Driver)都是橋接模式的應用,使用驅動程序的應用系統就是抽象化角色,而驅動器本身扮演實現化角色。
JDBC驅動器

3.9.3 發送消息的案例

  • 下面案例中,SendMsg及其子類是按照發送消息的方式進行擴展的;而Send是按照發送消息的時間進行擴展的,兩者互不影響。
  • Send持有類一個SendMsg對象,并可以使用此對象的方法。
// Implementor角色
interface SendMsg {void sendMsg();
}// Concrete Implementor角色
class EmailSendMsg implements SendMsg {@Overridepublic void sendMsg() {System.out.println("Send Msg By Email");}
}// Concrete Implementor角色
class WeChatSendMsg implements SendMsg {@Overridepublic void sendMsg() {System.out.println("Send Msg By WeChat");}
}// Abstraction 角色
abstract class Send {protected SendMsg sendMsg;public Send(SendMsg sendMsg) {this.sendMsg = sendMsg;}public abstract void send();
}// Concrete Implementor角色
class ImmediatelySend extends Send {public ImmediatelySend(SendMsg sendMsg) {super(sendMsg);}@Overridepublic void send() {sendMsg.sendMsg();System.out.println("Send Msg Immediately");}
}// Concrete Implementor角色
class DelayedlySend extends Send {public DelayedlySend(SendMsg sendMsg) {super(sendMsg);}@Overridepublic void send() {sendMsg.sendMsg();System.out.println("Send Msg DelayedlySend");}
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/281447.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/281447.shtml
英文地址,請注明出處:http://en.pswp.cn/news/281447.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

最快的計算機操作,世界十大最快的超級計算機

最近&#xff0c;《聯邦儲備技術》雜志對全球超級計算機進行了排名&#xff0c;并從中選出了十個最快的超級計算機. 其中&#xff0c;中國有兩臺超級計算機進入了榜單&#xff0c;而“天河2號”則依靠雙精度浮點算術峰. 速度達到了每秒5490億次&#xff0c;占據了王位.這也是兩…

蘋果iOS 10.3.1修復博通Wi-Fi芯片重大安全漏洞

如果你還沒有將設備升級到 iOS 10.3.1 的話&#xff0c;那么現在是個機會了。因為不久前發布的 iOS 10.3.1&#xff0c;修復了 iPhone 中博通 Wi-Fi 芯片的一個重大安全漏洞&#xff0c;該安全漏洞可能會使在 Wi-Fi 范圍內的攻擊者在智能手機上注入并運行代碼。 Google Project…

計算機開機跳過硬盤檢查,如何設置開機跳過檢測硬盤

如何設置開機跳過檢測硬盤導讀&#xff1a;很多win用戶都想設置開機跳過檢測硬盤&#xff0c;能夠更加快速的進入系統&#xff0c;今天百分網小編給大家整理出了方法。1、系統的問題系統解決在Windows界面下&#xff0c;通過“磁盤碎片整理”程序來完成。首先啟動“CMD”鍵入“…

CLIENT系列、OFFSET系列、SCROLL系列

一、client系列 clientWidth/clientHeight 是我們設置的寬和高加上內邊距&#xff08;沒有邊框&#xff09; clientLeft/clientTop  就是我們設置的邊框值 二、offset系列 offsetWidth/offsetHeight  是我們設置的寬和高加上內邊距&#xff0c;加上邊框 offsetLeft/offs…

臺積電放大招:甩開英特爾 7nm和5nm芯片將誕生

北京時間1月20日消息&#xff0c;據科技網站AppleInsider報道&#xff0c;近幾年來臺積電的發展勢頭相當猛&#xff0c;該公司總裁兼聯合CEO劉德音(Mark Liu)在最近的投資者會議中表示&#xff0c;預計今年年末公司就將正式量產10nm晶圓。此外&#xff0c;臺積電7nm研發一如預期…

01: 實現注冊登錄功能

目錄&#xff1a;抽屜項目之js最佳實踐 01: 實現注冊登錄功能 02: 實現發布帖子功能 03: 將帖子展示到頁面、點贊 04: 層級評論 目錄&#xff1a; 1.1 顯示、隱藏 "登錄/注冊" 菜單1.2 注冊功能1.3 登錄功能1.4 獲取當前用戶數量1.1 顯示、隱藏 "登錄/注冊"…

計算機系統怎么算頁面大小,電腦網頁的設計尺寸是多少

電腦網頁的設計尺寸是多少剛入門的網頁設計師可能對電腦網頁的設計尺寸大小存在疑問&#xff0c;以下百分網小編整理的電腦網頁的設計尺寸&#xff0c;希歡迎閱讀!  對大于30W臺客戶端用戶進行測試&#xff0c;得到的測試數據如下(數據來源于網絡)&#xff1a;安全分辨率為10…

Array 的一些常用 API

unshift、push、shift、pop 這4個方法都會改變原數組unshift() 從前面添加元素&#xff0c;push() 從后面追加元素&#xff0c;返回數組長度shift() 刪除第0個元素&#xff0c;pop() 刪除最后一個元素&#xff0c;返回被刪除的元素slice 不改變原數組slice(m, n)返回原數組索引…

Ovum觀察:運營商通信PaaS發展趨勢強勁

近日在佛羅里達州奧蘭多市舉行的Genband公司Perspectives16會議上&#xff0c;一個首要議題是在通信領域應對OTT威脅。 這場會議的主旨側重于電信運營商和其他服務提供商如何應對日益嚴重的威脅&#xff0c;不過似乎這樣的討論有些晚了&#xff0c;因為許多大型運營商已經找到了…

WPF 托盤閃爍

WPF 托盤閃爍控件名&#xff1a;NotifyIcon作者&#xff1a;WPFDevelopersOrg - 弈虎、驚鏵原文鏈接&#xff1a; https://github.com/WPFDevelopersOrg/WPFDevelopers框架使用大于等于.NET40。Visual Studio 2022。項目使用 MIT 開源許可協議。接著上一篇基礎托盤。新增如下…

電機與系統計算機仿真作業,北郵計算機仿真期末大作業.docx

文檔介紹&#xff1a;EvaluationWarning:ThedocumentwascreatedwithSpire..I瑋涼爰p電大摩《計算機仿真》MATLAB課程設計報告學院: 自動化學院 專業: 自動化專業 班級: 2010211411姓名: 韓思宇 學號: 10212006 2013年01月14日轉子繞線機控制系統綜合仿真摘要:轉子繞線機的控制系…

Software License Manager

slmgr -ilc lenovo.xrm-ms slmgr -ipk lenovo-lenovo-lenovo-lenovo-lenovo轉載于:https://www.cnblogs.com/cuthead/p/license.html

Sqlserver數據庫還原一直顯示“正在還原…”解決方法

今天在Sqlerver還原數據庫時一直就卡在那里提示“正在還原…”的狀態&#xff0c;此時無法操作數據庫,下面我來給大家介紹解決些問題的方法解決方法&#xff1a; 代碼如下復制代碼 RESTORE DATABASE demo FROM DISK D:/demo.bak WITH MOVE demo TO d:/demo.mdf, MOVE dem…

Information Retrieval 倒排索引 學習筆記

一&#xff0c;問題描述 在Shakespeare文集&#xff08;有很多文檔Document&#xff09;中&#xff0c;尋找哪個文檔包含了單詞“Brutus”和"Caesar"&#xff0c;且不包含"Calpurnia"。這其實是一個查詢操作&#xff08;Boolean Queries&#xff09;。 在U…

計算機地址欄搜索不了網,我的電腦地址欄不見了怎么辦 地址欄不見了如何解決...

導語&#xff1a;小編對電腦是比較癡迷的&#xff0c;因此喜歡在自己的電腦上進行各種操作&#xff0c;也經常會碰到一些問題。今天要為大家介紹的是在我的電腦地址欄不見了之后怎么辦&#xff0c;熟悉電腦的朋友都能夠了解。在我的電腦主界面里面&#xff0c;有一個地址欄&…

實踐App內存優化:如何有序地做內存分析與優化

由于項目里之前線上版本出現過一定比例的OOM,雖然比例并不大&#xff0c;但是還是暴露了一定的問題&#xff0c;所以打算對我們App分為幾個步驟進行內存分析和優化&#xff0c;當然內存的優化是個長期的過程&#xff0c;不是一兩個版本的事&#xff0c;每個版本都需要收集線上內…

php OpenSSL 加解密

2018-1-6 17:10:19 星期六 1 $data 123456;2 $openssl_method AES-256-CBC;3 $openssl_iv_length openssl_cipher_iv_length($openssl_method);4 $openssl_iv openssl_random_pseudo_bytes($openssl_iv_length);5 $openssl_password openssl_random_pseudo_bytes(16);6 7 …

前端應該掌握的網絡知識(1)

1、客戶端&#xff1a;通過發送請求獲取服務器資源的web瀏覽器等。 2、TCP/IP協議族按層次分為&#xff1a;應用層、傳輸層、網絡層和數據鏈路層。 應用層決定了向用戶提供應用服務時通信的活動。比如&#xff1a;FTP&#xff08;文本傳輸協議&#xff09;和DNS&#xff08;域名…

WinForm(十四)窗體滾動日志

在桌面程序里&#xff0c;一般日志記錄到文件里就可以了&#xff0c;但有的時間&#xff0c;也需要在窗體上動態滾動顯示&#xff0c;這時&#xff0c;就需要引入日志框架了。這里引入的依舊是NLog&#xff08;在我的Mini API系統里&#xff0c;用的也是NLog&#xff09;。首先…

xp計算機找不到音量調節,WinXP電腦沒聲音且小喇叭不見了如何解決?

有用戶在使用電腦聽音樂的時候&#xff0c;突然發現電腦沒有聲音了&#xff0c;本來以為只是被禁了音&#xff0c;想著調節音量即可解決問題。但是當他想要點開音量小喇叭的時候&#xff0c;發現桌面任務欄通知區域的小喇叭不見了&#xff0c;這該怎么辦呢&#xff1f;下面小編…