手柄/身体模式
将抽象与其实现分离,以便二者可以独立变化。
真实世界例子
考虑到你有一件武器具有不同的魔法,假如允许你让不同的武器与不同的魔法混合。你会怎么做? 一为每个不同的魔法功能创建多个武器副本, 二你会根据需要为武器创建单独的魔法并设置它。
通俗的说
桥接模式是一个更推荐组合而不是继承的模式。将实现细节从一个层次结构推送到具有单独层次结构的另一个对象。
维基百科说
桥接模式是软件工程中使用的一种设计模式,旨在“将抽象与其实现分离,从而使两者可以独立变化”
程序示例
翻译一下上面的武器示例。下面我们有武器的类层级:
/**
* 武器基类.
*/
public interface Weapon {
void wield();
void swing();
void unwield();
// 所有武器都具备获取其附魔属性的能力
Enchantment getEnchantment();
}
/**
* 剑.
*/
@Slf4j
@AllArgsConstructor
public class Sword implements Weapon {
private final Enchantment enchantment;
@Override
public void wield() {
LOGGER.info("剑开始挥舞.");
enchantment.onActivate();
}
@Override
public void swing() {
LOGGER.info("剑刺了过去.");
enchantment.apply();
}
@Override
public void unwield() {
LOGGER.info("剑安静下来.");
enchantment.onDeactivate();
}
@Override
public Enchantment getEnchantment() {
return enchantment;
}
}
/**
* 锤子.
*/
@Slf4j
@AllArgsConstructor
public class Hammer implements Weapon {
private final Enchantment enchantment;
@Override
public void wield() {
LOGGER.info("挥舞着锤子.");
enchantment.onActivate();
}
@Override
public void swing() {
LOGGER.info("锤子摆动起来.");
enchantment.apply();
}
@Override
public void unwield() {
LOGGER.info("锤子安静下来.");
enchantment.onDeactivate();
}
@Override
public Enchantment getEnchantment() {
return enchantment;
}
}
这里是单独的附魔类结构:
/**
* 附魔基类.
*/
public interface Enchantment {
void onActivate(); // 被激活
void apply(); // 执行
void onDeactivate(); // 消亡
}
/**
* 飞行附魔.
*/
@Slf4j
public class FlyingEnchantment implements Enchantment {
@Override
public void onActivate() {
LOGGER.info("该物品开始发光.");
}
@Override
public void apply() {
LOGGER.info("飞行并击中中敌人后返回.");
}
@Override
public void onDeactivate() {
LOGGER.info("光芒逐渐消失.");
}
}
/**
* 嗜血附魔.
*/
@Slf4j
public class SoulEatingEnchantment implements Enchantment {
@Override
public void onActivate() {
LOGGER.info("该物品开始传播嗜血效果.");
}
@Override
public void apply() {
LOGGER.info("该物品开始吞噬灵魂.");
}
@Override
public void onDeactivate() {
LOGGER.info("嗜血效果慢慢消失.");
}
}
这里是两种层次结构的实践:
LOGGER.info("骑士收到一个附魔了的剑.");
var enchantedSword = new Sword(new SoulEatingEnchantment());
enchantedSword.wield();
enchantedSword.swing();
enchantedSword.unwield();
LOGGER.info("女神收到一个附魔了锤子");
var hammer = new Hammer(new FlyingEnchantment());
hammer.wield();
hammer.swing();
hammer.unwield();
输出结果:
骑士收到一个附魔了的剑.
剑开始挥舞.
该物品开始传播嗜血效果.
剑刺了过去.
该物品开始吞噬灵魂.
剑安静下来.
嗜血效果慢慢消失.
女神收到一个附魔了锤子
挥舞着锤子.
该物品开始发光.
锤子摆动起来.
飞行并击中中敌人后返回.
锤子安静下来.
光芒逐渐消失.
- 明确类中独立的维度。 独立的概念可能是: 抽象/平台, 域/基础设施, 前端/后端或接口/实现。
- 了解客户端的业务需求, 并在抽象基类中定义它们。
- 确定在所有平台上都可执行的业务。 并在通用实现接口中声明抽象部分所需的业务。
- 为你域内的所有平台创建实现类, 但需确保它们遵循实现部分的接口。
- 在抽象类中添加指向实现类型的引用成员变量。 抽象部分会将大部分工作委派给该成员变量所指向的实现对象。
- 如果你的高层逻辑有多个变体, 则可通过扩展抽象基类为每个变体创建一个精确抽象。
- 客户端代码必须将实现对象传递给抽象部分的构造函数才能使其能够相互关联。 此后, 客户端只需与抽象对象进行交互, 无需和实现对象打交道。
- 如果你想要拆分或重组一个具有多重功能的庞杂类 (例如能与多个数据库服务器进行交互的类), 可以使用桥接模式。
- 如果你希望在几个独立维度上扩展一个类, 可使用该模式。
- 如果你需要在运行时切换不同实现方法, 可使用桥接模式。
优点
- 你可以创建与平台无关的类和程序。
- 客户端代码仅与高层抽象部分进行互动, 不会接触到平台的详细信息。
- 开闭原则。 你可以新增抽象部分和实现部分, 且它们之间不会相互影响。
- 单一职责原则。 抽象部分专注于处理高层逻辑, 实现部分处理平台细节。
缺点
- 对高内聚的类使用该模式可能会让代码更加复杂。
- 桥接模式通常会于开发前期进行设计, 使你能够将程序的各个部分独立开来以便开发。 另一方面, 适配器模式通常在已有程序中使用, 让相互不兼容的类能很好地合作。
- 桥接模式、 状态模式和策略模式 (在某种程度上包括适配器) 模式的接口非常相似。 实际上, 它们都基于组合模式——即将工作委派给其他对象, 不过也各自解决了不同的问题。 模式并不只是以特定方式组织代码的配方, 你还可以使用它们来和其他开发者讨论模式所解决的问题。
- 你可以将抽象工厂模式和桥接模式搭配使用。 如果由桥接定义的抽象只能与特定实现合作, 这一模式搭配就非常有用。 在这种情况下, 抽象工厂可以对这些关系进行封装, 并且对客户端代码隐藏其复杂性。
- 你可以结合使用生成器模式和桥接模式: 主管类负责抽象工作, 各种不同的生成器负责实现工作。
- Spring 模块中基于桥接模式的实现:
- ViewRendererServlet: 它是一个 servlet 桥接 ,主要是对 Portlet MVC 的支持
- Spring 日志处理使用到桥梁模式