一、什么是结构型设计模式
结构型设计模式的主要目的是处理类或对象之间的组合与继承问题,通过组织类和对象来形成更大的结构,帮助我们更好地解决类与类、对象与对象之间的耦合问题。这类模式通过定义如何组合对象来实现新的功能,着重于解决系统中复杂关系的组织和维护,使得系统更加易于扩展和维护。
结构型设计模式主要有以下几种:
- 适配器模式(Adapter)
- 桥接模式(Bridge)
- 装饰者模式(Decorator)
- 组合模式(Composite)
- 外观模式(Facade)
- 享元模式(Flyweight)
- 代理模式(Proxy)
接下来,我们将逐一讲解这些结构型设计模式。
二、适配器模式(Adapter Pattern)
2.1 模式介绍
适配器模式将一个类的接口转换成客户端所期待的另一个接口,使原本接口不兼容的类可以一起工作。通过这种模式,开发者无需修改已有代码便能使旧系统与新系统兼容。
2.2 模式结构
适配器模式主要有三个参与者:
- 目标接口(Target Interface):定义客户端所期待的接口。
- 适配者类(Adaptee):现有接口,但与目标接口不兼容。
- 适配器(Adapter):实现目标接口,内部封装适配者类,将适配者的接口转换为目标接口。
2.3 代码示例
// 目标接口
public interface Target {
void request();
}
// 适配者类
public class Adaptee {
public void specificRequest() {
System.out.println("调用适配者类中的方法");
}
}
// 适配器类
public class Adapter implements Target {
private Adaptee adaptee;
public Adapter(Adaptee adaptee) {
this.adaptee = adaptee;
}
@Override
public void request() {
adaptee.specificRequest();
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
Adaptee adaptee = new Adaptee();
Target target = new Adapter(adaptee);
target.request();
}
}
2.4 优缺点
-
优点:
- 可以让不兼容的类协同工作,不用修改现有类,符合开闭原则。
- 提高了类的复用性和灵活性。
-
缺点:
- 增加了系统的复杂性,特别是在过多使用适配器的情况下,代码可读性可能降低。
2.5 适用场景
- 现有系统的接口不符合需求,且无法修改现有系统时。
- 希望复用一些现有的类,但这些类的接口与目标接口不兼容。
三、桥接模式(Bridge Pattern)
3.1 模式介绍
桥接模式将抽象部分与它的实现部分分离,使它们都可以独立地变化。这种模式通常用于系统有多个维度的变化时,利用组合而不是继承来应对变化。
3.2 模式结构
桥接模式主要由以下几个角色组成:
- 抽象类(Abstraction):定义抽象部分的接口,包含一个实现类对象。
- 扩展抽象类(Refined Abstraction):是抽象类的子类,实现具体的业务方法。
- 实现类接口(Implementor):定义实现类的接口。
- 具体实现类(Concrete Implementor):实现
Implementor
接口,负责具体的业务逻辑。
3.3 代码示例
// 实现类接口
public interface Implementor {
void operationImpl();
}
// 具体实现类A
public class ConcreteImplementorA implements Implementor {
@Override
public void operationImpl() {
System.out.println("具体实现类A的操作");
}
}
// 具体实现类B
public class ConcreteImplementorB implements Implementor {
@Override
public void operationImpl() {
System.out.println("具体实现类B的操作");
}
}
// 抽象类
public abstract class Abstraction {
protected Implementor implementor;
public Abstraction(Implementor implementor) {
this.implementor = implementor;
}
public abstract void operation();
}
// 扩展抽象类
public class RefinedAbstraction extends Abstraction {
public RefinedAbstraction(Implementor implementor) {
super(implementor);
}
@Override
public void operation() {
implementor.operationImpl();
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
Implementor implementorA = new ConcreteImplementorA();
Abstraction abstraction = new RefinedAbstraction(implementorA);
abstraction.operation();
Implementor implementorB = new ConcreteImplementorB();
abstraction = new RefinedAbstraction(implementorB);
abstraction.operation();
}
}
3.4 优缺点
-
优点:
- 抽象和实现分离,避免了继承层次的急剧膨胀,支持系统的多维度扩展。
- 提高了系统的扩展性,增加新的实现类或抽象类都非常方便。
-
缺点:
- 增加了系统的复杂性,理解和实现桥接模式需要额外的努力。
3.5 适用场景
- 系统需要从多个维度进行扩展时,比如需要在不同平台上显示不同格式的图形。
- 希望减少子类的数量,减少类的层次结构。
四、装饰者模式(Decorator Pattern)
4.1 模式介绍
装饰者模式动态地给对象添加额外的职责,相比于继承,装饰者模式更加灵活。装饰者模式通过创建一系列装饰类来对原始对象进行增强,使得对象可以灵活地进行扩展。
4.2 模式结构
装饰者模式包括以下角色:
- 组件接口(Component):定义对象的基本接口。
- 具体组件(Concrete Component):实现
Component
接口,代表被装饰的对象。 - 装饰者(Decorator):实现
Component
接口,并持有一个Component
对象,负责对其进行增强。 - 具体装饰者(Concrete Decorator):具体实现装饰者逻辑,增强
Component
对象的功能。
4.3 代码示例
// 组件接口
public interface Component {
void operation();
}
// 具体组件
public class ConcreteComponent implements Component {
@Override
public void operation() {
System.out.println("执行具体组件的操作");
}
}
// 装饰者类
public class Decorator implements Component {
protected Component component;
public Decorator(Component component) {
this.component = component;
}
@Override
public void operation() {
component.operation();
}
}
// 具体装饰者A
public class ConcreteDecoratorA extends Decorator {
public ConcreteDecoratorA(Component component) {
super(component);
}
@Override
public void operation() {
super.operation();
addedBehavior();
}
// 添加新的行为
private void addedBehavior() {
System.out.println("具体装饰者A的额外行为");
}
}
// 具体装饰者B
public class ConcreteDecoratorB extends Decorator {
public ConcreteDecoratorB(Component component) {
super(component);
}
@Override
public void operation() {
super.operation();
System.out.println("具体装饰者B的额外行为");
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
Component component = new ConcreteComponent();
Component decoratorA = new ConcreteDecoratorA(component);
Component decoratorB = new ConcreteDecoratorB(decoratorA);
decoratorB.operation();
}
}
4.4 优缺点
-
优点:
- 动态地给对象添加新功能,遵循开闭原则。
- 装饰者模式可以灵活组合功能,使得每个装饰者类的职责单一,增强了可维护性。
-
缺点:
- 装饰者模式会增加系统的复杂性,特别是在多个装饰者叠加时,代码的调试可能变得困难。
4.5 适用场景
- 需要动态地给一个对象添加额外功能,且这些功能可以灵活地组合。
- 希望避免通过继承来扩展对象的功能时。
五、组合模式(Composite Pattern)
5.1 模式介绍
组合模式将对象组合成树形结构以表示“部分-整体”的层次结构。组合模式使得客户端对单个对象和组合对象的使用具有一致性,常用于表示层次结构的场景。
5.2 模式结构
组合模式包括以下角色:
- 组件(Component):为组合中的对象声明接口,并提供了子类的默认行为。
- 叶子(Leaf):代表组合中的叶子对象,没有子节点。
- 组合(Composite):定义有子节点的组合对象,并实现与叶子节点一致的接口。
5.3 代码示例
// 组件接口
public abstract class Component {
public void add(Component component) {
throw new UnsupportedOperationException();
}
public void remove(Component component) {
throw new UnsupportedOperationException();
}
public Component getChild(int i) {
throw new UnsupportedOperationException();
}
public abstract void operation();
}
// 叶子对象
public class Leaf extends Component {
@Override
public void operation() {
System.out.println("执行叶子的操作");
}
}
// 组合对象
public class Composite extends Component {
private List<Component> children = new ArrayList<>();
@Override
public void add(Component component) {
children.add(component);
}
@Override
public void remove(Component component) {
children.remove(component);
}
@Override
public Component getChild(int i) {
return children.get(i);
}
@Override
public void operation() {
for (Component child : children) {
child.operation();
}
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
Composite root = new Composite();
root.add(new Leaf());
Composite subComposite = new Composite();
subComposite.add(new Leaf());
root.add(subComposite);
root.operation();
}
}
5.4 优缺点
-
优点:
- 组合模式能清晰地描述对象层次结构,使得客户代码可以一致地操作单个对象和组合对象。
- 容易扩展新的类型,符合开闭原则。
-
缺点:
- 设计的层次结构复杂,可能会导致难以维护,特别是在层次过深的情况下。
5.5 适用场景
- 需要表示对象的部分-整体层次结构时。
- 希望客户端可以忽略组合对象与单个对象的区别,统一处理它们时。
六、外观模式(Facade Pattern)
6.1 模式介绍
外观模式为子系统中的一组接口提供一个一致的接口,使得子系统更加容易被使用。它通过将复杂的子系统与客户端之间的交互封装起来,简化了客户端的使用难度。
6.2 模式结构
外观模式由以下角色组成:
- 外观(Facade):提供了与客户端交互的简化接口。
- 子系统类(Subsystem Classes):复杂的子系统,负责实现子系统的具体功能。
6.3 代码示例
// 子系统A
public class SubsystemA {
public void operationA() {
System.out.println("子系统A的操作");
}
}
// 子系统B
public class SubsystemB {
public void operationB() {
System.out.println("子系统B的操作");
}
}
// 外观类
public class Facade {
private SubsystemA subsystemA;
private SubsystemB subsystemB;
public Facade() {
subsystemA = new SubsystemA();
subsystemB = new SubsystemB();
}
public void operation() {
subsystemA.operationA();
subsystemB.operationB();
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
Facade facade = new Facade();
facade.operation();
}
}
6.4 优缺点
-
优点:
- 简化了复杂系统的使用,使得系统的交互更加清晰明了。
- 外观模式提供了子系统和客户端之间的松耦合,增强了系统的可维护性。
-
缺点:
- 增加了额外的外观类,可能会使系统变得臃肿。
6.5 适用场景
- 需要简化子系统的复杂交互时。
- 子系统与客户端之间需要松耦合时。
七、享元模式(Flyweight Pattern)
7.1 模式介绍
享元模式通过共享对象来尽量减少内存消耗,特别适用于大量细粒度对象的场景。它通过将相同的对象共享来减少对象数量,以节省内存。
7.2 模式结构
享元模式主要有以下角色:
- 享元接口(Flyweight Interface):定义享元对象的接口。
- 具体享元(Concrete Flyweight):实现享元接口,表示可以共享的对象。
- 非共享享元(Unshared Flyweight):不可以共享的对象。
- 享元工厂(Flyweight Factory):负责管理享元对象的创建和缓存,确保每个享元对象可以被共享。
7.3 代码示例
// 享元接口
public interface Flyweight {
void operation(String externalState);
}
// 具体享元
public class ConcreteFlyweight implements Flyweight {
private String intrinsicState;
public ConcreteFlyweight(String intrinsicState) {
this.intrinsicState = intrinsicState;
}
@Override
public void operation(String externalState) {
System.out.println("内部状态:" + intrinsicState + ",外部状态:" + externalState);
}
}
// 享元工厂
public class FlyweightFactory {
private Map<String, Flyweight> flyweights = new HashMap<>();
public Flyweight getFlyweight(String key) {
if (!flyweights.containsKey(key)) {
flyweights.put(key, new ConcreteFlyweight(key));
}
return flyweights.get(key);
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
FlyweightFactory factory = new FlyweightFactory();
Flyweight flyweight1 = factory.getFlyweight("A");
flyweight1.operation("外部状态1");
Flyweight flyweight2 = factory.getFlyweight("A");
flyweight2.operation("外部状态2");
}
}
7.4 优缺点
-
优点:
- 通过共享对象减少内存消耗,特别适用于有大量重复对象的场景。
- 提高了系统的性能,减少了内存的使用。
-
缺点:
- 增加了系统的复杂性,享元对象的状态需要区分内部和外部状态。
7.5 适用场景
- 系统中有大量相似对象时,且这些对象大部分状态可以共享时。
- 需要优化内存使用,减少对象数量时。
八、代理模式(Proxy Pattern)
8.1 模式介绍
代理模式为另一个对象提供一个代理对象,代理对象控制着对目标对象的访问,代理模式通过创建一个代理类来实现对原对象的控制,使得原对象不直接暴露给客户端。
8.2 模式结构
代理模式包括以下几个角色:
- 抽象主题(Subject)
:定义目标对象和代理对象的共同接口。
- 真实主题(RealSubject):实际的业务实现对象,处理实际的业务逻辑。
- 代理(Proxy):控制对真实主题的访问,负责在调用真实主题之前进行处理。
8.3 代码示例
// 抽象主题
public interface Subject {
void request();
}
// 真实主题
public class RealSubject implements Subject {
@Override
public void request() {
System.out.println("真实主题的请求处理");
}
}
// 代理类
public class Proxy implements Subject {
private RealSubject realSubject;
@Override
public void request() {
if (realSubject == null) {
realSubject = new RealSubject();
}
System.out.println("代理类处理前的操作");
realSubject.request();
System.out.println("代理类处理后的操作");
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
Subject proxy = new Proxy();
proxy.request();
}
}
8.4 优缺点
-
优点:
- 代理模式可以在不修改目标对象的前提下,通过代理对象扩展目标对象的功能(如权限控制、日志记录、延迟加载等)。
- 提供了目标对象的间接访问,客户端无需知道具体的目标对象如何实现,可以通过代理来控制访问。
-
缺点:
- 增加了系统的复杂性,尤其是在多层代理时,可能会影响系统性能。
- 代理模式可能会造成过多的代理类,增加系统维护成本。
8.5 适用场景
- 需要控制对对象的访问时,例如在远程调用、权限控制、缓存机制、延迟加载等场景中。
- 希望通过代理对象在不修改目标对象的情况下增强某些功能时。
九、总结
在设计模式中,结构型设计模式主要关注类和对象的组合,它们通过一定的组合方式来解决系统的扩展问题,同时也提升了系统的灵活性和可维护性。在实际开发过程中,合理选择和使用结构型设计模式,不仅可以简化系统的复杂度,还能提高系统的可扩展性和可维护性。
- 适配器模式 用于转换接口,使得不兼容的接口可以一起工作。
- 桥接模式 通过分离抽象和实现,使得它们可以独立变化。
- 装饰者模式 动态地为对象添加额外的功能,符合开闭原则。
- 组合模式 将对象组合成树形结构,以实现部分-整体的层次结构。
- 外观模式 提供了简化子系统使用的统一接口,降低了系统的复杂度。
- 享元模式 通过共享细粒度对象来减少内存消耗。
- 代理模式 通过代理对象控制对目标对象的访问,增加了对目标对象访问的灵活性。
每种设计模式都有其独特的适用场景和优缺点,开发者在项目中选择合适的模式,不仅能够提高开发效率,还能增强系统的稳定性和扩展性。通过对这些结构型设计模式的理解和掌握,能够帮助开发者更好地应对复杂系统设计中的挑战,提升代码的质量和可维护性。
在实际的开发工作中,结构型设计模式常常是结合其他设计模式(如创建型模式、行为型模式)一起使用的,它们并非孤立存在,而是相互补充和增强,共同解决系统设计中的复杂问题。掌握这些模式的精髓,能够让开发者在复杂系统设计中游刃有余,打造出高效、灵活、易扩展的系统架构。
通过本篇对结构型设计模式的介绍和总结,希望读者能够对这些模式有一个清晰的理解和认知,并在实际项目中熟练运用,设计出更加健壮和高效的系统。