Java中常用的设计模式及应用场景
发布时间: 2024-02-14 06:09:54 阅读量: 39 订阅数: 44
# 1. 简介
## 1.1 什么是设计模式
设计模式是软件工程中一种被广泛应用的解决方案,它提供了一套经过验证的设计思想和设计方法,用于解决软件设计和开发过程中常见的问题。设计模式是从实际的软件开发经验中总结出来的,它们描述了在特定情境下,解决问题的最佳实践方法。
## 1.2 设计模式的作用
设计模式的主要作用是提高软件的可复用性、可扩展性和可维护性。通过在软件设计过程中应用设计模式,可以使得系统更加灵活、可靠,并且易于理解和维护。设计模式在面向对象编程中起到了桥梁的作用,帮助对象之间构建良好的关系,促进代码的复用和可读性。
## 1.3 Java中常用的设计模式概述
Java中常用的设计模式可以分为三大类:创建型模式、结构型模式和行为型模式。创建型模式主要解决对象的创建问题,包括单例模式、工厂模式等;结构型模式主要解决类的组合和对象之间的关系问题,包括适配器模式、装饰器模式等;行为型模式主要解决对象之间的通信和控制问题,包括观察者模式、命令模式等。在Java开发中,这些设计模式被广泛应用,成为了程序员们解决常见问题的利器。在接下来的章节中,我们将逐一介绍这些常用的设计模式,并给出具体的应用示例和代码实现。
# 2. 创建型模式
创建型模式主要用于对象的创建和实例化过程,通过隐藏对象的创建细节,提供一个统一的接口来创建对象。
### 2.1 单例模式
单例模式是一种对象创建型设计模式,它保证一个类只有一个实例,并提供一个全局访问点。
#### 2.1.1 懒汉式单例模式
懒汉式单例模式是指在需要获取单例对象时才会进行对象的创建。
```java
public class LazySingleton {
private static LazySingleton instance;
private LazySingleton() {
// 私有化构造方法,只能在类内部进行实例化
}
public static synchronized LazySingleton getInstance() {
if (instance == null) {
instance = new LazySingleton();
}
return instance;
}
}
```
##### 场景
```java
public class Main {
public static void main(String[] args) {
LazySingleton singleton1 = LazySingleton.getInstance();
LazySingleton singleton2 = LazySingleton.getInstance();
System.out.println(singleton1 == singleton2); // true
}
}
```
##### 代码解析
在懒汉式单例模式中,通过双重检查锁定(double-checked locking)的方式来实现延迟加载,保证了懒汉式的线程安全性。
在单线程环境下,懒汉式单例模式可以正常工作。但在多线程环境下,有可能会出现多个实例的情况。为了避免这种情况,需要对`getInstance()`方法进行同步,使用`synchronized`关键字锁住方法,以保证在同一时间仅有一个线程进入创建实例的代码块。
#### 2.1.2 饿汉式单例模式
饿汉式单例模式是指未被使用时已经创建好了实例。
```java
public class EagerSingleton {
private static final EagerSingleton instance = new EagerSingleton();
private EagerSingleton() {
// 私有化构造方法,只能在类内部进行实例化
}
public static EagerSingleton getInstance() {
return instance;
}
}
```
##### 场景
```java
public class Main {
public static void main(String[] args) {
EagerSingleton singleton1 = EagerSingleton.getInstance();
EagerSingleton singleton2 = EagerSingleton.getInstance();
System.out.println(singleton1 == singleton2); // true
}
}
```
##### 代码解析
在饿汉式单例模式中,实例是在类加载时初始化的,所以在多线程环境下也可以正常工作,不会出现多个实例的情况。
### 2.2 工厂模式
工厂模式是一种对象创建型设计模式,它提供了一种抽象的方式来创建对象,将对象的创建过程隐藏在具体的工厂类中。
#### 2.2.1 简单工厂模式
简单工厂模式是一种简单的工厂模式,它由一个工厂类负责创建所有对象。
```java
public interface Shape {
void draw();
}
public class Circle implements Shape {
@Override
public void draw() {
System.out.println("Drawing a circle");
}
}
public class Rectangle implements Shape {
@Override
public void draw() {
System.out.println("Drawing a rectangle");
}
}
public class ShapeFactory {
public Shape getShape(String shapeType) {
if (shapeType == null) {
return null;
}
if (shapeType.equalsIgnoreCase("circle")) {
return new Circle();
} else if (shapeType.equalsIgnoreCase("rectangle")) {
return new Rectangle();
}
return null;
}
}
```
##### 场景
```java
public class Main {
public static void main(String[] args) {
ShapeFactory shapeFactory = new ShapeFactory();
Shape circle = shapeFactory.getShape("circle");
circle.draw(); // Drawing a circle
Shape rectangle = shapeFactory.getShape("rectangle");
rectangle.draw(); // Drawing a rectangle
}
}
```
##### 代码解析
在简单工厂模式中,通过一个工厂类来创建具体的对象。工厂类根据给定的参数来决定创建哪种对象。客户端只需要知道工厂类和对象的接口,不需要关心对象的具体创建过程。
#### 2.2.2 抽象工厂模式
抽象工厂模式是一种更加复杂的工厂模式,它通过提供一个抽象的工厂类和一组具体的工厂子类来创建一系列相关的对象。
```java
public interface Shape {
void draw();
}
public class Circle implements Shape {
@Override
public void draw() {
System.out.println("Drawing a circle");
}
}
public class Rectangle implements Shape {
@Override
public void draw() {
System.out.println("Drawing a rectangle");
}
}
public interface Color {
void fill();
}
public class Red implements Color {
@Override
public void fill() {
System.out.println("Filling with red color");
}
}
public class Blue implements Color {
@Override
public void fill() {
System.out.println("Filling with blue color");
}
}
public abstract class AbstractFactory {
abstract Shape getShape(String shapeType);
abstract Color getColor(String colorType);
}
public class ShapeFactory extends AbstractFactory {
@Override
public Shape getShape(String shapeType) {
if (shapeType == null) {
return null;
}
if (shapeType.equalsIgnoreCase("circle")) {
return new Circle();
} else if (shapeType.equalsIgnoreCase("rectangle")) {
return new Rectangle();
}
return null;
}
@Override
public Color getColor(String colorType) {
return null;
}
}
public class ColorFactory extends AbstractFactory {
@Override
public Shape getShape(String shapeType) {
return null;
}
@Override
public Color getColor(String colorType) {
if (colorType == null) {
return null;
}
if (colorType.equalsIgnoreCase("red")) {
return new Red();
} else if (colorType.equalsIgnoreCase("blue")) {
return new Blue();
}
return null;
}
}
public class FactoryProducer {
public static AbstractFactory getFactory(String factoryType) {
if (factoryType.equalsIgnoreCase("shape")) {
return new ShapeFactory();
} else if (factoryType.equalsIgnoreCase("color")) {
return new ColorFactory();
}
return null;
}
}
```
##### 场景
```java
public class Main {
public static void main(String[] args) {
AbstractFactory shapeFactory = FactoryProducer.getFactory("shape");
Shape circle = shapeFactory.getShape("circle");
circle.draw(); // Drawing a circle
Shape rectangle = shapeFactory.getShape("rectangle");
rectangle.draw(); // Drawing a rectangle
AbstractFactory colorFactory = FactoryProducer.getFactory("color");
Color red = colorFactory.getColor("red");
red.fill(); // Filling with red color
Color blue = colorFactory.getColor("blue");
blue.fill(); // Filling with blue color
}
}
```
##### 代码解析
在抽象工厂模式中,通过抽象工厂类和一组具体的工厂子类来创建一系列相关的对象。抽象工厂类拥有一组工厂方法,每个工厂方法负责创建一类具体的对象。
通过工厂生产者类来获取具体的工厂类,再通过具体的工厂类来创建具体的对象。客户端只需要知道工厂生产者类和抽象工厂类,不需要关心具体的对象和工厂类的实现细节。
# 3. 结构型模式
结构型设计模式关注对象之间的组合,以创造出更大的结构,它们涉及到如何建立类或对象之间的关系,以形成更大的结构。
#### 3.1 适配器模式
适配器模式是一种结构型设计模式,它允许接口不兼容的对象能够进行合作。它包括一个称为适配器的类,允许两个不兼容的接口进行交互。
**示例场景:**
假设有一个音乐播放器,它只能播放MP3格式的音乐,但现在我们想要播放其他格式的音乐文件,比如MP4格式或VLC格式。
```java
public interface MediaPlayer {
public void play(String audioType, String fileName);
}
public interface AdvancedMediaPlayer {
public void playVlc(String fileName);
public void playMp4(String fileName);
}
public class VlcPlayer implements AdvancedMediaPlayer{
@Override
public void playVlc(String fileName) {
System.out.println("Playing vlc file. Name: " + fileName);
}
@Override
public void playMp4(String fileName) {
//什么也不做
}
}
public class Mp4Player implements AdvancedMediaPlayer{
@Override
public void playVlc(String fileName) {
//什么也不做
}
@Override
public void playMp4(String fileName) {
System.out.println("Playing mp4 file. Name: " + fileName);
}
}
public class MediaAdapter implements MediaPlayer {
AdvancedMediaPlayer advancedMusicPlayer;
public MediaAdapter(String audioType){
if(audioType.equalsIgnoreCase("vlc") ){
advancedMusicPlayer = new VlcPlayer();
} else if (audioType.equalsIgnoreCase("mp4")){
advancedMusicPlayer = new Mp4Player();
}
}
@Override
public void play(String audioType, String fileName) {
if(audioType.equalsIgnoreCase("vlc")){
advancedMusicPlayer.playVlc(fileName);
}else if(audioType.equalsIgnoreCase("mp4")){
advancedMusicPlayer.playMp4(fileName);
}
}
}
public class AudioPlayer implements MediaPlayer {
MediaAdapter mediaAdapter;
@Override
public void play(String audioType, String fileName) {
//播放MP3文件的内置支持
if(audioType.equalsIgnoreCase("mp3")){
System.out.println("Playing mp3 file. Name: " + fileName);
}
//mediaAdapter 提供了播放其他文件格式的支持
else if(audioType.equalsIgnoreCase("vlc") || audioType.equalsIgnoreCase("mp4")){
mediaAdapter = new MediaAdapter(audioType);
mediaAdapter.play(audioType, fileName);
}
else{
System.out.println("Invalid media. " + audioType + " format not supported");
}
}
}
public class AdapterPatternDemo {
public static void main(String[] args) {
AudioPlayer audioPlayer = new AudioPlayer();
audioPlayer.play("mp3", "beyond the horizon.mp3");
audioPlayer.play("mp4", "alone.mp4");
audioPlayer.play("vlc", "far far away.vlc");
audioPlayer.play("avi", "mind me.avi");
}
}
```
**代码总结:**
在这个示例中,我们使用适配器模式允许音乐播放器对象播放其他格式的音乐文件,而无需修改原始类的代码,这样就实现了接口不兼容的对象能够进行合作。
**结果说明:**
当我们运行`AdapterPatternDemo`类时,会输出不同格式的音乐文件的播放信息,证明了适配器模式的有效性。
#### 3.2 装饰器模式
装饰器模式允许向一个现有的对象添加新的功能,同时又不改变其结构。
**示例场景:**
假设我们有一个咖啡店,顾客可以选择普通咖啡或者添加额外的配料,比如牛奶、糖浆等。
```java
public interface Coffee {
public double getCost();
public String getIngredients();
}
public class SimpleCoffee implements Coffee {
@Override
public double getCost() {
return 1;
}
@Override
public String getIngredients() {
return "Coffee";
}
}
public abstract class CoffeeDecorator implements Coffee {
protected final Coffee decoratedCoffee;
public CoffeeDecorator(Coffee decoratedCoffee) {
this.decoratedCoffee = decoratedCoffee;
}
public double getCost() {
return decoratedCoffee.getCost();
}
public String getIngredients() {
return decoratedCoffee.getIngredients();
}
}
public class Milk extends CoffeeDecorator {
public Milk(Coffee decoratedCoffee) {
super(decoratedCoffee);
}
@Override
public double getCost() {
return super.getCost() + 0.5;
}
@Override
public String getIngredients() {
return super.getIngredients() + ", Milk";
}
}
public class Whip extends CoffeeDecorator {
public Whip(Coffee decoratedCoffee) {
super(decoratedCoffee);
}
@Override
public double getCost() {
return super.getCost() + 0.7;
}
@Override
public String getIngredients() {
return super.getIngredients() + ", Whip";
}
}
public class DecoratorPatternDemo {
public static void main(String[] args) {
Coffee c = new SimpleCoffee();
System.out.println("Cost: " + c.getCost() + "; Ingredients: " + c.getIngredients());
c = new Milk(c);
System.out.println("Cost: " + c.getCost() + "; Ingredients: " + c.getIngredients());
c = new Whip(c);
System.out.println("Cost: " + c.getCost() + "; Ingredients: " + c.getIngredients());
}
}
```
**代码总结:**
在这个示例中,我们使用装饰器模式为咖啡对象添加了额外的配料,而不改变原始类的结构。
**结果说明:**
当我们运行`DecoratorPatternDemo`类时,会输出加入不同配料的咖啡的成本和配料信息,证明了装饰器模式的有效性。
# 4. 行为型模式
行为型模式主要关注对象之间的通信和协作,着重于对象之间的交互和职责分配。以下是四种常见的行为型模式:
#### 4.1 观察者模式
观察者模式定义了一种一对多的关系,即一个主题对象可以被多个观察者对象同时监听,当主题状态发生变化时,会通知所有的观察者进行更新。观察者模式包括以下几个角色:
- Subject(主题):具有注册、移除和通知观察者的方法,维护一份观察者列表。
- Observer(观察者):接收到主题的通知后进行更新的接口。
- ConcreteSubject(具体主题):实现了Subject接口,维护自身状态,并在状态发生变化时通知观察者。
- ConcreteObserver(具体观察者):实现了Observer接口,被通知后进行更新的具体实现。
观察者模式的应用场景包括:新闻订阅、股票行情等实时更新的系统。
#### 4.2 命令模式
命令模式将请求封装成一个对象,使得请求发送者与请求接收者之间解耦。请求发送者只需要直接与命令对象进行交互,而不需要了解请求的具体执行细节。命令模式包括以下几个角色:
- Command(命令):定义了执行操作的接口,通常包含一个execute方法。
- ConcreteCommand(具体命令):Command的实现类,负责具体的命令执行。
- Receiver(接收者):真正执行命令的对象。
- Invoker(请求者):调用命令对象执行命令的对象。
命令模式的应用场景包括:任务调度器、UNDO/REDO操作等。
#### 4.3 策略模式
策略模式定义了一系列具有相同接口的算法族,将每个算法都封装成独立的类,使它们之间可以互换。策略模式包括以下几个角色:
- Context(环境类):持有一个策略对象的引用,提供统一的接口供客户端调用。
- Strategy(策略类):定义所有支持的算法的公共接口。
- ConcreteStrategy(具体策略类):实现了Strategy接口的具体算法。
策略模式的应用场景包括:支付方式选择、排序算法选择等。
#### 4.4 模板方法模式
模板方法模式定义了一个操作中的算法骨架,将某些步骤延迟到子类中实现。模板方法模式包括以下几个角色:
- AbstractClass(抽象类):定义了一个模板方法,它给出了算法的骨架或者是步骤,并且每个步骤都可以是具体方法或者是抽象方法。
- ConcreteClass(具体类):实现了抽象类中定义的抽象方法,完成算法中的具体步骤。
模板方法模式的应用场景包括:数据库访问操作、框架中的钩子方法等。
以上是行为型模式的简介和常见应用场景。通过使用这些设计模式,可以提高代码的重用性、灵活性和可维护性,使得系统更加易于扩展和修改。
# 5. 迭代器与组合模式
在这一章节中,我们将介绍迭代器模式和组合模式,它们分别属于行为型模式和结构型模式。
### 5.1 迭代器模式
迭代器模式是指提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露该对象的内部表示。
#### 实现迭代器模式的场景和步骤
典型情况是当有一个聚合对象,并且希望隐藏其内部结构,同时希望能够按顺序访问其中的元素。
**实现步骤:**
1. 定义一个迭代器接口,包括遍历方法、判断是否有下一个元素的方法等。
2. 实现聚合对象,并在聚合对象中定义获取迭代器的方法。
3. 实现迭代器接口,并根据实际情况在具体迭代器中实现遍历方法。
#### Python示例:
```python
# 定义一个迭代器接口
class Iterator:
def has_next(self) -> bool:
pass
def next(self):
pass
# 实现一个聚合对象
class MyList:
def __init__(self):
self.data = []
def add(self, value):
self.data.append(value)
def get_iterator(self):
return MyListIterator(self.data)
# 实现迭代器接口
class MyListIterator(Iterator):
def __init__(self, data):
self.data = data
self.index = 0
def has_next(self):
return self.index < len(self.data)
def next(self):
value = self.data[self.index]
self.index += 1
return value
# 使用示例
my_list = MyList()
my_list.add(1)
my_list.add(2)
my_list.add(3)
iterator = my_list.get_iterator()
while iterator.has_next():
print(iterator.next())
```
**代码总结:** 上述示例演示了迭代器模式的基本实现,利用迭代器接口和具体迭代器来遍历聚合对象中的元素。
**结果说明:** 运行示例代码将按顺序输出聚合对象中的元素。
### 5.2 组合模式
组合模式是一种结构型模式,它允许我们将对象组合成树形结构以表示"部分-整体"的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。
#### 实现组合模式的场景和步骤
典型情况是当有一个对象可能包含其他对象,并且希望对这些对象进行统一处理时,可以考虑使用组合模式。
**实现步骤:**
1. 定义统一的组件接口,使得叶子对象和组合对象都可以按照统一的方式被访问。
2. 实现叶子对象和组合对象,并在组件接口中定义添加、删除、获取子组件等方法。
3. 在客户端代码中使用统一的组件接口进行操作,无论是叶子对象还是组合对象。
#### Java示例:
```java
// 定义统一的组件接口
public interface Component {
void operation();
}
// 实现叶子对象
public class Leaf implements Component {
@Override
public void operation() {
System.out.println("Leaf operation");
}
}
// 实现组合对象
public 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);
}
@Override
public void operation() {
for (Component component : children) {
component.operation();
}
}
}
// 使用示例
Component leaf1 = new Leaf();
Component leaf2 = new Leaf();
Component composite = new Composite();
composite.add(leaf1);
composite.add(leaf2);
composite.operation();
```
**代码总结:** 上述示例展示了组合模式的基本实现,包括统一的组件接口、叶子对象和组合对象的实现,以及它们的使用方式。
**结果说明:** 运行示例代码将按照统一的方式对叶子对象和组合对象进行操作,实现"部分-整体"的处理。
通过以上示例,我们详细了解了迭代器模式和组合模式的概念、实现步骤以及代码示例。在实际应用中,根据具体的场景可以选择合适的设计模式来提高代码的灵活性和可维护性。
# 6. 模式的应用场景
6.1 使用设计模式解决实际问题的案例分析
设计模式是面向对象软件开发中的重要组成部分,可以帮助开发人员提高代码的可读性、复用性和灵活性。下面我们将通过几个实例来展示设计模式在实际问题中的应用。
案例1: 订单处理系统
假设我们正在开发一个订单处理系统,在该系统中,我们需要根据物品的不同类型和规格,给出对应的价格计算逻辑。这个系统涉及到多个物品类型的价格计算方式,但每个物品类型具体的算法又可能会有不同的实现。这就是一个典型的需求变化频繁的场景,我们可以使用策略模式来实现价格计算的可扩展性。
代码示例:
```java
// 抽象策略接口
public interface PriceCalculator {
double calculate(double price);
}
// 具体策略类1
public class DiscountPriceCalculator implements PriceCalculator {
@Override
public double calculate(double price) {
// 具体的价格计算逻辑
// ...
}
}
// 具体策略类2
public class VIPPriceCalculator implements PriceCalculator {
@Override
public double calculate(double price) {
// 具体的价格计算逻辑
// ...
}
}
// 上下文类
public class PriceContext {
private PriceCalculator calculator;
public void setCalculator(PriceCalculator calculator) {
this.calculator = calculator;
}
public double computePrice(double price) {
return calculator.calculate(price);
}
}
// 使用示例
public class OrderProcessor {
public static void main(String[] args) {
PriceContext context = new PriceContext();
context.setCalculator(new DiscountPriceCalculator());
double totalPrice = context.computePrice(100.0);
System.out.println("Total price: " + totalPrice);
}
}
```
通过策略模式,我们将具体的价格计算逻辑封装在不同的策略类中,可以动态地切换不同的计算方式,同时又保持了计算逻辑的解耦。这样,当需求变化时,我们只需要添加新的策略类或修改现有的策略类即可,不会影响到原有的代码逻辑。
案例2: 日志记录器
在许多应用程序中,我们需要记录系统执行过程中的操作日志,包括错误日志、警告日志和信息日志等。由于日志记录的方式和目标可能会有所不同,我们可以使用装饰器模式来实现灵活的日志记录器。
代码示例:
```java
// 抽象日志记录器接口
public interface Logger {
void log(String message);
}
// 具体日志记录器类
public class FileLogger implements Logger {
@Override
public void log(String message) {
// 将日志信息写入文件
// ...
}
}
// 装饰器类
public abstract class LoggerDecorator implements Logger {
protected Logger logger;
public LoggerDecorator(Logger logger) {
this.logger = logger;
}
@Override
public void log(String message) {
logger.log(message);
}
}
// 具体装饰器类1
public class ErrorLogger extends LoggerDecorator {
public ErrorLogger(Logger logger) {
super(logger);
}
@Override
public void log(String message) {
// 添加错误日志的处理逻辑
// ...
super.log(message);
}
}
// 具体装饰器类2
public class WarningLogger extends LoggerDecorator {
public WarningLogger(Logger logger) {
super(logger);
}
@Override
public void log(String message) {
// 添加警告日志的处理逻辑
// ...
super.log(message);
}
}
// 使用示例
public class LogExample {
public static void main(String[] args) {
Logger logger = new WarningLogger(new ErrorLogger(new FileLogger()));
logger.log("Something happened!"); // 错误日志和警告日志都会记录
}
}
```
通过装饰器模式,我们可以动态地为日志记录器添加新的功能,而无需修改原始的日志记录器类。这样,我们可以根据实际需求来选择、组合不同的装饰器类,以满足不同的日志记录需求。
6.2 比较常用的设计模式在Java开发中的应用实例
设计模式在Java开发中得到了广泛的应用,在实际项目中的使用也相当丰富。以下是一些常见的设计模式在Java开发中的应用实例:
- 单例模式:数据库连接池、线程池等
- 工厂模式:日志记录器工厂、图形界面组件工厂等
- 适配器模式:代码转换器、数据适配器等
- 装饰器模式:IO流类、Swing组件等
- 代理模式:远程代理、虚拟代理等
- 观察者模式:事件监听器、消息推送等
- 命令模式:GUI菜单项、遥控器按钮等
- 策略模式:排序算法、支付方式选择等
- 模板方法模式:数据库事务处理、框架的生命周期方法等
- 迭代器模式:集合类的遍历、文件系统的遍历等
- 组合模式:菜单树、文件系统等
这些是设计模式在实际项目开发中的一些应用场景,通过合理地选择和应用设计模式,我们能够提高代码质量、提升开发效率,并更好地应对需求变化。
0
0