一、目錄概要
二、問題探究
需求:假設要設計一個電腦商場管理系統的某個模塊設計,電腦分為品牌和類型兩個緯度,我們應該怎么解決?
按照初學者的思路,利用繼承就能簡單粗暴的實現,那我們來看下這種思路的設計類圖。
從電腦緯度划分
package com.aaron.bridge;
public interface Computer {
public void sale();
}
class Desktop implements Computer{
@Override
public void sale() {
System.out.println("台式電腦");
}
}
class Laptop implements Computer{
@Override
public void sale() {
System.out.println("筆記本電腦");
}
}
class Pad implements Computer{
@Override
public void sale() {
System.out.println("平板電腦");
}
}
從品牌緯度划分
package com.aaron.bridge;
// 宏碁品牌的三種類型
public class AcerDesktop extends Desktop{
@Override
public void sale() {
System.out.println("宏碁台式機");
}
}
class AcerLaptop extends Laptop{
@Override
public void sale() {
System.out.println("宏碁筆記本電腦");
}
}
class AcerPad extends Pad{
@Override
public void sale() {
System.out.println("宏碁平板電腦");
}
}
package com.aaron.bridge;
// 蘋果品牌的三種類型
public class AppleDesktop extends Desktop{
@Override
public void sale() {
System.out.println("蘋果台式機");
}
}
class AppleLaptop extends Laptop{
@Override
public void sale() {
System.out.println("蘋果筆記本電腦");
}
}
class ApplePad extends Pad{
@Override
public void sale() {
System.out.println("蘋果平板電腦");
}
}
package com.aaron.bridge;
//戴爾品牌的三種類型
public class DellDesktop extends Desktop{
@Override
public void sale() {
System.out.println("戴爾台式機");
}
}
class DellLaptop extends Laptop{
@Override
public void sale() {
System.out.println("戴爾筆記本電腦");
}
}
class DellPad extends Pad{
@Override
public void sale() {
System.out.println("戴爾平板電腦");
}
}
問題1:假設我們的系統按照上述思路設計,當我們新增一個品牌的時候,怎么辦?
根據上述思路,從品牌緯度擴展品牌時直接新增台式機、筆記本電腦、平板電腦的類即可。
問題2:當我們新增一個機器類型的時候又該怎么辦?
根據上述思路,在機器緯度類型擴展。
- 添加新的電腦機型。
- 在所有的品牌中,都新增新的機型。
package com.aaron.bridge;
// 電腦緯度
public interface Computer {
public void sale();
}
class Desktop implements Computer{
@Override
public void sale() {
System.out.println("台式電腦");
}
}
class Laptop implements Computer{
@Override
public void sale() {
System.out.println("筆記本電腦");
}
}
class Pad implements Computer{
@Override
public void sale() {
System.out.println("平板電腦");
}
}
class MaxPad implements Computer{
@Override
public void sale() {
System.out.println("超大屏電腦");
}
}
// 品牌緯度
public class DellDesktop extends Desktop{
@Override
public void sale() {
System.out.println("戴爾台式機");
}
}
class DellLaptop extends Laptop{
@Override
public void sale() {
System.out.println("戴爾筆記本電腦");
}
}
class DellPad extends Pad{
@Override
public void sale() {
System.out.println("戴爾平板電腦");
}
}
class DellPad extends Pad{
@Override
public void sale() {
System.out.println("戴爾平板電腦");
}
}
class DellMaxPad extends Pad{
@Override
public void sale() {
System.out.println("超大屏平板電腦");
}
}
思考:當我們添加少量的機型和品牌的時候,該方案的代碼擴展性還是可以接受的。但是,再往遠思考一步。
- 假設電腦商城,添加10個品牌,再添加10種機型,怎么辦?
- 系統已經上線,系統處於維護階段人力有限,怎么辦?
問題:上述問題主要是把電腦和品牌兩個緯度耦合。
- 違背單一職責原則(一個類只由一個維度影響)
- 違背開閉原則(對拓展開放,對修改關閉)。
解決:將機器、品牌兩個緯度進行拆分,不要將他們耦合。
三、解決思路
3.1 什么是橋接模式?
將兩個維度(抽象、實現)分離,使它們都可以獨立地變化。
3.2 橋接模式的分析
- 類型:台式機、筆記本電腦、平板電腦。
- 品牌:宏碁、蘋果、戴爾。
關鍵:將上述問題拆分成兩個緯度類型和品牌。我們剛剛也討論了,主要解決拓展性問題。當添加一個新的機器類型或者品牌的時候,不會對其他機器類型或者品牌產生影響。
概念:簡單談下概念,重點根據類圖和實現代碼去理解Abstraction、RefinedAbstraction、Implementor、ConcreteImplementor
Abstraction:抽象部分的接口。通常在這個對象里面,要維護一個實現部分的對象引用,在抽象對象里面的方法,需要調用實現部分的對象來完成。這個對象里面的方法,通常都是跟具體的業務相關的方法。
RefinedAbstraction:
擴展抽象部分的接口,通常在這些對象里面,定義跟實際業務相關的方法,這些方法的實現通常會使用Abstraction中定義的方法,也可能需要調用實現部分的對象來完成。
Implementor:
定義實現部分的接口,這個接口不用和Abstraction里面的方法一致(根據約定優於配置原則,建議跟Abstraction一致。),通常是由Implementor接口提供基本的操作,而Abstraction里面定義的是基於這些基本操作的業務方法,也就是說Abstraction定義了基於這些基本操作的較高層次的操作。
ConcreteImplementor:
真正實現Implementor接口的對象。
3.3 橋接模式實現
1、實體部分設計
package com.aaron.bridge;
/**
*
* @author xiaoyongAaron
* 品牌緯度-實現部分接口
*/
public interface ImplementorBrand {
public void sale();
}
class ConcreteImplementorAcer implements ImplementorBrand{
@Override
public void sale() {
System.out.println("宏碁品牌");
}
}
class ConcreteImplementorApple implements ImplementorBrand{
@Override
public void sale() {
System.out.println("蘋果品牌");
}
}
class ConcreteImplementorDell implements ImplementorBrand{
@Override
public void sale() {
System.out.println("戴爾品牌");
}
}
2、抽象部分設計
package com.aaron.bridge;
/**
* 機器類型緯度-抽象部分
* @author xiaoyongAaron
*/
public class AbstractionComputer {
protected ImplementorBrand brand;
public AbstractionComputer(ImplementorBrand brand){
this.brand=brand;
}
public void sale(){
brand.sale();
};
}
/**
* 擴展部分
* @author xiaoyongAaron
*/
class RefinedAbstractionDesktop extends AbstractionComputer{
public RefinedAbstractionDesktop(ImplementorBrand brand) {
super(brand);
}
@Override
public void sale() {
//添加自己的特性,實際業務。
paly();
super.sale();
}
public void paly(){
System.out.println("我台式機抗摔打");
}
}
class RefinedAbstractionLaptop extends AbstractionComputer{
public RefinedAbstractionLaptop(ImplementorBrand brand) {
super(brand);
}
@Override
public void sale() {
//添加自己的特性,實際業務。
lighting();
super.sale();
}
public void lighting(){
System.out.println("我筆記本電腦充電5分鍾,續航5小時");
}
}
class RefinedAbstractionPad extends AbstractionComputer{
public RefinedAbstractionPad(ImplementorBrand brand) {
super(brand);
}
@Override
public void sale() {
//添加自己的特性,實際業務。
weighting();
super.sale();
}
public void weighting(){
System.out.println("我平板電腦便於攜帶");
}
}
4、執行結果顯示
四、問題回顧
4.1 什么是橋接模式、為什么要橋接?
簡單說橋接模式就是把兩個緯度分離,所以說當我們在實際開發的時候,遇到兩個維度問題的時候,直接條件反射橋接模式。
就像上述問題,當有兩個維度(品牌+機器類型)賦予給一個類的時候,基於單一職責原則,需要把它們解耦。那通過上述范例可知,那么我們就需要一座橋一樣,把兩個緯度用一個中間物(類或者接口)把它們關聯起來,從而達到我們的目的。
4.2 橋接模式怎么接?
核心:如何把Implementor對象傳遞到抽象接口。
- 如上述描述,利用構造函數傳參。
- 創造無參構造函數,添加get、set方法。
- 工廠模式:參考設計模式的工廠模式。
- IOC控制反轉,最經典的就是Spring容器。內部的實現原理原本創建對象都是由我們自己管理,但是把這一步驟交給容器管理,就不用我們擔心了。例如在JDBC的設計當中,充當這個角色的就是DriverManage去把對象注入在抽象接口當中。
4.3 橋接模式本質和經驗
- 本質:抽象與實現(兩個緯度)分離。
- 多用對象組合(has-A),少用繼承。
- 開閉原則:我們應該對代碼拓展開放,拒絕代碼修改。
實際應用場景:
作者:碼農皮邱
原文:https://www.cnblogs.com/qiuyong/p/6357839.html
推薦:您的支持是對博主深入思考總結的最大鼓勵,要是有需要關注公眾號與作者面對面對話交流~
說明:本文版權歸作者所有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文鏈接,尊重作者的勞動成果。