源碼git地址 https://github.com/dlovetco/designMode
問題提出
不同品牌的手機各不相同。同一款軟件在不同手機上運行很有可能是不一樣的。要求用代碼實現這一種情況。
題目中提到了兩種不同的分類方式:一種是按品牌分類手機,然後對於每一個軟件都需要兩個實體類來表示不同手機運行情況不同;另外一種是先按照軟件種類分,然後再根據手機品牌分。這兩種分類方法都有很明顯的缺點,就是在種類過多時類的層次結構過於複雜。此外手機類和軟件類之間的耦合也極強。
在介紹橋接模式之前,我們先學習一個新的原則。
合成/聚合複用原則
儘量使用合成/聚合,儘量不要使用類繼承。
類繼承往往會在不知不覺中形成一顆龐大的類“家族”。而且後期對於這種類“家族”也非常不容易維護。若改動一個父類,他的無數個子類就會隨之改變,從而造成難以預知的錯誤。
橋接模式
將抽象部分與它的實現部分分離,使他們都可以獨立地變化。實際上就是說若系統有多中分類方式,則把這些分類方式都給分離出來讓他們獨自變化,減少它們之間的耦合性。
package bridgemode;
public class BridgeMode {
public static void main(String[] args) {
//安卓手機玩遊戲
Brand android = new AndroidPhone();
android.run(new Game());
//蘋果手機看電影
Brand apple = new ApplePhone();
apple.run(new VideoPlayer());
}
}
/**
* 手機品牌
*/
interface Brand {
//每個手機都需要運行軟件
void run(Software software);
}
class AndroidPhone implements Brand{
@Override
public void run(Software software) {
System.out.print("安卓手機");
software.run();
}
}
class ApplePhone implements Brand{
@Override
public void run(Software software) {
System.out.print("蘋果手機");
software.run();
}
}
/**
* 不同的軟件
*/
interface Software {
//不同手機軟件運行效果
void run();
}
class Game implements Software{
@Override
public void run() {
System.out.println("玩遊戲");
}
}
class VideoPlayer implements Software{
@Override
public void run() {
System.out.println("播放電影");
}
}
輸出:
安卓手機玩遊戲
蘋果手機播放電影
通過組合來代替繼承,我們就不需要實現特定的類(如看電影的安卓手機)。此外如果要新增一個手機品牌或者新增一個手機軟件,只需要加一個實現類而不會影響其他類。這一點也能很好的反映出開放封閉原則.
注:我把手機品牌和手機軟件兩種分類方式看作平級,所以使用的關係是引用。書中認爲手機包含了手機軟件,所以關係爲聚合。這點不同我覺得無傷大雅。
plantuml
@startuml
interface Brand{
{abstract}run(Software)
}
Brand <|.. AndroidPhone
class AndroidPhone{
run(Software)
}
Brand <|.. ApplePhone
class ApplePhone{
run(Software)
}
Software <-- Brand
interface Software{
{abstract}run()
}
Software <|.. Game
class Game{
run()
}
Software <|.. VideoPlayer
class VideoPlayer{
run()
}
@enduml