结构型设计模式
一,介绍
1.为什么要使用结构型设计模式
结构型设计模式是一种在软件开发中常用的设计模式之一,它可以帮助我们更好地组织和管理代码结构,使得代码更加灵活、可扩展和易于维护。以下是使用结构型设计模式的一些主要原因:
1. 解耦和组件化:结构型设计模式可以帮助我们解耦各个组件之间的依赖关系,使得它们可以独立地进行修改、扩展和重用。这样使得系统的不同部分可以以更高的内聚性和低耦合性组合在一起,促进了系统的模块化和组件化。
2. 简化复杂性:在大型软件系统中,代码的复杂性往往是导致系统难以理解和维护的主要原因之一。结构型设计模式提供了一些通用的解决方案来处理系统中的复杂业务逻辑和数据结构,使得代码更加清晰、简洁和易于理解。
3. 提升代码的灵活性和可扩展性:结构型设计模式通过引入抽象层和接口来实现系统的灵活性和可扩展性。它们可以帮助我们应对需求变化和功能扩展,而无需修改已有的代码。通过使用这些模式,我们可以轻松地添加新的功能或变化系统的行为,而不会对整个系统造成重大的影响。
4. 促进代码的重用性:结构型设计模式提供了一些通用的解决方案和设计原则,可以在多个项目中反复使用。通过将这些模式应用到不同的场景中,我们可以降低代码的冗余度,提高代码的可维护性和可重用性。
5. 提高系统的性能和效率:使用结构型设计模式可以优化系统的性能和效率。例如,享元模式可以减少内存消耗,代理模式可以延迟对象的初始化,装饰器模式可以动态地添加额外的功能等等。
总的来说,结构型设计模式提供了一些通用的解决方案和原则,可以帮助我们更好地组织和管理代码结构,提高系统的灵活性、可扩展性和可维护性。它们是软件开发中的重要工具,可以帮助我们构建高质量、可靠性和可扩展性的软件系统。
二,适配器模式
1.介绍
适配器模式(Adapter Pattern)是一种结构型设计模式,用于将一个类的接口转换成客户端所期望的另一个接口。适配器模式通常用于解决两个不兼容接口之间的兼容性问题。
2.代码示例
适配器模式包括以下几个角色:
- 目标接口(Target Interface):客户端期望使用的接口。
- 源接口(Adaptee Interface):已经存在的接口,但与目标接口不兼容。
- 适配器(Adapter):作为一个中间层,将源接口转换为目标接口。
下面是一个使用适配器模式的Java代码示例:
// 目标接口 interface Target { void request(); } // 源接口 class Adaptee { public void specificRequest() { System.out.println("执行源接口的方法"); } } // 适配器类 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 adapter = new Adapter(adaptee); adapter.request(); // 调用目标接口的方法,实际上会执行源接口的方法 } }
在上面的示例中,`Target`是客户端期望使用的接口,其中定义了一个`request()`方法。`Adaptee`是已经存在的接口,其中定义了一个`specificRequest()`方法。由于`Target`和`Adaptee`的接口不兼容,我们需要使用适配器将其转换。
适配器类`Adapter`实现了`Target`接口,并在`request()`方法中调用了`Adaptee`的`specificRequest()`方法。这样,当客户端通过适配器调用`request()`方法时,实际上会执行`Adaptee`的`specificRequest()`方法。
通过适配器模式,我们可以使得原本不兼容的接口能够协同工作,达到代码复用和解耦的目的。
三,桥接模式
1.介绍
桥接模式(Bridge Pattern)是一种结构型设计模式,它将抽象和实现解耦,使它们可以独立地变化。桥接模式通过组合而不是继承的方式实现了这种解耦。
2.代码示例
桥接模式包括以下几个角色:
- 实现(Implementor):定义实现接口,提供基本操作的接口。
- 具体实现(Concrete Implementor):实现具体的实现接口。
- 抽象(Abstraction):定义抽象接口,维护一个指向实现对象的引用。
- 具体抽象(Concrete Abstraction):扩展抽象接口,实现抽象接口定义的方法。
下面是一个使用桥接模式的Java代码示例:
// 实现接口 interface Implementor { void operationImpl(); } // 具体实现类A class ConcreteImplementorA implements Implementor { @Override public void operationImpl() { System.out.println("具体实现类A的具体操作"); } } // 具体实现类B class ConcreteImplementorB implements Implementor { @Override public void operationImpl() { System.out.println("具体实现类B的具体操作"); } } // 抽象类 abstract class Abstraction { protected Implementor implementor; public Abstraction(Implementor implementor) { this.implementor = implementor; } public abstract void operation(); } // 具体抽象类 class ConcreteAbstraction extends Abstraction { public ConcreteAbstraction(Implementor implementor) { super(implementor); } @Override public void operation() { // 调用实现类的方法 implementor.operationImpl(); } } // 客户端代码 public class Client { public static void main(String[] args) { Implementor implementorA = new ConcreteImplementorA(); Abstraction abstractionA = new ConcreteAbstraction(implementorA); abstractionA.operation(); // 调用抽象类的方法,实际上会调用具体实现类A的方法 Implementor implementorB = new ConcreteImplementorB(); Abstraction abstractionB = new ConcreteAbstraction(implementorB); abstractionB.operation(); // 调用抽象类的方法,实际上会调用具体实现类B的方法 } }
在上面的示例中,`Implementor`是实现接口,定义了一个`operationImpl()`方法。`ConcreteImplementorA`和`ConcreteImplementorB`是具体实现类,分别实现了`Implementor`接口。
`Abstraction`是抽象类,维护一个指向实现对象的引用,并定义了一个`operation()`方法。`ConcreteAbstraction`是具体抽象类,扩展了`Abstraction`接口,并实现了`operation()`方法。
在客户端代码中,我们创建了`ConcreteImplementorA`和`ConcreteImplementorB`的实例,然后分别将它们传递给`ConcreteAbstraction`的构造函数,得到相应的抽象类对象。当我们调用抽象类对象的`operation()`方法时,实际上会调用具体实现类的`operationImpl()`方法。
通过桥接模式,我们可以将抽象和实现解耦,使它们可以独立变化。这样一来,如果需要新增其他的具体实现类,只需要扩展相应的实现接口和具体实现类即可,不需要修改原有的代码,符合开闭原则。
四,组合模式
1.介绍
组合模式是一种结构型设计模式,它将对象组合成树形结构以表示"部分-整体"的层次结构,使得客户端可以统一使用单个对象和组合对象。
2.代码示例
组合模式包含以下几个角色:
- 组合(Component):定义叶子节点和容器节点的公共接口。
- 叶子节点(Leaf):表示组合中最基本的对象,没有子节点。
- 容器节点(Composite):表示包含其他子节点的对象,并实现`Component`接口。
- 客户端(Client):通过`Component`接口操作组合中的对象。下面是一个使用组合模式的Java代码示例:
// 组件接口 interface Component { void operation(); } // 叶子节点 class Leaf implements Component { @Override public void operation() { System.out.println("叶子节点的操作"); } } // 容器节点 class Composite implements Component { private List<Component> children = new ArrayList<>(); public void add(Component component) { children.add(component); } public void remove(Component component) { children.remove(component); } public Component getChild(int index) { return children.get(index); } @Override public void operation() { System.out.println("容器节点的操作"); for (Component component : children) { component.operation(); } } } // 客户端代码 public class Client { public static void main(String[] args) { Component leaf1 = new Leaf(); Component leaf2 = new Leaf(); Composite composite1 = new Composite(); composite1.add(leaf1); composite1.add(leaf2); Component leaf3 = new Leaf(); Composite composite2 = new Composite(); composite2.add(composite1); composite2.add(leaf3); composite2.operation(); // 调用容器节点的操作方法,实际上会递归调用所有子节点的操作方法 } }
在上面的示例中,`Component`是组件接口,定义了一个`operation()`方法。`Leaf`是叶子节点,表示组合中最基本的对象,没有子节点。`Composite`是容器节点,表示包含其他子节点的对象,并实现`Component`接口。
在客户端代码中,我们首先创建两个叶子节点`leaf1`和`leaf2`,然后创建一个容器节点`composite1`,将叶子节点添加到容器节点中。接着创建一个叶子节点`leaf3`,再创建一个容器节点`composite2`,将`composite1`和`leaf3`添加到`composite2`中。
最后,我们调用容器节点`composite2`的`operation()`方法,实际上会递归调用所有子节点的`operation()`方法,从而完成所有的操作。
通过组合模式,我们可以将对象组合成树形结构,使得客户端可以统一使用单个对象和组合对象,同时也方便了我们对整个层次结构的查找、删除、修改等操作。
五,装饰模式
1.介绍
装饰器模式(Decorator Pattern)是一种结构型设计模式,它允许你动态地将新功能添加到对象中,同时保持了与原始对象接口的一致性。
2.代码示例
装饰器模式包含以下几个角色:
- 抽象组件(Component):定义了原始对象和装饰器对象的共同接口。
- 具体组件(Concrete Component):实现了抽象组件接口,是需要被装饰的原始对象。
- 抽象装饰器(Decorator):继承自抽象组件,包含一个指向抽象组件的引用,并定义了与抽象组件相同的接口。
- 具体装饰器(Concrete Decorator):继承自抽象装饰器,实现了具体的装饰逻辑。
下面是一个使用装饰器模式的Java代码示例:
// 抽象组件 interface Component { void operation(); } // 具体组件 class ConcreteComponent implements Component { @Override public void operation() { System.out.println("执行具体组件的操作"); } } // 抽象装饰器 abstract class Decorator implements Component { protected Component component; public Decorator(Component component) { this.component = component; } @Override public void operation() { component.operation(); } } // 具体装饰器A class ConcreteDecoratorA extends Decorator { public ConcreteDecoratorA(Component component) { super(component); } @Override public void operation() { super.operation(); additionalOperationA(); } private void additionalOperationA() { System.out.println("执行具体装饰器A的附加操作"); } } // 具体装饰器B class ConcreteDecoratorB extends Decorator { public ConcreteDecoratorB(Component component) { super(component); } @Override public void operation() { super.operation(); additionalOperationB(); } private void additionalOperationB() { System.out.println("执行具体装饰器B的附加操作"); } } // 客户端代码 public class Client { public static void main(String[] args) { Component component = new ConcreteComponent(); // 装饰器A对原始对象进行装饰 Component decoratorA = new ConcreteDecoratorA(component); decoratorA.operation(); // 装饰器B对装饰器A进行装饰 Component decoratorB = new ConcreteDecoratorB(decoratorA); decoratorB.operation(); } }
在上面的示例中,`Component`是抽象组件,定义了原始对象和装饰器对象的共同接口。`ConcreteComponent`是具体组件,实现了抽象组件接口,是需要被装饰的原始对象。
`Decorator`是抽象装饰器,继承自抽象组件,并包含一个指向抽象组件的引用,实现了与抽象组件相同的接口。`ConcreteDecoratorA`和`ConcreteDecoratorB`是具体装饰器,继承自抽象装饰器,实现了具体的装饰逻辑。
在客户端代码中,我们首先创建一个具体组件`ConcreteComponent`的实例,表示需要被装饰的原始对象。然后,我们通过创建具体装饰器`ConcreteDecoratorA`并将原始对象作为参数传递给它,对原始对象进行装饰。接着,我们再创建具体装饰器`ConcreteDecoratorB`并将装饰器A作为参数传递给它,对装饰器A进行装饰。
最后,我们调用装饰器对象的`operation()`方法,实际上会依次调用原始对象和每个装饰器对象的`operation()`方法,从而实现了一系列的装饰操作。
通过装饰器模式,我们可以灵活地组合各个装饰器对象,动态地添加新的功能到对象中,同时保持了与原始对象接口的一致性,符合开闭原则。
六,外观模式
1.介绍
外观设计模式(Facade Pattern)是一种结构型设计模式,它提供了一个统一的接口,用来访问子系统中的一群接口。它定义了一个高层接口,使得客户端更容易地使用子系统。
2.代码示例
外观设计模式包含以下几个角色:
- 外观(Facade):为客户端提供了访问子系统中一群接口的简单接口,它需要了解所有子系统的方法或属性,进行组合,以备外界调用。
- 子系统(SubSystem):实现子系统的功能,处理外观对象指派的任务,它对客户端和外观对象是未知的,它内部可以有多个模块。
- 客户端(Client):通过调用外观来完成业务。下面是一个使用外观模式的Java代码示例:
// 子系统1 class SubSystem1 { public void operation() { System.out.println("执行子系统1的操作"); } } // 子系统2 class SubSystem2 { public void operation() { System.out.println("执行子系统2的操作"); } } // 子系统3 class SubSystem3 { public void operation() { System.out.println("执行子系统3的操作"); } } // 外观类 class Facade { private SubSystem1 subSystem1; private SubSystem2 subSystem2; private SubSystem3 subSystem3; public Facade() { subSystem1 = new SubSystem1(); subSystem2 = new SubSystem2(); subSystem3 = new SubSystem3(); } // 提供给客户端的简单接口 public void doWork() { subSystem1.operation(); subSystem2.operation(); subSystem3.operation(); } } // 客户端代码 public class Client { public static void main(String[] args) { Facade facade = new Facade(); facade.doWork(); } }
在上面的示例中,`SubSystem1`、`SubSystem2`和`SubSystem3`表示子系统,它们实现了子系统的功能,处理外观对象指派的任务。
`Facade`是外观类,为客户端提供了访问子系统中一群接口的简单接口,它需要了解所有子系统的方法或属性,进行组合,以备外界调用。
在客户端代码中,我们首先创建一个外观类`Facade`的实例,然后通过它的简单接口`doWork()`来访问子系统中的一群接口。实际上,`doWork()` 方法内部会依次调用子系统对象的 `operation()` 方法,从而完成一系列的操作。
通过外观设计模式,客户端可以更容易地访问子系统中的一群接口,而不必知道底层子系统的具体实现,从而实现了松耦合和高内聚。
七,享元模式
1.介绍
享元设计模式(Flyweight Pattern)是一种结构型设计模式,它旨在通过共享尽可能多的对象来有效地支持大量细粒度对象的复用。
享元设计模式通过将对象的状态分为内部状态和外部状态来实现对象的共享。内部状态是指可以被共享的对象的状态,而外部状态是不可共享的对象的状态。
2.代码示例
享元设计模式包含以下几个角色:
- 享元工厂(Flyweight Factory):负责创建和管理享元对象,其中包括对享元对象的缓存池。
- 享元(Flyweight):定义了享元对象的接口,通过这个接口可以接收并作用于外部状态。
- 具体享元(Concrete Flyweight):实现了享元接口,并为内部状态增加存储空间。
- 客户端(Client):使用享元对象的客户端,需要维护对外部状态的引用。
下面是一个使用享元模式的Java代码示例:
// 享元接口 interface Flyweight { void operation(String externalState); } // 具体享元 class ConcreteFlyweight implements Flyweight { private String internalState; public ConcreteFlyweight(String internalState) { this.internalState = internalState; } @Override public void operation(String externalState) { System.out.println("内部状态:" + internalState); System.out.println("外部状态:" + externalState); // 对内部状态和外部状态进行操作 } } // 享元工厂 class FlyweightFactory { private Map<String, Flyweight> flyweights = new HashMap<>(); public Flyweight getFlyweight(String key) { if (flyweights.containsKey(key)) { return flyweights.get(key); } else { Flyweight flyweight = new ConcreteFlyweight(key); flyweights.put(key, flyweight); return flyweight; } } } // 客户端代码 public class Client { public static void main(String[] args) { FlyweightFactory factory = new FlyweightFactory(); // 获取享元对象,并传入外部状态 Flyweight flyweight1 = factory.getFlyweight("key1"); flyweight1.operation("externalState1"); Flyweight flyweight2 = factory.getFlyweight("key2"); flyweight2.operation("externalState2"); Flyweight flyweight3 = factory.getFlyweight("key1"); flyweight3.operation("externalState3"); } }
在上面的示例中,`Flyweight`是享元接口,定义了享元对象的接口方法`operation()`,其中传入了外部状态。`ConcreteFlyweight`是具体享元,实现了享元接口,并为内部状态增加了存储空间。
`FlyweightFactory`是享元工厂,负责创建和管理享元对象。它维护了一个享元对象的缓存池,通过`getFlyweight()`方法获取享元对象。如果缓存池中已经存在对应的享元对象,则直接返回;否则,创建新的享元对象并放入缓存池中。
在客户端代码中,我们首先创建了一个享元工厂对象`FlyweightFactory`。然后通过调用工厂的`getFlyweight()`方法来获取享元对象,并传入外部状态。实际上,当获取对象时,如果缓存池中已经存在对应的享元对象,则返回缓存池中的对象;否则,创建新的享元对象并放入缓存池中。
通过享元设计模式,我们可以有效地复用细粒度对象,并减少内存消耗。当多个对象共享一些相似的状态时,可以使用享元模式来减少对象的数量,提高系统的性能。
八,代理模式
1.介绍
代理设计模式是一种结构型设计模式,它允许通过提供一个代理对象来控制对另一个对象的访问。这种模式可以用于实现许多功能,例如远程代理、虚拟代理、保护代理等。
2.代码示例
下面是一个简单的Java代码示例,演示了如何使用代理设计模式:
// 定义一个接口 interface Image { void display(); } // 创建实现接口的具体类 class RealImage implements Image { private String filename; public RealImage(String filename) { this.filename = filename; loadFromDisk(); } private void loadFromDisk() { System.out.println("从磁盘加载图像:" + filename); } public void display() { System.out.println("显示图像:" + filename); } } // 创建代理类,实现同样的接口 class ProxyImage implements Image { private String filename; private RealImage realImage; public ProxyImage(String filename) { this.filename = filename; } public void display() { if (realImage == null) { realImage = new RealImage(filename); } realImage.display(); } } // 使用代理类进行访问 public class ProxyPatternExample { public static void main(String[] args) { Image image1 = new ProxyImage("image1.jpg"); Image image2 = new ProxyImage("image2.jpg"); // 第一次访问图像,会从磁盘加载 image1.display(); // 第二次访问图像,直接显示,不再加载 image1.display(); // 第一次访问图像,会从磁盘加载 image2.display(); } }
在上述示例中,`RealImage`类表示真实的图像对象,`ProxyImage`类是它的代理类。当需要显示图像时,首先通过代理类进行访问。代理类检查是否已经创建了真实的图像对象,如果没有,则创建并加载图像,然后调用真实图像对象的方法进行显示。如果已经创建了真实的图像对象,则直接调用其方法显示图像。
这样,通过代理类可以控制对真实图像对象的访问,并且可以在访问前后进行一些额外的操作,例如延迟加载、权限控制等。
九,另类幽默讲解一下结构型设计模式
1.幽默另类介绍
从前有一只叫做小鸟的动物,它经常飞行于城市上空。小鸟非常喜欢观察人类在城市大楼中的生活。但是有一天,小鸟突然发现自己不再能够像以前那样轻松地飞行了。原来,城市上空出现了越来越多的建筑物和高层大楼,空气中充满了各种障碍物。小鸟开始感到迷茫和困惑。
于是,小鸟想了一个聪明的主意,它开始学习建筑师们使用的设计模式,来更好地适应这个充满挑战的城市环境。
首先,小鸟学习了适配器模式。适配器模式可以将不兼容的接口转换成可用的接口。小鸟现在可以轻松地使用锅炉排气口和空调的风口在城市大楼之间飞行,就像使用电源适配器一样。
接着,小鸟学习了桥接模式。桥接模式可以将抽象部分与实现部分分离开来,使得二者可以独立变化。小鸟现在可以像穿过桥一样,在各个城市大楼之间自由穿梭,而不必担心建筑物的具体实现方式。
然后,小鸟学习了装饰器模式。装饰器模式可以动态地为对象添加额外的功能,而不必修改其源代码。小鸟现在可以使用各种工具和道具来改变自己的飞行表现,例如使用跑步机可以增加速度,使用翅膀上的小喇叭可以发出各种有趣的声音。
最后,小鸟学习了组合模式。组合模式可以将对象组合成树形结构,以表示“整体/部分”的层次结构。小鸟现在可以在城市大楼之间形成一个网络,像一只大鸟一样自由飞行。
经过这些学习,小鸟终于成功地适应了城市环境,并成为了一位优秀的城市观察家。从此,小鸟在人们中间赢得了美誉,因为它总是能够在一次次的飞越中带给人们欢笑和启示。
结构型设计模式,就像小鸟学到的这些技能一样,可以帮助我们更好地适应复杂多变的系统和环境,提高程序的可维护性和灵活性。