橋接模式
1 橋接模式
将抽象化與實作分離,使二者可以獨立的變化。
合成/聚合複用原則:面向對象設計的一個重要原則,盡量使用合成/聚合,不使用繼承。
《大話設計模式》中的例子:手機品牌下面派生了華為和蘋果等手機品牌, 華為又派生了華為的通信錄,華為的遊戲;蘋果又派生了蘋果的通信錄和遊戲。如果再加入新的品牌中興,還要再派生中興的通信錄和遊戲。而中興和華為的遊戲可能是一樣的。在這個案例中,手機品牌在變化,軟體也在變化,其實是兩個次元都在變化。如果用繼承的話,因為兩個次元都變化,會很麻煩。解決方案:隔離兩個次元變化之間的影響,手機品牌是接口,派生出各種手機品牌;手機軟體作為接口,各種軟體繼承它。手機品牌的一個成員是手機軟體,相當于聚合,這樣二者可以互相獨立變化。
上述方案中,表示和實作解耦,兩者可以獨立的變化,abstraction(手機品牌)中維護一個abstraction Impelemnt(軟體)的指針,需要采用不同的實作方式的時候隻需要傳入不同的執行個體就可以了。
适用場景:手機的品牌是變化的(不斷的有新品牌加入進來),手機的功能也變化(遊戲,通信錄,視訊,...),如果不分離的話,在原來的繼承體系上,每增加一個功能,或者每次增加一個品牌,都要加入好多類。比如我加入個小米手機,就需要加入各種小米的功能。 這種多個變化因素在多個對象之間共享,可以将這些變化的部分抽象出來再聚合進去。
優點: 依賴倒置; 将可以共享的部分,抽象出來,用聚合代替繼承。減少代碼重複。
缺點: 客戶必須清楚的知道選擇哪一種類型的實作。
注意:什麼時候用繼承,什麼時候用聚合? 看變化是幾維的,這裡有2個次元,品牌和功能,如果隻有一個次元,用繼承就可以。
2 例子1:
先來一個一般的例子(就是不好了解,不知所雲的例子),後面還有個不一般的例子。
一個畫闆的形狀可以是圓形的,正方形的,顔色可以是紅色,綠色。
class Implementor
{
public:
virtual void Show() {}
};
class Implementor1 : public Implementor
{
public:
virtual void Show()
{
XXXXXX;
}
};
class Implementor2 :public Implementor
{
public:
virtual void show()
{
XXXXXX
}
};
class Abstractor
{
Implementor * _implementor;
public:
virtual void SetImplementor(Implementor* ot)
{
this->_iplementor = ot;
}
virtual void operatoe()
{
_implementor->Show();
}
};
class RefindAbstractor: public Abstractor
{
};
int mian()
{
Implementor* im1 = new Implementor1();
Implementor2 * im2 = new Implementor2();
RefinedAbstractor *re = new ReefindeAbstractor();
re->SetImplementor(im1);
re->operate();
re->SetIpeementor(im2);
re->operate();
}
2 例子2:
(轉載自: javascript:void(0))
這是一個不一般的(比較通俗易懂的)例子,是以直接搬過來(發現人家是原創,圖檔上有名字,是以明确寫明是轉載。)
以前老是講繼承的時候最愛用畫圖說事,這裡有一個畫筆,可以畫正方形、長方形、圓形(熟悉的味道...)。但是現在我們需要給這些形狀進行上色,這裡有三種顔色:白色、灰色、黑色。這裡我們可以畫出3*3=9中圖形:白色正方形、白色長方形、白色圓形。。。。。。到這裡了我們幾乎到知道了這裡存在兩種解決方案:
方案一:為每種形狀都提供各種顔色的版本。(根據經驗,這一定四最Stupid方案)。
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsISPrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdsATOfd3bkFGazxCMx8VesATMfhHLlN3XnxCMwEzX0xiRGZkRGZ0Xy9GbvNGLpZTY1EmMZVDUSFTU4VFRR9Fd4VGdsYTMfVmepNHLrJXYtJXZ0F2dvwVZnFWbp1zczV2YvJHctM3cv1Ce-cmbw5SNxYGOyMGZ3gjNjljN0YGOzImMzQjYkJGZzIjY3MzNx8CXyEzLchDMxIDMy8CXn9Gbi9CXzV2Zh1WavwVbvNmLvR3YxUjLzM3Lc9CX6MHc0RHaiojIsJye.png)
假如我們添加橢圓,我們是不是又要增加三種顔色呢?假如我們在增加一個綠色,我們就要增加其四種形狀了,繼續加。繼續加……每次增加都會增加若幹個類(如果增加顔色則會增加形狀個數個類,若增加形狀則會增加顔色個數個類),這樣的情況我想每個程式員都不會想要吧!那麼我們看方案二。
方案二:根據實際需要對顔色和形狀進行組合。
提供兩個父類一個是顔色、一個形狀,顔色父類和形狀父類兩個類都包含了相應的子類,然後根據需要對顔色和形狀進行組合。
橋接模式将繼承關系轉化成關聯關系,它降低了類與類之間的耦合度,減少了系統中類的數量,也減少了代碼量。
首先是形狀類:該類為一個抽象類,主要提供畫形狀的方法:Shape.java
public abstract class Shape {
Color color;
public void setColor(Color color) {
this.color = color;
}
public abstract void draw();
}
然後是三個形狀 。圓形:Circle.java
public class Circle extends Shape{
public void draw() {
color.bepaint("正方形");
}
}
長方形:Rectangle.java
public class Rectangle extends Shape{
public void draw() {
color.bepaint("長方形");
}
}
正方形:Square.java
public class Square extends Shape{
public void draw() {
color.bepaint("正方形");
}
}
顔色接口:Color.java
public interface Color {
public void bepaint(String shape);
}
白色:White.java
public class White implements Color{
public void bepaint(String shape) {
System.out.println("白色的" + shape);
}
}
灰色:Gray.java
public class Gray implements Color{
public void bepaint(String shape) {
System.out.println("灰色的" + shape);
}
}
黑色:Black.java
public class Black implements Color{
public void bepaint(String shape) {
System.out.println("黑色的" + shape);
}
}
用戶端:Client.java
public class Client {
public static void main(String[] args) {
//白色
Color white = new White();
//正方形
Shape square = new Square();
//白色的正方形
square.setColor(white);
square.draw();
//長方形
Shape rectange = new Rectangle();
rectange.setColor(white);
rectange.draw();
}
}
3 例子3:
最後,看一下《大話設計模式》中多個手機品牌上面執行多個軟體的例子吧:
首先定義一個不同軟體的抽象接口。
public interface Software {
public void run();
}
public class AppStore implements Software {
@Override
public void run() {
System.out.println("run app store");
}
}
public class Camera implements Software {
@Override
public void run() {
System.out.println("run camera");
}
}
手機品牌的抽象接口:注意,裡面包含了一個Software 的引用。
public abstract class Phone {
protected Software software;
public void setSoftware(Software software){
this.software = software;
}
public abstract void run();
}
public class Oppo extends Phone {
@Override
public void run() {
software.run();
}
}
public class Vivo extends Phone {
@Override
public void run() {
software.run();
}
}