Bridge桥接模式.ppt
- 格式:ppt
- 大小:768.01 KB
- 文档页数:31
桥接模式(Bridge)1 场景问题1.1 发送提示消息考虑这样一个实际的业务功能:发送提示消息。
基本上所有带业务流程处理的系统都会有这样的功能,比如某人有新的工作了,需要发送一条消息提示他。
从业务上看,消息又分成普通消息、加急消息和特急消息多种,不同的消息类型,业务功能处理是不一样的,比如加急消息是在消息上添加加急,而特急消息除了添加特急外,还会做一条催促的记录,多久不完成会继续催促。
从发送消息的手段上看,又有系统内短消息、手机短消息、邮件等等。
现在要实现这样的发送提示消息的功能,该如何实现呢?1.2 不用模式的解决方案1:实现简化版本先考虑实现一个简单点的版本,比如:消息先只是实现发送普通消息,发送的方式呢,先实现系统内短消息和邮件。
其它的功能,等这个版本完成过后,再继续添加,这样先把问题简单化,实现起来会容易一点。
(1)由于发送普通消息会有两种不同的实现方式,为了让外部能统一操作,因此,把消息设计成接口,然后由两个不同的实现类,分别实现系统内短消息方式和邮件发送消息的方式。
此时系统结构如图1所示:图1 简化版本的系统结构示意图下面看看大致的实现示意。
(2)先来看看消息的统一接口,示例代码如下:Java代码1./**2. * 消息的统一接口3. */4.public interface Message {5. /**6. * 发送消息7. * @param message 要发送的消息内容8. * @param toUser 消息发送的目的人员9. */10. public void send(String message,String toUser);11.}(3)再来分别看看两种实现方式,这里只是为了示意,并不会真的去发送Email 和站内短消息,先看站内短消息的方式,示例代码如下:Java代码1./**2. * 以站内短消息的方式发送普通消息3. */4.public class CommonMessageSMS implements Message{5. public void send(String message, String toUser) {6. System.out.println("使用站内短消息的方式,发送消息'"7.+message+"'给"+toUser);8. }9.}同样的,实现以Email的方式发送普通消息,示例代码如下:Java代码1./**2. * 以Email的方式发送普通消息3. */4.public class CommonMessageEmail implements Message{5. public void send(String message, String toUser) {6. System.out.println("使用Email的方式,发送消息'"7.+message+"'给"+toUser);8. }9.}2:实现发送加急消息上面的实现,看起来很简单,对不对。
桥接模式(Bridge、Implementor)(具体不同平台⽇志记录,抽象与实现分离)桥接模式(Bridge Pattern):将抽象部分与它的实现部分分离,使它们都可以独⽴地变化。
它是⼀种对象结构型模式,⼜称为柄体(Handle and Body)模式或接⼝(Interface)模式。
假如我们需要建⽴⼀个⽇志记录器,可以实现数据库记录和⽂本记录。
根据我们的经验我们应该将⽇志记录抽象化,在分别⽤数据库记录和⽂本记录来实现具体的功能。
后来我们需要将他分别应⽤到net平台和java平台,考虑到不同平台的记录不同我们分别将数据库记录细化为net平台上的数据库机记录和java平台上的数据库记录。
以及不同的⽂本记录。
在后来我们打算添加xml平台以及更多的平台进去。
现在发现我们的设计变得庞⼤脆弱,即便平台不同但总有相似之处,这其中的冗余代码会有很多。
假如我们不⽌沿着平台这⼀⽅向扩展呢。
因此我们需要使⽤桥接模式进⾏更加合理的设计。
桥接模式将抽象部分与他的实现部分分离,使他们都可以独⽴的变化。
抽象化:多个实体中共同的概念性联系。
如:⽇志记录实现化:抽象化的具体实现。
如:在Net平台上进⾏数据库⽇志记录。
我们可以进⾏如下设计:桥接类:抽象⽇志1public abstract class log {2private ImpLog impLog;34public log(ImpLog impLog) {5super();6this.impLog = impLog;7 }89abstract public void write(String log);1011public ImpLog getImpLog() {12return impLog;13 }1415public void setImpLog(ImpLog impLog) {16this.impLog = impLog;17 }18 }实现化⾓⾊:抽象平台1public abstract class ImpLog {23abstract public void execute(String msg);45 }具体实现化⾓⾊:java平台1public class JImpLog extends ImpLog{23 @Override4public void execute(String msg) {5 System.out.println("在java平台上," + msg);6 }78 }net平台1public class NImpLog extends ImpLog{23 @Override4public void execute(String msg) {5 System.out.println("在Net平台上,"+msg);67 }89 }桥接具体类:数据库⽇志:public class DataBaseLog extends log{public DataBaseLog(ImpLog impLog) {super(impLog);}@Overridepublic void write(String log) {getImpLog().execute("数据库⽇志:" + log);}}⽂本⽂件⽇志1public class TextFileLog extends log{23public TextFileLog(ImpLog impLog) {4super(impLog);5 }67 @Override8public void write(String log) {9 getImpLog().execute(log);10 }1112 }客户测试类:1public class BridgePattern {23public static void main(String[] args) {4 ImpLog j= new JImpLog();//建⽴java平台5 log jl = new DataBaseLog(j);//建⽴基于java 的数据库⽇志写⼊6 jl.write(" I am fine!"); //写⼊⽇志⽂件789 }1011 }。
设计模式之桥接(bridge)模式 在现实⽣活中,我们常常会⽤到两种或多种类型的笔,⽐如⽑笔和蜡笔。
假设我们需要⼤、中、⼩三种类型的画笔来绘制12中不同的颜⾊,如果我们使⽤蜡笔,需要准备3*12=36⽀。
但如果使⽤⽑笔的话,只需要提供3种型号的⽑笔,外加12个颜料盒即可,涉及的对象个数仅为3+12=15,远远⼩于36却能实现与36⽀蜡笔同样的功能。
如果需要新增⼀种画笔,并且同样需要12种颜⾊,那么蜡笔需要增加12⽀,⽽⽑笔却只需要新增1⽀。
通过分析,在蜡笔中,颜⾊和型号两个不同的变化维度耦合在⼀起,⽆论对其中任何⼀个维度进⾏扩展,都势必会影响另外⼀个维度。
但在⽑笔中,颜⾊和型号实现了分离,增加新的颜⾊或者型号都对另外⼀⽅没有任何影响。
在软件系统中,有些类型由于⾃⾝的逻辑,它具有两个或多个维度的变化。
为了解决这种多维度变化,⼜不引⼊复杂度,这就要使⽤今天介绍的Bridge桥接模式。
⼀、跨平台的图像浏览系统1.1 需求介绍M公司开发部想要开发⼀个跨平台的图像浏览系统,要求该系统能够显⽰JPG、BMP、GIF、PNG等多种格式的⽂件,并且能够在Windows、Linux以及Unix等多个操作系统上运⾏。
该系统⾸先将各种格式的⽂件解析为像素矩阵(Matrix),然后将像素矩阵显⽰在屏幕上,在不同的操作系统中可以调⽤不同的绘制函数来绘制像素矩阵。
该系统需要具备较好的扩展性以⽀持新的⽂件格式和操作系统。
1.2 初始设计 M公司开发部的程序猿针对需求,⽴马提出了⼀个初始的设计⽅案,其基本结构如下图所⽰: 通过对这个设计⽅案的分析,发现存在以下两个主要问题: (1)由于采⽤了多重继承结构,导致系统中类的个数急剧增加,系统中类的个数达到了17个。
(2)系统扩展⿇烦,由于每⼀个具体类既包括图像⽂件格式信息,⼜包含操作系统信息,因此⽆论增加新图像⽂件格式还是新的操作系统,都需要增加⼤量的具体类。
这将导致系统变得⾮常庞⼤,增加运⾏和维护开销。
注意:做无线桥接需要路由器支持,最好是同品牌的路由器这样稳定性好一点。
今天介绍下水星Mercury无线路由器桥接Bridge设置图解,其他路由器大同小异。
首先分别登录两台路由器获取路由器LAN口的MAC地址并记录下来。
方法:登录路由
器在运行状态中的“LAN口状态”中获取。
未设置桥接前如果用无线登录时可能有冲突只能登
录一个,建议可以用有线设置,也可以给路由器分别断电用无线设置。
路由器管理界面——无线参数——基本设置勾选“开启Bridge功能”在AP1的MAC地
址中输入之前记录的对方LAN口MAC地址(两台路由器同样操作)。
在频段和模式选项中
两台路由器须一致否则无法连接。
登录副路由器(没有连接互联网的路由器)网络参数——LAN口设置在IP地址中输入
不与主路由器相同的IP地址。
副路由器:DHCP服务选择关闭,一个网络中最好只有一个DHCP服务器。
设置完成后分别重启两台路由器就可以了。
测试方法是:连接其中一台路由器后PING另一台路由器,通则表明设置成功。
已经通过PPPOE能正常上网,目的,我样想通过功能差不多一样的B路由器把信号传递的更远。
先说A 的配置WAN口pppoe 连接猫,拨号上网,设置自动连接LAN :IP保持不变192.168.3.1无线配置:SSID:AP 无线频段设:6 (以上仅供参考,可以自己设置。
两个路由的SSID可以相同也可不同,如果相同,笔记本才可以漫游。
但无线频段必须相同),开启bridge功能,AP1中填入B的无线MAC地址,安全设置选择WEP(说明书上写的,如果桥接只支持WEP方式),两个路由器设置的加密方式和密码必须一致。
启用DHCP再说B的配置WAN口默认不变LAN:IP 192.168.3.2 (分的开点,以免冲突)无线配置:SSID:AP 无线频段设:6, 开启bridge功能,AP1中填入A的无线MAC地址(地址在路由器底部也可以看到)。
安全设置和A设置成一样的禁用DHCP,以免发生冲突。
这样是通过B的LAN口或者是无线连入(笔记本无线接入选择AP,密码就是你在安全里面设置的密码,两台都一样),都能从A获得IP地址,可以ping通A,可以共享上网。
(你想像成两个路由器的LAN口被一根网线连起来啦)网上还有一对多的桥接那么A的AP1,AP2,AP3…分别填上这些路由器的无线MAC,而这些路由器的AP1中填上A的无线MAC。
下面是TP-LINK官方的一些建议:选择设备:为了达到最好的兼容性,请选择同一品牌的无线产品配套使用。
频段设置:桥接的路由器无线频段必须相同,建议手动设置以保证频段相同。
SSID设置:无线路由器所设置的SSID可以不同,此时客户端在此网络中不能无线漫游,要想实现无线漫游需设置相同的SSID。
(这是什么意思呢,就是说如果设置成相同的SSID,你的笔记本在无线网络中只看得到一个SSID,而且你在移动时会自动切换,不用你靠谁近的时候,再在无线网络中去选择这个)。
C++设计模式-Bridge桥接模式作⽤:将抽象部份与它的实现部份分离,使它们都可以独⽴地变化。
将抽象(Abstraction)与实现(Implementation)分离,使得⼆者可以独⽴地变化。
桥接模式号称设计模式中最难理解的模式之⼀,关键就是这个抽象和实现的分离⾮常让⼈奇怪,⼤部分⼈刚看到这个定义的时候都会认为实现就是继承⾃抽象,那怎么可能将他们分离呢。
《⼤话设计模式》中就Bridge模式的解释:⼿机品牌和软件是两个概念,不同的软件可以在不同的⼿机上,不同的⼿机可以有相同的软件,两者都具有很⼤的变动性。
如果我们单独以⼿机品牌或⼿机软件为基类来进⾏继承扩展的话,⽆疑会使类的数⽬剧增并且耦合性很⾼,(如果更改品牌或增加软件都会增加很多的变动)两种⽅式的结构如下:所以将两者抽象出来两个基类分别是PhoneBrand和PhoneSoft,那么在品牌类中聚合⼀个软件对象的基类将解决软件和⼿机扩展混乱的问题,这样两者的扩展就相对灵活,剪短了两者的必要联系,结构图如下:这样扩展品牌和软件就相对灵活独⽴,达到解耦的⽬的!UML结构图如下:抽象基类及接⼝:1、Abstraction::Operation():定义要实现的操作接⼝2、AbstractionImplement::Operation():实现抽象类Abstaction所定义操作的接⼝,由其具体派⽣类ConcreteImplemenA、ConcreteImplemenA或者其他派⽣类实现。
3、在Abstraction::Operation()中根据不同的指针多态调⽤AbstractionImplement::Operation()函数。
理解:Bridge⽤于将表⽰和实现解耦,两者可以独⽴的变化.在Abstraction类中维护⼀个AbstractionImplement类指针,需要采⽤不同的实现⽅式的时候只需要传⼊不同的AbstractionImplement派⽣类就可以了.Bridge的实现⽅式其实和Builde⼗分的相近,可以这么说:本质上是⼀样的,只是封装的东西不⼀样罢了.两者的实现都有如下的共同点:抽象出来⼀个基类,这个基类⾥⾯定义了共有的⼀些⾏为,形成接⼝函数(对接⼝编程⽽不是对实现编程),这个接⼝函数在Buildier中是BuildePart函数在Bridge中是Operation函数;其次,聚合⼀个基类的指针,如Builder模式中Director类聚合了⼀个Builder基类的指针,⽽Brige模式中Abstraction类聚合了⼀个AbstractionImplement基类的指针(优先采⽤聚合⽽不是继承);⽽在使⽤的时候,都把对这个类的使⽤封装在⼀个函数中,在Bridge中是封装在Director::Construct函数中,因为装配不同部分的过程是⼀致的,⽽在Bridge模式中则是封装在Abstraction::Operation函数中,在这个函数中调⽤对应的AbstractionImplement::Operation函数.就两个模式⽽⾔,Builder封装了不同的⽣成组成部分的⽅式,⽽Bridge封装了不同的实现⽅式.桥接模式就将实现与抽象分离开来,使得RefinedAbstraction依赖于抽象的实现,这样实现了依赖倒转原则,⽽不管左边的抽象如何变化,只要实现⽅法不变,右边的具体实现就不需要修改,⽽右边的具体实现⽅法发⽣变化,只要接⼝不变,左边的抽象也不需要修改。
桥接模式⼿机操作问题现在对不同⼿机类型、不同品牌的⼿机实现操作编程(⽐如:开机、关机、上⽹,打电话等)传统⽅式实现类图:传统⽅案解决⼿机操作问题分析:1. 扩展性问题,如果我们再增加⼿机的样式(旋转式),就需要增加各个品牌⼿机的类,同样如果我们增加⼀个⼿机品牌,也要在各个⼿机样式类下增加2. 违反了单⼀职责原则,当我们增加⼿机样式时,要同时增加所有品牌的⼿机,这样增加了代码维护成本3. 解决⽅案 --> 将⼿机的品牌和样式进⾏⽔平分割,并使⽤桥梁沟通,即使⽤桥接模式1. 桥接模式的基本介绍1. 桥接模式(Bridge模式)是指:将实现与抽象放在两个不同的类层次中,使两个层次可以独⽴改变,桥接模式是⼀种结构型设计模式2. Bridge模式基于类的最⼩设计原则,通过使⽤封装、聚合及继承等⾏为让不同的类承担不同的职责。
它的主要特点是把抽象(Abstraction)与⾏为实现(Implementation)分离开来,从⽽可以保持各部分的独⽴性以及应对他们的功能扩展桥接模式(Bridge)-原理类图1. Client:桥接模式的调⽤者2. Abstraction:抽象类,Abstraction 中维护了⼀个 Implementor 实现类的实例(聚合关系),Abstraction 充当桥接类3. RefinedAbstraction:Abstraction 的具体实现类,已经就与Implementor实现类桥接起来了4. Implementor:定义⾏为的接⼝5. ConcreteImplementor:Implementor 的具体实现类桥接模式和传统⽅式的对⽐1. 根据上⾯所述, 我们发现,在传统的⽅式中, ⼀个具体类的特性,都需要⾃⼰去通过继承体现,类的关系由继承完成,这样在类的数量上就成⼏何式增长,若品牌为三个,类型为四个,若想完整的实现所有的情况,就需要定义⼗⼆个类,2. 若使⽤上述描述中的桥接思想,我们只需将品牌和类型都以具体实现类的形式定义,再定义⼀个桥接类,分别持有品牌和类型属性. 对于不同的组合只需设置不同的属性即可2. 代码实现使⽤桥接模式改进传统⽅式,让程序具有更好的扩展性,利⽤程序维护类图:1. Brand:规定各个品牌⼿机的⾏为 ,和各个品牌的实现类//接⼝public interface Brand {void open();void close();void call();}public class XiaoMi implements Brand {@Overridepublic void open() {System.out.println(" ⼩⽶⼿机开机 ");}@Overridepublic void close() {System.out.println(" ⼩⽶⼿机关机 ");}@Overridepublic void call() {System.out.println(" ⼩⽶⼿机打电话 ");}}public class Vivo implements Brand {@Overridepublic void open() {System.out.println(" Vivo⼿机开机 ");}@Overridepublic void close() {System.out.println(" Vivo⼿机关机 ");}@Overridepublic void call() {System.out.println(" Vivo⼿机打电话 ");}}2. Phone:电话的抽象类,在该类中聚合了⼀个 Brand 接⼝的具体实现类 ,充当桥接的作⽤public abstract class Phone {// 组合品牌private Brand brand;// 构造器public Phone(Brand brand) {this.brand = brand;}protected void open() {this.brand.open();}protected void close() {this.brand.close();}protected void call() {this.brand.call();}}3. 继承抽象⽗类 Phone的各个款式的⼿机, 对抽象⽗类中的⽅法进⾏重写//折叠式⼿机类,继承抽象类 Phonepublic class FoldedPhone extends Phone {// 构造器public FoldedPhone(Brand brand) {super(brand);}@Overridepublic void open() {super.open();System.out.println(" 折叠样式⼿机 ");}@Overridepublic void close() {super.close();System.out.println(" 折叠样式⼿机 ");}@Overridepublic void call() {super.call();System.out.println(" 折叠样式⼿机 ");}}public class UpRightPhone extends Phone {// 构造器public UpRightPhone(Brand brand) {super(brand);}@Overridepublic void open() {super.open();System.out.println(" 直⽴样式⼿机 ");}@Overridepublic void close() {super.close();System.out.println(" 直⽴样式⼿机 ");}@Overridepublic void call() {super.call();System.out.println(" 直⽴样式⼿机 ");}}4. 客户端,可以看到,使⽤桥接模式可以轻松地组合出不同⼿机类型、不同品牌的⼿机public class Client {public static void main(String[] args) {// 折叠式的⼩⽶⼿机 (样式 + 品牌 )Phone phone1 = new FoldedPhone(new XiaoMi());phone1.open();phone1.call();phone1.close();System.out.println("=======================");// 折叠式的Vivo⼿机 (样式 + 品牌 )Phone phone2 = new FoldedPhone(new Vivo());phone2.open();phone2.call();phone2.close();System.out.println("==============");// 直⽴式的⼩⽶⼿机 (样式 + 品牌 )UpRightPhone phone3 = new UpRightPhone(new XiaoMi());phone3.open();phone3.call();phone3.close();System.out.println("==============");// 直⽴式的Vivo⼿机 (样式 + 品牌 )UpRightPhone phone4 = new UpRightPhone(new Vivo());phone4.open();phone4.call();phone4.close();}}总结:1. 可以看出,我们将品牌做成各个具体的实现类,使⽤Phone 作为桥梁,可以持有不同的品牌2. 因为此案例中只有两个属性,所以我们将款式的表⽰,体现为继承的⽅式 ,不同的款式,建⽴不同的类,继承抽象桥梁类Phone,也就可以持有不同的品牌了3. 若此时我们加了⼀个品牌或者加了⼀个款式,只需加⼀个类即可3. JDBC 源码分析Jdbc 的 Driver接⼝,如果从桥接模式来看, Driver就是⼀个接⼝(⾏为规范),下⾯可以有MySQL的Driver, Oracle的Driver,这些就可以当做实现接⼝类类图:1. 对于java定义的Connection 接⼝,不同的数据库有其相应的实现,Mysql Oracle 等2. 客户端通过桥接对象 DriverManager 去聚合各个数据源的实现类, 可以通过⽅法getConnection 获取@CallerSensitivepublic static Connection getConnection(String url,java.util.Properties info) throws SQLException {return (getConnection(url, info, Reflection.getCallerClass()));}4. 桥接模式的注意事项和细节1. 实现了抽象和实现部分的分离,从⽽极⼤的提供了系统的灵活性,让抽象部分和实现部分独⽴开来,这有助于系统进⾏分层设计,从⽽产⽣更好的结构化系统。
设计模式(08):结构型模式(⼆)桥接模式(Bridge)⼀、动机(Motivation)在很多游戏场景中,会有这样的情况:【装备】本⾝会有的⾃⼰固有的逻辑,⽐如枪⽀,会有型号的问题,同时现在很多的游戏⼜在不同的介质平台上运⾏和使⽤,这样就使得游戏的【装备】具有了两个变化的维度——⼀个变化的维度为“平台的变化”,另⼀个变化的维度为“型号的变化”。
如果我们要写代码实现这款游戏,难道我们针对每种平台都实现⼀套独⽴的【装备】吗?复⽤在哪⾥?如何应对这种“多维度的变化”?如何利⽤⾯向对象技术来使得【装备】可以轻松地沿着“平台”和“型号”两个⽅向变化,⽽不引⼊额外的复杂度?⼆、意图(Intent)将抽象部分与实现部分分离,使它们都可以独⽴地变化。
桥模式不能只是认为是抽象和实现的分离,它其实并不仅限于此。
其实两个都是抽象的部分,更确切的理解,应该是将⼀个事物中多个维度的变化分离。
三、结构(Structure)其中imp的地⽅就是⼀个组合。
Abstraction就是我们例⼦中的Tank,它的⼦类RefinedAbstraction就是T50等型号。
Implementor是TankPlatformImplementation类,ConcreteImplementorA和ConcreteImplementorB分别是PCTankImplementation和MobileTankImplementation。
整个设计模式的关键就是组合的使⽤。
四、模式的组成桥接模式的结构包括Abstraction、RefinedAbstraction、Implementor、ConcreteImplementorA和ConcreteImplementorB五个部分,其中:(1)、抽象化⾓⾊(Abstraction):抽象化给出的定义,并保存⼀个对实现化对象(Implementor)的引⽤。
(2)、修正抽象化⾓⾊(Refined Abstraction):扩展抽象化⾓⾊,改变和修正⽗类对抽象化的定义。
Bridge-AP模式和Bridge-Sriation模式互通
1.设备供电与本地连接相通
2.修改ip地址与设备相同网段
3.
4.通过游览器访问设备IP地址进行配置管理
5.
6.配置设备工作模式(主网桥配置成ap模式)
7.
8.选择无线(虚拟ap)配置设备SSID勾选WDS(wuxian为ssid)
9.
10.选择无线(射频)里面修改频宽,信道,应用变更
11.
12.保存设备修改信息换第二个无线设备做Bridge-Sriation模式
13.
14.选择接口(局域网)修改设备IP(于第一台设备相同网段)
15.
16.选择无线(射频)确定与第一台设备一致
17.
18.扫描中心基站ssid—无线---虚拟ap(显示配置信息)---点击修改
19.
20.选择扫描
21.
22.选择第一个设备ssid
23.
24.勾选锁定到ap 选择应用变更
25.
26.连接互相ping一下确定连接成功
27.
28.
到此桥接模式配置成功。