1)概述
桥接模式又称柄体(Handle and body)模式或者接口(Interface)模式。将抽象部分和实现部分部分脱耦,使两部分可以独立的变化。抽象类和实现类之间使用组合/聚合而不是继承关系,设计更有扩展性,客户端调用时不用知道实现的细节。
相似场景:家兴最近开发一款新的游戏,公司分配任务的是让他开发各种角色的基本功能,例如LOL的肉盾和法师之类的角色,同时呢,这些角色拥有基本的行动功能,走路,跳跃,放技能等等;家兴马上对这些代码进行开发,他马上用一个继承方式来实现,角色为父类,然后肉盾,法师为继承角色,之后走路,跳跃,放技能再继承其中的肉盾,法师;一开始的角色少的时候,反而没有发现问题,后来增多几个角色或技能的时候发现类增加也很多,成子类爆炸式增长,耦合性增大。家兴在想该怎么去解决这样一个问题呢?
2)解决方案
分析:对象的继承关系是在编译时就定义好了,所以无法在运行时改变从父类继承的实现。子类的实现与它的父类有非常紧密的依赖关系,以至于父类实现中的任何变化必然会导致子类的发生变化。当你需要复用子类时,如果继承下来的实现不适合解决新的问题,则父类必须重写或其它更适合的类替换。这种依赖的关系限制了灵活性并限制了复用性,针对这个问题,提出了通过聚合或组合去实现需求,以桥接模式去实现。
重点需要理解如何将抽象化(Abstraction)与实现化(Implementation)脱耦,使得二者可以独立地变化。
脱耦:脱耦就是将抽象化和实现化之间的耦合解脱开,或者说是将它们之间的强关联改换成弱关联,将两个角色之间的继承关系改为关联关系。桥接模式中的所谓脱耦,就是指在一个软件系统的抽象化和实现化之间使用关联关系(组合或者聚合关系)而不是继承关系,从而使两者可以相对独立地变化,这就是桥接模式的用意。
3)结构
Action:抽象类动作类,维护一个Role的对象,聚合关系;
Walk:继承抽象类Action,走路动作的类,修正抽象类化角色。扩充由Action定义的接口
Jump:继承抽象类Action,跳的动作的类,修正抽象类化角色。扩充由Action定义的接口
PullSkill:继承抽象类Action,放技能动作的类,修正抽象类化角色。扩充由Action定义的接口
Role:角色接口,实现化角色。定义实现类的接口,这个接口不一定要与Action类完全一致。一般Role提供基本操作,
Action可能做更多更复杂的操作。
Epimatium:实现Role接口的肉盾类,进行具体实现操作等。
Master:实现Role接口的法师类,进行具体实现操作等。
4.桥接模式的XML图
5.代码实现
//Action抽象类
public abstract class Action {
protected Role role;
public void setRole(Role role){
this.role = role;
}
public abstract void diIt();
}
//walk类
public class Walk extends Action{
@Override
public void diIt() {
// TODO Auto-generated method stub
this.role.doSomeThing();
System.out.println("我要走路了");
}
}
//jump类
public class Jump extends Action{
@Override
public void diIt() {
// TODO Auto-generated method stub
this.role.doSomeThing();
System.out.println("我要跳了");
}
}
//pullSkill类
public class PullSkill extends Action{
@Override
public void diIt() {
// TODO Auto-generated method stub
this.role.doSomeThing();
System.out.println("我要放技能");
}
}
//Role接口
public interface Role {
public void doSomeThing();
}
//Epimatium
public class Epimatium implements Role{
@Override
public void doSomeThing() {
// TODO Auto-generated method stub
System.out.println("我是肉盾角色:");
}
}
//Master 类
public class Master implements Role{
@Override
public void doSomeThing() {
// TODO Auto-generated method stub
System.out.println("我是法师角色:");
}
}
//客户端
public class Client {
public static void main(String[] args) {
// TODO Auto-generated method stub
Role role = new Epimatium();
Action walk = new Walk();
walk.setRole(role);
walk.diIt();
Role role1 = new Master();
Action jump = new Jump();
jump.setRole(role1);
jump.diIt();
Action skill = new PullSkill();
skill.setRole(role1);
skill.diIt();
}
}
程序输出结果:
我是肉盾角色:
我要走路了
我是法师角色:
我要跳了
我是法师角色:
我要放技能
6)分析结果
实现系统有可能更多的角度分类,每一种分类都有可能变化,就把这种多角度分离出来让它们独立变化,减少他们之间的耦合。如上面的动作等等
7)合成/聚合复用原则
合成/聚合复用原则,尽量使用合成/聚合,尽量不要使用类继承。
合成和聚合都是关联特殊种类。聚合表示一种弱拥有关系,体现的事A对象包含B对象,但B对象不是A对象的一部分;合成则是一种强拥有的关系,体现严格的整体和部分的关系,部分和整体的生命周期都是一样。
合成/聚合复用原则的好处是:优先使用对象的合成/聚合将有助于你保持每个类被封装,并被集合中在单个任务上。这个类和类继承层次会保持较小规模,并且不太可能增长为不可控制的庞然大物。
8)与其它模式之间的关联
1.抽象工厂模式
Factory 模式可以用来创建和配置一个特定的Bridge模式。
2.适配器模式
适配器模式使用一般是在系统开发完成后,由于被适配者不能使用,进行适配改动去使用。桥接模式一般是在系统在开发初期进行设计。
3.装饰模式
这两种模式都是避免继承导致子类过多才使用的,但是他们解决的问题不同,装饰模式是不在一个同层次上实现,而桥接模式则把原来的基类的实现化细节抽象出来,在构造到一个实现化的结构中,然后再把原来的基类改造