当前位置:   article > 正文

设计模式(七)----桥接模式_一个圆和矩形桥接

一个圆和矩形桥接

前言

有两个属性:形状和颜色。
假如现在形状有圆形、长方形和正方形,颜色有黑色、白色和灰色。
现在我们要对形状进行上色,可以知道,一共会有3*3=9种不同的图形:黑色圆形、白色圆形、灰色圆形、黑色长方形。。。
存在两种解决方案
方案一:为每种形状提供各种颜色的版本(或为每种颜色提供各种形状的版本)。
方案二:根据实际需要对形状和颜色进行组合。
可以料想,方案一存在这样一个问题,假如我要添加一个三角形形状,那就要增加三种图形:黑色三角形、白色三角形和灰色三角形;假如我要添加一个红色的颜色,也要增加三种图形:红色圆形、红色长方形、红色正方形。。。每次增加都要增加若干个类。
从类的设计角度分析,具体的类如"黑色圆形"等违反了"单一职责原则",因为有不止一个引起它们变化的原因(形状和颜色),它们将形状描绘和颜色填充这两种完全不同的职责融合在一起,任意一个职责发生改变都需要修改它们,系统扩展困难。
方案二的解决方法就是,提供两个父类,形状和颜色,两个父类都包含了相应的子类,然后根据需要对形状和颜色进行组合。
对于有两个或两个以上变化的维度,一般采用方案二实现,这样不仅减少了系统中类的个数,也利于系统扩展。

概述

桥接模式(Bridge Pattern),将抽象部分和实现部分分离开来,使两者都可独立变化。它是一种对象结构型模式,又称为柄体(Handle and Body)模式或接口(Interface)模式。
注:将抽象部分和实现部分分离并不是将抽象类与其派生类分离,而是抽象类和其他派生类用来实现自己的对象。
它通过提供抽象化和实现化的桥接结构,实现两者的解耦。
该模式涉及到一个作为桥接的接口,使得实体类的功能独立于接口的实现类。这两种类型的类可被结构化改变而互不影响。

作用:桥接模式的作用就是为被分离的抽象部分和实现部分搭桥。为多维度变化的系统提供了一套完整的解决方案,降低了系统复杂度。将继承关系转换成关联关系,降低了类之间的耦合度,减少了系统中类的数量。
何时使用:实现系统可能有多个角度分类,每一种角度都可能会变化。
如何解决:将这种多角度分类分离出来,让它们独立变化,减少它们之间的耦合。
关键代码:抽象类依赖实现类。

若软件系统中某个类存在两个独立变化的维度,通过该模式可将这两个维度分离出来,使两者可以独立扩展,让系统更加符合"单一职责原则"。
与多层继承方案不同,它将两个独立变化的维度设计为两个独立的继承等级结构,并且在抽象层建立一个抽象关联,该关联关系类似于一条连接两个独立继承结构的桥。
桥接模式用一种巧妙地方式处理多层继承存在的问题,用抽象关联取代了传统的多层继承,将类之间的静态继承关系转换为动态的对象组合关系,使系统更加灵活,并易于扩展,同时有效控制了系统中类的个数。

特点
将抽象部分与实现部分分离,使它们都可以独立变化。为达到该目的,抽象部分会拥有实现部分的接口对象,通过这个接口来调用具体实现部分的功能。桥接在程序上就成了抽象部分拥有实现部分的接口对象,维护桥接就是维护这个关系。也就是说,桥接模式中的桥接是一个单方向的关系,只能抽象部分使用实现部分的对象,而不能反过来。

