什么是桥接模式
桥接模式是将他的抽象部分与实现部分分离,使他们都可以独立地变化。
桥接模式包含以下几个角色:
Abstraction(抽象类):用于定义抽象类,一般是抽象类不是接口,该类中定义了一个实现类接口类型的对象并可以维护该对象,并且与实现类接口有关联关系,该抽象类即可以包含抽象业务方法,也可以包含具体业务方法。
RefinedAbstraction(扩展抽象类):扩展由Abstraction定义的接口,通常情况下为具体类,实现了在Abstraction中声明的抽象业务方法,在RefinedAbstraction中可以调用在实现类中定义的业务方法。
Implementor(实现类接口):这个接口不一定与Abstraction的接口完全一致,实际上可以完全不同。一般实现类接口只提供基本操作,而Abstraction则会做更多复杂的操作。实现类接口对这些基本操作进行了声明,而具体实现交给其子类。通过关联关系,在Abstraction中不仅有自己定义的方法,还可以通过关联关系调用实现类中定义的方法,使用关联关系来代替继承关系。
ConcreteImplementor(具体实现类):在不同的具体实现类中提供基本操作的不同实现。
桥接模式中体现了很多面向对象的设计原则思想,包括单一职责原则,开闭原则,合成复用原则,里氏替换原则,依赖倒转原则等。
桥接模式的优缺点
优点
- 分离抽象类及其实现部分。桥接模式使用对象间的关联关系解耦了抽象和现实之间固有的绑定关系,使得抽象和实现可以沿着各自的维度变化。
- 桥接模式可以取代多层继承方案,多层继承方案违背了单一职责原则,复用性较差,且类的个数非常多。
- 提供了系统的扩展性,在两个变化维度中任意扩展一个维度,不需要修改原有系统,符合开闭原则。
缺点
- 桥接模式的使用增加了系统的理解和设计难度,由于关联关系建立在抽象层,要求开发者一开始就针对抽象层进行编程。
- 桥接模式要求正确识别出系统中两个独立变化的维度,因此其使用范围有一定的局限性。
桥接模式的应用场景
- 如果一个系统需要在抽象化和具体化之间增加更多的灵活性,避免在两个层次之间建立静态的继承关系,通过桥接模式可以使他们在抽象层建立一个关联关系。
- 抽象部分和实现部分可以以继承的方式独立扩展而不相互影响。
- 一个类存在多个独立变化的维度,且多个维度都要独立进行扩展。
桥接模式的案例
// 抽象类
public abstract class Phone {
/**
* 组合品牌
*/
private Brand brand;
public Phone(Brand brand) {
super();
this.brand = brand;
}
protected void open() {
this.brand.open();
}
protected void close() {
brand.close();
}
protected void call() {
brand.call();
}
}
// 抽象扩展类
public class FoldedPhone extends Phone {
public FoldedPhone(Brand brand) {
super(brand);
}
@Override
public void open() {
super.open();
System.out.println(" 折叠样式手机 ");
}
@Override
public void close() {
super.close();
System.out.println(" 折叠样式手机 ");
}
@Override
public void call() {
super.call();
System.out.println(" 折叠样式手机 ");
}
}
// 实现类接口
public interface Brand {
void open();
void close();
void call();
}
// 具体实现类
public class Vivo implements Brand {
@Override
public void open() {
System.out.println(" Vivo手机开机 ");
}
@Override
public void close() {
System.out.println(" Vivo手机关机 ");
}
@Override
public void call() {
System.out.println(" Vivo手机打电话 ");
}
}
public class XiaoMi implements Brand {
@Override
public void open() {
System.out.println(" 小米手机开机 ");
}
@Override
public void close() {
System.out.println(" 小米手机关机 ");
}
@Override
public void call() {
System.out.println(" 小米手机打电话 ");
}
}
public static void main(String[] args) {
//获取折叠式手机 (样式 + 品牌 )
Phone phone1 = new FoldedPhone(new XiaoMi());
phone1.open();
phone1.call();
phone1.close();
Phone phone2 = new FoldedPhone(new Vivo());
phone2.open();
phone2.call();
phone2.close();
}
桥接模式在源码中的应用
Handler
// 抽象类
public abstract class Handler {
private static final int offValue = Level.OFF.intValue();
private final LogManager manager = LogManager.getLogManager();
// We're using volatile here to avoid synchronizing getters, which
// would prevent other threads from calling isLoggable()
// while publish() is executing.
// On the other hand, setters will be synchronized to exclude concurrent
// execution with more complex methods, such as StreamHandler.publish().
// We wouldn't want 'level' to be changed by another thread in the middle
// of the execution of a 'publish' call.
private volatile Filter filter;
private volatile Formatter formatter;
private volatile Level logLevel = Level.ALL;
public synchronized void setFormatter(Formatter newFormatter) throws SecurityException {
checkPermission();
// Check for a null pointer:
newFormatter.getClass();
formatter = newFormatter;
}
......
}
// 实现类接口
public abstract class Formatter {
/**
* Construct a new formatter.
*/
protected Formatter() {
}
/**
* Format the given log record and return the formatted string.
* <p>
* The resulting formatted String will normally include a
* localized and formatted version of the LogRecord's message field.
* It is recommended to use the {@link Formatter#formatMessage}
* convenience method to localize and format the message field.
*
* @param record the log record to be formatted.
* @return the formatted log record
*/
public abstract String format(LogRecord record);
......
}
// 具体实现类
public class SimpleFormatter extends Formatter {
// format string for printing the log record
private static final String format = LoggingSupport.getSimpleFormat();
private final Date dat = new Date();
public synchronized String format(LogRecord record) {
dat.setTime(record.getMillis());
String source;
if (record.getSourceClassName() != null) {
source = record.getSourceClassName();
if (record.getSourceMethodName() != null) {
source += " " + record.getSourceMethodName();
}
} else {
source = record.getLoggerName();
}
String message = formatMessage(record);
String throwable = "";
if (record.getThrown() != null) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
pw.println();
record.getThrown().printStackTrace(pw);
pw.close();
throwable = sw.toString();
}
return String.format(format,
dat,
source,
record.getLoggerName(),
record.getLevel().getLocalizedLevelName(),
message,
throwable);
}
......
}