Java设计模式之结构型模式详解
发布时间: 2023-12-17 05:03:01 阅读量: 40 订阅数: 41
JAVA设计模式之结构模式
# 1. 引言
## 1.1 什么是Java设计模式
设计模式是在软件设计中常用的解决问题的一种方法或者思路,这些设计模式是经过了实践和大量的总结得出的一种被普遍认可的最佳实践。Java设计模式包括了23种设计模式,它们分别属于三种类型:创建型模式、结构型模式和行为型模式。
## 1.2 为什么要使用设计模式
使用设计模式可以提高代码的可重用性、可维护性和可扩展性。设计模式提供了一种标准化的解决方案,使得代码更易于理解和维护。此外,使用设计模式可以提高代码的效率,减少犯错的可能性。
## 1.3 结构型模式的作用
结构型模式是Java设计模式中的一种类型,它主要关注类和对象之间的组合。结构型模式可以将类和对象以某种方式组织起来,以达到更好的结构和设计。它通过将类和对象组织成更大的结构,从而实现更高层次的抽象和复杂性管理。
在本文中,我们将重点介绍五种常见的结构型模式: 适配器模式、装饰器模式、外观模式、代理模式和组合模式。
# 2. 适配器模式
适配器模式(Adapter Pattern)是一种结构型设计模式,它允许接口不兼容的对象之间合作。适配器模式可以将一个类的接口转换为客户端所期望的另一个接口。这种适配器模式通常用于需要复用一些现存的类,但接口与需求不符合的情况。
#### 2.1 概述
适配器模式的核心思想是创建一个新的适配器类,该类实现客户端所期望的目标接口,并持有一个对旧接口的引用。通过将客户端的请求委派给旧接口的实现来实现目标接口的行为,从而实现了接口之间的适配。
#### 2.2 适用场景
- 当需要使用一个已经存在的类,但它的接口不符合需求时
- 当需要创建一个可复用的类,该类与其他不相关的或不可预见的类协作
#### 2.3 实现方法
适配器模式可以通过对象适配器和类适配器两种方式实现。对象适配器使用组合的方式持有一个旧接口的引用,而类适配器则使用多重继承等方式实现接口的适配。
#### 2.4 实例分析
假设我们有一个音乐播放器,它只能播放MP3格式的音乐,但是现在有一些其他格式的音乐(比如MP4、WAV等),我们需要通过适配器模式来实现适配。
```java
// 目标接口
public interface MediaPlayer {
public void play(String audioType, String fileName);
}
// 旧接口
public interface AdvancedMediaPlayer {
public void playMp4(String fileName);
public void playWav(String fileName);
}
// 适配器类
public class MediaAdapter implements MediaPlayer {
private AdvancedMediaPlayer advancedMusicPlayer;
public MediaAdapter(String audioType) {
if (audioType.equalsIgnoreCase("mp4")) {
advancedMusicPlayer = new Mp4Player();
} else if (audioType.equalsIgnoreCase("wav")) {
advancedMusicPlayer = new WavPlayer();
}
}
public void play(String audioType, String fileName) {
if (audioType.equalsIgnoreCase("mp4")) {
advancedMusicPlayer.playMp4(fileName);
} else if (audioType.equalsIgnoreCase("wav")) {
advancedMusicPlayer.playWav(fileName);
}
}
}
```
在上面的示例中,我们通过适配器模式创建了 `MediaAdapter` 类来实现 `MediaPlayer` 接口,该适配器类持有对 `AdvancedMediaPlayer` 接口的引用,并根据需要调用不同的方法来实现对不同音乐格式的播放。
这个例子清楚地展示了适配器模式的作用和实现方法。
# 3. 装饰器模式
装饰器模式是一种结构型设计模式,允许向对象动态添加新功能,而不必更改其原始代码。这种模式能够有效地组合对象,以便无需使用子类就能添加新行为。
### 概述
装饰器模式通过将对象包装在装饰器类的实例中来扩展其行为。这种模式允许逐渐添加功能,而不是一次性对整个对象进行改动。
### 适用场景
装饰器模式通常在以下情况下使用:
- 当需要扩展一个类的功能,而继承会导致子类数量急剧增加时。
- 当要动态地为对象添加功能,而不会影响其他对象时。
### 实现方法
装饰器模式通常涉及以下角色:
1. **Component(组件)**:定义一个对象接口,可以动态地给这个接口添加职责。
2. **ConcreteComponent(具体组件)**:实现Component接口的对象,是被装饰的原始对象。
3. **Decorator(装饰器)**:持有一个Component对象的实例,并定义一个和Component接口一致的接口。
4. **ConcreteDecorator(具体装饰器)**:负责向组件添加新的职责。
### 实例分析
下面是一个使用装饰器模式的实例,假设有一个`Coffee`类表示咖啡,我们可以为咖啡添加调料(如牛奶、糖)来增强其功能。我们可以使用装饰器模式来实现该功能。
# 4. 外观模式
外观模式(Facade Pattern)是一种结构型设计模式,它为子系统提供一个统一的接口,封装了子系统的复杂性,简化了外部调用者与子系统之间的交互。外观模式通过一个高层接口,帮助客户端代码与子系统进行交互,降低了客户端代码与子系统之间的耦合度,提高了系统的相对独立性与可维护性。
##### 4.1 概述
外观模式主要包括三个角色:外观(Facade)、子系统(SubSystem)、客户端(Client)。外观作为客户端与子系统之间的接口层,隐藏了子系统的复杂性,使得客户端能够更加简单地使用子系统功能。
##### 4.2 适用场景
- 当存在复杂的子系统,且需要对外提供简单接口时,可以考虑使用外观模式。
- 当需要对外封装子系统,降低客户端与子系统之间的耦合度时,可以考虑使用外观模式。
##### 4.3 实现方法
外观模式的实现方法主要是定义一个外观类,该类提供了一个简单接口,用于与客户端交互,并将客户端的请求转发给子系统进行处理。
##### 4.4 实例分析
假设有一个电商系统,包括订单系统、支付系统、库存系统等多个子系统,客户端需要完成下单操作。我们可以使用外观模式,定义一个名为`OrderFacade`的外观类,提供一个`placeOrder`方法,客户端只需调用该方法即可完成下单操作,而不需要关心订单、支付、库存等子系统的具体实现细节。
```java
// 外观类
public class OrderFacade {
private OrderService orderService;
private PaymentService paymentService;
private InventoryService inventoryService;
public OrderFacade() {
this.orderService = new OrderService();
this.paymentService = new PaymentService();
this.inventoryService = new InventoryService();
}
public void placeOrder(Order order) {
if (inventoryService.isAvailable(order.getProduct())) {
orderService.createOrder(order);
paymentService.processPayment(order);
inventoryService.updateInventory(order.getProduct());
System.out.println("Order placed successfully!");
} else {
System.out.println("Product out of stock, order placement failed.");
}
}
}
// 客户端
public class Client {
public static void main(String[] args) {
Order order = new Order("123", "Sample Product");
OrderFacade orderFacade = new OrderFacade();
orderFacade.placeOrder(order);
}
}
```
在上面的示例中,`OrderFacade`作为外观类,隐藏了订单、支付、库存等子系统的复杂性,客户端只需要通过`OrderFacade`的`placeOrder`方法即可完成整个下单流程,而不需要与各个子系统进行直接交互。
以上是外观模式的一个简单示例,通过外观模式,我们可以有效地简化客户端代码,降低了客户端与子系统之间的耦合度,提高了系统的灵活性与可维护性。
# 5. 代理模式
### 5.1 概述
代理模式是一种结构型设计模式,它可以在不改变原始类(被代理类)的情况下,通过引入代理类来对原始类的功能进行扩展或增强。代理类可以控制对原始类的访问,并在调用原始类的方法前后进行一些预处理和后处理操作。
### 5.2 适用场景
- 远程代理:为不同地址空间的对象提供本地代表。远程代理负责对请求及其参数进行编码,并向不同地址空间中的实际对象发送已编码的请求。
- 虚拟代理:用于按需创建昂贵对象的代理。在需要时,代理才会创建真实对象,并将请求传递给它。
- 保护代理:控制对敏感对象的访问,以确保只有合适的用户能够访问该对象。
### 5.3 实现方法
代理模式的实现通常涉及以下角色:
- 抽象接口:定义了代理类和原始类共同实现的接口。
- 原始类:实现了抽象接口的类,是被代理的真实对象。
- 代理类:实现了抽象接口,并有一个指向原始类的引用,并在其方法的调用前后执行相关操作。
### 5.4 实例分析
```java
// 抽象接口
interface Image {
void display();
}
// 原始类
class RealImage implements Image {
private String fileName;
public RealImage(String fileName) {
this.fileName = fileName;
loadFromDisk(fileName);
}
@Override
public void display() {
System.out.println("Displaying " + fileName);
}
private void loadFromDisk(String fileName) {
System.out.println("Loading " + fileName + " from disk");
}
}
// 代理类
class ProxyImage implements Image {
private RealImage realImage;
private String fileName;
public ProxyImage(String fileName) {
this.fileName = fileName;
}
@Override
public void display() {
if (realImage == null) {
realImage = new RealImage(fileName);
}
realImage.display();
}
}
// 测试类
public class ProxyPatternDemo {
public static void main(String[] args) {
Image image = new ProxyImage("test.jpg");
// 图像将从磁盘加载
image.display();
// 图像不需要从磁盘加载
image.display();
}
}
```
上面的示例中,我们使用了代理模式来延迟加载一个高分辨率的图像,并且在需要时才真正加载。第一次调用`display`方法时,需要从磁盘加载图像,而第二次调用时,由于已经存在代理对象,不需要再次加载。这样就能实现对原始类方法调用的控制和增强。
# 6. 组合模式
#### 6.1 概述
组合模式是一种结构型设计模式,它允许你将对象组合成树形结构以表示“整体-部分”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性,可以用于处理树形结构的问题。
#### 6.2 适用场景
- 当你希望客户端代码以相同的方式处理单个对象和对象组合时,可以考虑使用组合模式。例如,文件系统中的文件和目录都可以用组合模式来表示。
- 当您希望在不考虑组合对象和单个对象的具体类别的情况下对它们进行操作时,组合模式是一个不错的选择。
#### 6.3 实现方法
组合模式通过将对象组合成树形结构来表示整体-部分关系。通常包括以下组件:
- **Component(组件)**:是组合中的对象声明接口,在适当情况下,实现所有类共有接口的默认行为。
- **Leaf(叶节点)**:代表树结构的叶节点对象,叶节点没有子节点。
- **Composite(容器)**:定义有子部件的那些部件的行为,并对子部件进行管理。
#### 6.4 实例分析
```java
// Component
interface Graphic {
void draw();
}
// Leaf
class Line implements Graphic {
@Override
public void draw() {
System.out.println("Drawing a Line");
}
}
class Circle implements Graphic {
@Override
public void draw() {
System.out.println("Drawing a Circle");
}
}
// Composite
class CompositeGraphic implements Graphic {
private List<Graphic> graphics = new ArrayList<>();
@Override
public void draw() {
for (Graphic graphic : graphics) {
graphic.draw();
}
}
public void add(Graphic graphic) {
graphics.add(graphic);
}
public void remove(Graphic graphic) {
graphics.remove(graphic);
}
}
// Usage
public class Main {
public static void main(String[] args) {
// Create a composite graphic
CompositeGraphic compositeGraphic = new CompositeGraphic();
// Add graphic objects to the composite graphic
compositeGraphic.add(new Line());
compositeGraphic.add(new Circle());
// Print the composite graphic
compositeGraphic.draw();
}
}
```
在上面的示例中,我们实现了一个简单的组合模式。`Graphic` 接口是组件对象的声明,`Line` 和 `Circle` 分别是叶节点对象,`CompositeGraphic` 是容器对象。在 `Main` 类中,我们创建了一个复合图形对象 `compositeGraphic`,并向其中添加了直线和圆形。最终打印出这个复合图形对象时,会依次调用每个子对象的 `draw` 方法,实现了整体-部分结构的处理。
通过组合模式,我们可以以统一的方式处理单个对象和对象组合,使得客户端代码可以一致地对待它们。
0
0