1、简介
将抽象部分与它的实现部分分离,使它们都可以独立地变化。
一个维度可以认为是抽象部分,另一个维度可以认为是实现部分,而这两个维度可以独立扩充和维护。
桥接模式中的角色:
抽象化角色(Abstraction):定义抽象类的接口,一般为抽象类,规范RefinedAbstraction,并保存一个对实现化对象(Implementor)的引用。主要靠这个类进行桥接
修正抽象化角色(Refined Abstraction):Abstraction的子类,具体实现Abstraction里规定的方法。
实现化角色(Implementor):这个角色给出实现化角色的接口,但不给出具体的实现。必须指出的是,这个接口不一定和抽象化角色的接口定义相同,实际上,这两个接口可以非常不一样。实现化角色应当只给出底层操作,而抽象化角色应当只给出基于底层操作的更高一层的操作。
具体实现化角色(Concrete Implementor):Implementor的子类,这个角色给出实现化角色接口的具体实现。
优点:
1、把抽象接口与其实现解耦。
2、抽象和实现可以独立扩展,不会影响到对方。
3、实现细节对客户透明,对用于隐藏了具体实现细节。
缺点:
1、增加了系统的复杂度
2、使用场景
1、如果一个系统需要在构件的抽象化角色和具体化角色之间添加更多的灵活性,避免在两个层次之间建立静态的联系。
2、设计要求实现化角色的任何改变不应当影响客户端,或者实现化角色的改变对客户端是完全透明的。
3、需要跨越多个平台的图形和窗口系统上。
4、 一个类存在两个独立变化的维度,且两个维度都需要进行扩展。
3、举例说明:
你现在有个需求,你需要把用户保存的图片经过系统映射为可直接访问的URL链接供前端直接根据路径调用显示,但是在不同平台的物理路径获取的结果不同,例如你的图片放在本地服务器的D盘下的Images文件夹下,在Windows下物理路径为D://Images/具体图片名称,在Linux下就是/Images/具体图片名称。两者是不一致的。一般人的写法如下:
接口:
/// <summary>/// 定义不同平台的映射方法/// </summary>public interface IMyPlatform{void WinRun(string path);void LinuxRun(string path);}
实现:
public class MyPlatform : IMyPlatform
{public void LinuxRun(string path){Console.WriteLine($"Linux平台的详细映射逻辑");}public void WinRun(string path){Console.WriteLine($"Win平台的详细映射逻辑");}
}
判断当前系统环境并进行映射
private void WTBtn_Click(object sender, EventArgs e)
{string hj = "Linux";IMyPlatform softwares =new MyPlatform();if (hj == "Linux")//判断是不是Linux环境{string path = "";//从配置文件取出具体地址,或者程序变量定义的地址softwares.LinuxRun(path);}else{string path = "";softwares.WinRun(path);}
}
按照这样即可完成该功能,但是如果后续布置在IOS系统或者其他系统呢?这个时候是不是要修改接口和实现类。这样并不满足开闭原则,对程序的维护也十分不利。
这个时候就得换种思路,保证增加接口的同时不会影响到实现类的使用,使抽象和实现解耦,互不干预。这时候就得使用桥接模式了。
之前我们是仅有一个映射地址的操作,仅有这一个维度,这也是底层核心维度。使用桥接模式我们不妨把路径获取也看作一个维度,其是基于地址映射维度的前置维度,客户端调用也是根据不同平台的地址不同来进行映射的,那么我们就暂且把地址映射看作下层维度(实现化角色),地址获取看作上层维度(抽象化角色)来实现桥接。
实现化角色(Implementor):
/// <summary>
/// 实现化角色---接口(Implementor)
/// </summary>
public interface IMySoftwares
{/// <summary>/// 映射地址的操作/// </summary>/// <param name="path"></param>void Mapp(string path);
}
具体实现化角色(Concrete Implementor):
/// <summary>
/// 实现角色---具体实现
/// </summary>
public class LinuxSoftwares : IMySoftwares
{public void Mapp(string path){Console.WriteLine($"Linux平台的详细映射逻辑");}
}
/// <summary>
/// 实现角色---具体实现
/// </summary>
public class WinSoftwares : IMySoftwares
{ public void Mapp(string path){Console.WriteLine($"Win平台的详细映射逻辑");}
}
抽象化角色(Abstraction):
/// <summary>
/// 抽象角色--抽象类。。也是主要的桥接类
/// </summary>
public abstract class IMyPlatform
{public IMySoftwares Softwares;public IMyPlatform(IMySoftwares Softwares){this.Softwares = Softwares;}public abstract void GetPath();
}
修正抽象化角色(Refined Abstraction):
/// <summary>/// 抽象角色--实现/// </summary>public class LinuxPlatform : IMyPlatform{public LinuxPlatform(IMySoftwares Softwares) : base(Softwares){}public override void GetPath(){Console.WriteLine($"Linux平台获取路径");Softwares.Mapp("");//路径获取后映射}}/// <summary>/// 抽象角色--实现/// </summary>public class WinPlatform : IMyPlatform{public WinPlatform(IMySoftwares Softwares) : base(Softwares){}public override void GetPath(){Console.WriteLine($"Win平台获取路径");Softwares.Mapp("");//路径获取后映射}}
调用
private void WTBtn_Click(object sender, EventArgs e)
{string hj = "Linux";if (hj == "Linux")//判断是不是Linux环境{LinuxSoftwares linuxSoftwares = new LinuxSoftwares();//下层维度,核心逻辑--看作实现IMyPlatform platform=new LinuxPlatform(linuxSoftwares);//上层维度--看作接口platform.GetPath();}else{WinSoftwares winSoftwares = new WinSoftwares();IMyPlatform platform = new WinPlatform(winSoftwares);platform.GetPath();}
}
使用桥接模式后你再需要增加IOS平台的获取路径方式仅需要 再实现一个修正抽象化角色(Refined Abstraction)即可,无需修改其他,符合开闭原则
/// <summary>
/// 抽象角色--实现
/// </summary>
public class IOSPlatform : IMyPlatform
{public IOSPlatform(IMySoftwares Softwares) : base(Softwares){}public override void GetPath(){Console.WriteLine($"IOS平台获取路径");Softwares.Mapp("");}
}
再需要增加IOS平台的路径映射方式仅需要 再实现一个具体实现化角色(Concrete Implementor)即可
/// <summary>/// 实现角色---具体实现/// </summary>public class IOSSoftwares : IMySoftwares{public void Mapp(string path){Console.WriteLine($"IOS平台的详细映射逻辑");}}
由此可见,不管是修改实现部分(下层维度)还是抽象部分(上层维度)均不会互相影响,也就满足其核心:将抽象部分与它的实现部分分离,使它们都可以独立地变化。
看到这肯定有部分人思考桥接模式与适配器模式似乎差不了太大,我这简要说一下区别:
共同点
桥接和适配器都是让两个东西配合工作
不同点
出发点不同。
适配器:改变已有的两个接口,让他们相容。
桥接模式:分离抽象化和实现,使两者的接口可以不同,目的是分离。
所以说,如果你拿到两个已有模块,想让他们同时工作,那么你使用的适配器。
如果你还什么都没有,但是想分开实现,那么桥接是一个选择。
桥接是先有桥,才有两端的东西
适配是先有两边的东西,才有适配器
桥接是在桥好了之后,两边的东西还可以变化。
END-----------------------------------------------------------------------------------------------------------------------