理解桥接模式,重点需要理解如何将抽象化(Abstraction)与实现化(Implementor)脱耦,使两者可独立变化。
抽象化
将复杂物体的一个或几个特性抽取出来只关注其动作或过程。在面向对象就是将对象共同的性质抽取出来,形成类的过程。
实现化
根据抽象化给出具体实现。它和抽象化是一个互逆的过程,实现化是对抽象化事物的进一步具体化。
脱耦
将抽象化是实现化的耦合解开,或者说将两者之间的强关联关系变成弱关联关系,将两个角色之间的继承关系改为关联关系。
将抽象部分与实现部分分离套用《大话设计模式》里的就是实现系统可能有多个角度类,每一种角度都可能变化,那么把这种多角度分类分离出来让它们独立变化,减少它们之间的耦合。

桥接模式主要包含如下几个角色
Abstraction:抽象类。用于定义抽象类的接口,它一般是抽象类而非接口。其中定义了一个Implementor(实现类接口)类型的对象并可维护该对象,它与Implementor之间有关联关系,既可以包含抽象业务方法,也可以包含具体业务方法。
RefinedAbstraction:扩充抽象类。通常它不再是抽象类而是具体类,它实现了在Abstraction中声明的抽象业务方法,在该类中可以调用在Implementor中定义的业务方法。
Implementor:定义实现类的接口。该接口不一定要与Abstraction接口完全一致,事实上这两个接口可能完全不同。一般而言,该接口仅提供基本操作,而Abstraction定义的接口可能会做更多更复杂的操作。该接口对这些基本操作进行了声明,具体实现交给其子类。通过关联关系,在Abstraction中可调用该接口中的方法,使用关联关系代替继承关系。
ConcreteImplementor:具体实现类。在不同的ConcreteImplementor中提供基本操作的不同实现。程序运行时,ConcreteImplementor对象替换其父类对象,提供给抽象类具体的业务方法。

使用桥接模式时,首先应识别出一个类所具有的两个独立变化的维度,将它们设计为两个独立的继承等级结构,为两个维度都提供抽象层,并建立抽象耦合。通常将具有两个独立变化维度的类的一些普通业务方法和与之关系最密切的维度设计为抽象层次接口(抽象部分),将另一个维度设计为"实现类"层次结构(实现部分)。

实例

就以一开始说的形状填充颜色为例,给出下面的代码
形状类Shape(Abstraction)
主要提供画形状的方法
在抽象类(Abstraction)中定义了一个实现类接口类型的成员对象color,再通过注入的方式为该对象赋值,一般将该对象的可见性定义为protected,以便在其子类中访问Implementor的方法

public abstract class Shape {
	protected Color color;
	public void setColor(Color color) {
		this.color = color;
	}
	//画形状的方法
	public abstract void draw();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

圆形类(Refined Abstraction)

public class Circle extends Shape {
	@Override
	public void draw() {
		color.bePaint("圆形");
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

长方形类(Refined Abstraction)

public class Rectangle extends Shape {
	@Override
	public void draw() {
		color.bePaint("长方形");
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

正方形类(Refined Abstraction)

public class Square extends Shape {
	@Override
	public void draw() {
		color.draw("正方形");
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

颜色接口Color(Implementor)
用于定义与该维度相对应的一些方法

public interface Color {
	public void bePaint(String shape);
}
  • 1
  • 2
  • 3

黑色类(ConcreteImplementor)

public class Black implements Color {
	public void bePaint(String shape) {
		System.out.println("黑色的" + shape);
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5

白色类(ConcreteImplementor)

public class White implements Color {
	public void bePaint(String shape) {
		System.out.println("白色的" + shape);
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5

灰色类(ConcreteImplementor)

public class Gray implements Color {
	public void bePaint(String shape) {
		System.out.println("灰色的" + shape);
	} 
}
  • 1
  • 2
  • 3
  • 4
  • 5

客户端类
于客户端而言,可针对两个纬度的抽象层编程,在程序运行时再动态确定两个维度的子类,动态组合对象,将两个独立变化的维度完全解耦,以便能够灵活扩充任一维度而对另一维度不造成任何影响。

public class Client {
	public static void main(String[] args) {
		//创建形状对象
		//圆形对象
		Shape circle = new Circle();
		//长方形对象
		Shape rectangle = new Rectangle();
		//正方形对象
		Shape square = new Square();

		//创建颜色对象
		//黑色对象
		Color black = new Black();
		//白色对象
		Color white = new White();
		//灰色对象
		Color gray = new Gray();

		//自定义形状颜色组合
		//黑色的圆形
		circle.setColor(black);
		circle.draw();
		//白色的正方形
		square.setColor(white);
		square.draw();
		//灰色的长方形
		rectangle.setColor(gray);
		rectangle.draw();
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

打印结果
黑色的圆形
白色的正方形
灰色的长方形

总结

优点
分离抽象接口及其实现部分,提供了比继承更好的解决方案。使用"对象间的关联关系"解耦了抽象和实现之间固有的绑定关系,使得抽象和实现可沿着各自的维度来变化。也就是说抽象实现不再在同一个继承层次结构中,而是"子类化"它们,使它们各自都有自己的子类,以便任意组合子类,获取多维度组合对象。
提高了系统的扩展性,在两个变化维度中扩展任意一个维度,都不需要修改原有系统。
实现细节对客户透明。
缺点
桥接模式的引入会增加系统的理解与设计难度,由于聚合关联关系建立在抽象层,要求开发者一开始就针对抽象层进行设计与编程。
桥接模式要求正确识别出系统中两个独立变化的维度,因此其使用范围具有一定局限性。
使用场景
若一个系统需在构建的抽象化角色和具体化角色之间增加更多灵活性,避免在两个层次之间建立静态的继承关系,通过桥接模式可使它们在抽象层建立一个关联关系。
对于那些不希望使用继承或因多层次继承而导致系统类的个数急剧增加的,桥接模式尤为适用。
一个类存在两个或两个以上独立变化的维度,且这两个维度都需进行扩展。
桥接模式是设计Java虚拟机和实现JDBC等驱动程序的核心模式之一,应用较为广泛。
不希望在抽象和它的实现部分之间有一个固定的绑定关系,如在程序运行时刻实现部分应该可以被选择或切换。
对一个抽象的实现部分的修改不应对客户产生影响,即客户的代码不需要重新编译。

与其它设计模式的关系

桥接模式和抽象工厂模式
用抽象工厂模式可用来创建和配置一个特定的桥接模式。
桥接模式和装饰器模式
这两个设计模式在一定程度上都是为了减少子类数目避免出现复杂的继承关系,但两者的解决方法不同。
装饰器模式
将子类中比基类多出来的部分放到单独的类中,以适应新功能增加的需要,当将新功能的类封装到基类的对象里时,就得到了所需的子类对象,这些描述新功能的类通过组合可实现很多功能。
桥接模式
将原来基类的实现化细节抽离出来,再构造到一个现实化的结构中,然后由原来的基类改造成一个抽象化的等级结构,这样就可以实现系统在多个维度上独立变化。
桥接模式和适配器模式
软件开发中,适配器模式通常可与桥接模式联合使用。
适配器模式可用来解决两个不兼容的接口的问题,这种情况下被适配的类往往是一个黑盒子,有时候不想也不能改变这个被适配的类,也不能控制其扩展。适配器模式通常用于现有系统与第三方产品功能的集成,采用增加适配器的方式将第三方类集成到系统中。桥接模式则不同,用户可通过接口继承或类继承的方式对系统进行扩展。
这两者设计模式用于不同的设计阶段,桥接模式用于系统的初步设计,用于存在两个独立变化的维度的类可将其分为抽象化和实现化两个角色,使它们可独立变化;初步设计完成之后,当发现系统与已有类无法协同工作时,可采用适配器模式。但有时候在设计初期也需要考虑使用适配器模式,特别是那些涉及到大量第三方应用接口的情况。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/很楠不爱3/article/detail/569079
推荐阅读
相关标签
  

闽ICP备14008679号