Java中常用的设计模式详解
发布时间: 2023-12-13 02:13:00 阅读量: 40 订阅数: 40
JAVA常用设计模式详解大全
# 1. 介绍设计模式的概念与作用
## 什么是设计模式
设计模式是在面向对象软件设计过程中针对特定问题的可重复使用的解决方案。它是经过反复使用和验证的,被广泛认可的解决问题的方式。设计模式不是一段特定的代码,而是一种解决问题的思路,它描述了在特定情境下的解决方案。
## 设计模式的作用与优势
设计模式的作用主要体现在以下几个方面:
- 提高软件的可维护性和可复用性
- 促进代码的灵活性和扩展性
- 促进设计思想的传播和沉淀
使用设计模式能够使代码更易于理解和维护,降低系统耦合度,提高代码的灵活性和可重用性。
## 设计模式在Java中的应用场景
在Java开发中,设计模式广泛应用于各种场景,如单例模式用于线程池、工厂模式用于对象创建、观察者模式用于事件监听等。设计模式在Java中的应用能够更好地组织代码,提高代码质量和可维护性。
# 2. 创建型设计模式
创建型设计模式主要关注对象的实例化过程,旨在找到更好的方法来创建对象。下面是几种常见的创建型设计模式:
#### 单例模式
单例模式确保一个类只有一个实例,并提供一个全局访问点。这在需要控制资源访问、配置设置等场景中非常有用。以下是一个简单的单例模式示例:
```java
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
```
##### 场景及应用
单例模式适合需要全局访问点的情况,比如线程池、日志记录器、配置文件等。
#### 工厂模式
工厂模式用于创建对象,而不是在代码中直接实例化对象。这样做可以隐藏创建对象的逻辑,并提供更高的灵活性。以下是一个简单的工厂模式示例:
```java
public interface Shape {
void draw();
}
public class Circle implements Shape {
@Override
public void draw() {
System.out.println("Inside Circle::draw() method.");
}
}
public class Rectangle implements Shape {
@Override
public void draw() {
System.out.println("Inside Rectangle::draw() method.");
}
}
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 interface Color {
void fill();
}
public class Red implements Color {
@Override
public void fill() {
System.out.println("Inside Red::fill() method.");
}
}
public class Blue implements Color {
@Override
public void fill() {
System.out.println("Inside Blue::fill() method.");
}
}
public interface Shape {
void draw();
}
public class Circle implements Shape {
@Override
public void draw() {
System.out.println("Inside Circle::draw() method.");
}
}
public class Rectangle implements Shape {
@Override
public void draw() {
System.out.println("Inside Rectangle::draw() method.");
}
}
public abstract class AbstractFactory {
public abstract Color getColor(String color);
public abstract Shape getShape(String shape);
}
public class ShapeFactory extends AbstractFactory {
@Override
public Color getColor(String color) {
return null;
}
@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;
}
}
public class ColorFactory extends AbstractFactory {
@Override
public Color getColor(String color) {
if (color == null) {
return null;
}
if (color.equalsIgnoreCase("RED")) {
return new Red();
} else if (color.equalsIgnoreCase("BLUE")) {
return new Blue();
}
return null;
}
@Override
public Shape getShape(String shape) {
return null;
}
}
```
##### 场景及应用
抽象工厂模式适合在需要创建一系列相关对象的情况,比如在图形界面工具包中需要创建不同操作系统风格的界面元素。
#### 建造者模式
建造者模式用于构建一个复杂对象,将构建过程与表示分离,使得同样的构建过程可以创建不同的表示。以下是一个简单的建造者模式示例:
```java
public class Meal {
private String burger;
private String drink;
public String getBurger() {
return burger;
}
public void setBurger(String burger) {
this.burger = burger;
}
public String getDrink() {
return drink;
}
public void setDrink(String drink) {
this.drink = drink;
}
}
public interface MealBuilder {
void buildBurger();
void buildDrink();
Meal getMeal();
}
public class VegMealBuilder implements MealBuilder {
private Meal meal = new Meal();
@Override
public void buildBurger() {
meal.setBurger("Veg Burger");
}
@Override
public void buildDrink() {
meal.setDrink("Coke");
}
@Override
public Meal getMeal() {
return meal;
}
}
public class NonVegMealBuilder implements MealBuilder {
private Meal meal = new Meal();
@Override
public void buildBurger() {
meal.setBurger("Chicken Burger");
}
@Override
public void buildDrink() {
meal.setDrink("Pepsi");
}
@Override
public Meal getMeal() {
return meal;
}
}
public class MealDirector {
public Meal createMeal(MealBuilder mealBuilder) {
mealBuilder.buildBurger();
mealBuilder.buildDrink();
return mealBuilder.getMeal();
}
}
```
##### 场景及应用
建造者模式适合在创建对象的构建步骤比较复杂且有多种表示时应用,比如在制作套餐时,需要根据顾客选择的不同组合构建复杂的套餐对象。
#### 原型模式
原型模式用于创建复杂对象,通过复制现有对象的方式创建新对象。以下是一个简单的原型模式示例:
```java
import java.util.Hashtable;
public abstract class Shape implements Cloneable {
private String id;
protected String type;
abstract void draw();
public String getType() {
return type;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
@Override
public Object clone() {
Object clone = null;
try {
clone = super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return clone;
}
}
public class Circle extends Shape {
public Circle() {
type = "Circle";
}
@Override
public void draw() {
System.out.println("Inside Circle::draw() method.");
}
}
public class Rectangle extends Shape {
public Rectangle() {
type = "Rectangle";
}
@Override
public void draw() {
System.out.println("Inside Rectangle::draw() method.");
}
}
public class ShapeCache {
private static Hashtable<String, Shape> shapeMap
= new Hashtable<String, Shape>();
public static Shape getShape(String shapeId) {
Shape cachedShape = shapeMap.get(shapeId);
return (Shape) cachedShape.clone();
}
public static void loadCache() {
Circle circle = new Circle();
circle.setId("1");
shapeMap.put(circle.getId(), circle);
Rectangle rectangle = new Rectangle();
rectangle.setId("2");
shapeMap.put(rectangle.getId(), rectangle);
}
}
```
##### 场景及应用
原型模式适合在需要创建成本较大的对象,并且对象之间的区别在于属性值时应用,比如在游戏中创建敌人角色时,根据已有角色创建新角色。
通过上述例子,我们可以看到在实际的软件开发场景中,不同的创建型设计模式可以根据需求选择不同的实现方式,以便更好地管理对象的创建,并在一定程度上提高代码的可维护性和扩展性。
# 3. 结构型设计模式
结构型设计模式关注如何将类或对象组合成更大的结构,以解决系统的组件之间的关系和交互问题。它们通过改变类之间的组合方式来实现系统的灵活性和可扩展性。
下面将介绍几种常见的结构型设计模式及其应用场景。
#### 适配器模式(Adapter Pattern)
适配器模式用于将一个类的接口转换成客户端所期望的另一个接口,从而使原本不兼容的类能够合作无间。适配器模式可以分为类适配器和对象适配器两种实现方式。
**适用场景:**
- 当希望使用一个已有的类,但其接口与其他代码不兼容时,可以使用适配器模式进行接口转换。
- 当希望复用一些现有的类,但是这些类的接口与系统的其他部分不一致时,可以使用适配器模式将其接口转换成符合要求的接口。
**示例代码:**
```java
// 目标接口
interface Target {
void request();
}
// 被适配者类
class Adaptee {
public void specificRequest() {
System.out.println("Adaptee: specific request");
}
}
// 类适配器
class ClassAdapter extends Adaptee implements Target {
@Override
public void request() {
specificRequest();
}
}
// 对象适配器
class ObjectAdapter implements Target {
private Adaptee adaptee;
public ObjectAdapter(Adaptee adaptee) {
this.adaptee = adaptee;
}
@Override
public void request() {
adaptee.specificRequest();
}
}
// 客户端代码
public class Main {
public static void main(String[] args) {
// 使用类适配器
Target classAdapter = new ClassAdapter();
classAdapter.request();
// 使用对象适配器
Target objectAdapter = new ObjectAdapter(new Adaptee());
objectAdapter.request();
}
}
```
**代码说明:**
- `Adaptee` 是被适配的类,其中包含一个不符合目标接口的方法 `specificRequest`。
- `Target` 是目标接口,定义了客户端期望的接口方法 `request`。
- `ClassAdapter` 是类适配器模式的实现类,继承自 `Adaptee` 并实现了 `Target` 接口。
- `ObjectAdapter` 是对象适配器模式的实现类,使用了组合关系,将 `Adaptee` 对象作为成员变量,并实现了 `Target` 接口。
- 客户端代码中,分别使用了类适配器和对象适配器来调用 `request` 方法。
#### 桥接模式(Bridge Pattern)
桥接模式将抽象部分与实现部分分离,使它们可以独立变化。它通过将抽象部分和实现部分分别定义为独立的类层次结构,从而实现它们的解耦。
**适用场景:**
- 当一个类存在两个或多个独立变化的维度时,可以使用桥接模式将这些维度分离,使它们能够独立扩展。
- 当一个类需要在多个独立维度上进行扩展,且不希望扩展时引入额外的复杂性时,可以使用桥接模式。
**示例代码:**
```java
// 实现化角色
interface Implementor {
void operationImpl();
}
// 具体实现化角色
class ConcreteImplementorA implements Implementor {
@Override
public void operationImpl() {
System.out.println("ConcreteImplementorA: operation implementation");
}
}
class ConcreteImplementorB implements Implementor {
@Override
public void operationImpl() {
System.out.println("ConcreteImplementorB: operation implementation");
}
}
// 抽象化角色
abstract class Abstraction {
protected Implementor implementor;
public Abstraction(Implementor implementor) {
this.implementor = implementor;
}
public abstract void operation();
}
// 扩展抽象化角色
class RefinedAbstraction extends Abstraction {
public RefinedAbstraction(Implementor implementor) {
super(implementor);
}
@Override
public void operation() {
implementor.operationImpl();
}
}
// 客户端代码
public class Main {
public static void main(String[] args) {
Implementor implementorA = new ConcreteImplementorA();
Abstraction abstractionA = new RefinedAbstraction(implementorA);
abstractionA.operation();
Implementor implementorB = new ConcreteImplementorB();
Abstraction abstractionB = new RefinedAbstraction(implementorB);
abstractionB.operation();
}
}
```
**代码说明:**
- `Implementor` 是实现化角色,定义了具体实现类需要实现的方法 `operationImpl`。
- `ConcreteImplementorA` 和 `ConcreteImplementorB` 是具体实现化角色的实现类,分别实现了 `operationImpl` 方法。
- `Abstraction` 是抽象化角色,其中包含一个对实现化角色的引用,并定义了抽象方法 `operation`。
- `RefinedAbstraction` 是扩展抽象化角色,继承自 `Abstraction`,并实现了 `operation` 方法。
- 客户端代码中,分别实例化了具体的实现化角色,并将其传入相应的抽象化角色中,通过调用抽象化角色的 `operation` 方法来执行操作。
#### 装饰器模式(Decorator Pattern)
装饰器模式动态地将责任添加到对象上,以扩展功能,相比继承,装饰器模式更加灵活,可以在运行时添加或删除对象的行为。
**适用场景:**
- 在不影响其他对象的情况下,以动态、透明的方式对对象的功能进行扩展或修改。
- 当不能或不希望使用子类来扩展对象的功能时,可以使用装饰器模式。
**示例代码:**
```java
// 抽象组件
interface Component {
void operation();
}
// 具体组件
class ConcreteComponent implements Component {
@Override
public void operation() {
System.out.println("ConcreteComponent: operation");
}
}
// 抽象装饰器
abstract class Decorator implements Component {
protected Component component;
public Decorator(Component component) {
this.component = component;
}
@Override
public void operation() {
component.operation();
}
}
// 具体装饰器
class ConcreteDecoratorA extends Decorator {
public ConcreteDecoratorA(Component component) {
super(component);
}
@Override
public void operation() {
super.operation();
addedBehavior();
}
private void addedBehavior() {
System.out.println("ConcreteDecoratorA: added behavior");
}
}
class ConcreteDecoratorB extends Decorator {
public ConcreteDecoratorB(Component component) {
super(component);
}
@Override
public void operation() {
super.operation();
addedBehavior();
}
private void addedBehavior() {
System.out.println("ConcreteDecoratorB: added behavior");
}
}
// 客户端代码
public class Main {
public static void main(String[] args) {
Component component = new ConcreteComponent();
Component decoratorA = new ConcreteDecoratorA(component);
decoratorA.operation();
Component decoratorB = new ConcreteDecoratorB(component);
decoratorB.operation();
Component decoratorAB = new ConcreteDecoratorB(new ConcreteDecoratorA(component));
decoratorAB.operation();
}
}
```
**代码说明:**
- `Component` 是抽象组件,定义了要扩展的对象接口方法 `operation`。
- `ConcreteComponent` 是具体组件,实现了 `Component` 接口的 `operation` 方法。
- `Decorator` 是抽象装饰器,包含一个对抽象组件的引用,并继承了 `Component` 接口。
- `ConcreteDecoratorA` 和 `ConcreteDecoratorB` 是具体装饰器,继承自 `Decorator`,并实现了 `operation` 方法。其中,在 `operation` 方法中调用了父类的 `operation` 方法,并在其前后添加了自己的行为。
- 客户端代码中,分别实例化了具体的组件和装饰器,并将其传入相应的装饰器中,通过调用装饰器的 `operation` 方法来执行操作。可以看到,通过装饰器模式,可以在运行时动态地添加新的行为。
# 4. 行为型设计模式
在软件开发过程中,行为型设计模式主要关注对象之间的通信以及如何将算法与对象的解耦。下面介绍几种常见的行为型设计模式。
#### 观察者模式
观察者模式定义了一种一对多的依赖关系,当一个对象的状态发生改变时,它的所有依赖者(观察者)都会得到通知并自动更新。
观察者模式的实现通常包含两个角色:观察者和目标。观察者将自己注册到目标对象上,目标对象发生变化时会通知观察者进行更新。
适用场景:
- 当一个对象的改变需要同时改变其他对象的时候,可以考虑使用观察者模式。
- 当一个抽象模型有两个方面,其中一个方面依赖于另一个方面,可以使用观察者模式来解耦。
示例代码如下(Java):
```java
// 观察者接口
interface Observer {
void update(String message);
}
// 目标类
class Subject {
private List<Observer> observers = new ArrayList<>();
private String message;
public void attach(Observer observer) {
observers.add(observer);
}
public void detach(Observer observer) {
observers.remove(observer);
}
public void setMessage(String message) {
this.message = message;
notifyObservers();
}
private void notifyObservers() {
for (Observer observer : observers) {
observer.update(message);
}
}
}
// 具体观察者类
class ConcreteObserver implements Observer {
private String name;
public ConcreteObserver(String name) {
this.name = name;
}
@Override
public void update(String message) {
System.out.println(name + " received message: " + message);
}
}
public class ObserverPatternExample {
public static void main(String[] args) {
// 创建目标对象
Subject subject = new Subject();
// 创建观察者对象
Observer observerA = new ConcreteObserver("Observer A");
Observer observerB = new ConcreteObserver("Observer B");
// 将观察者对象注册到目标对象上
subject.attach(observerA);
subject.attach(observerB);
// 改变目标对象的状态,触发观察者的更新
subject.setMessage("Hello, world!");
// 将观察者对象从目标对象上移除
subject.detach(observerB);
// 改变目标对象的状态,再次触发观察者的更新
subject.setMessage("Goodbye, world!");
}
}
```
运行结果:
```
Observer A received message: Hello, world!
Observer B received message: Hello, world!
Observer A received message: Goodbye, world!
```
#### 策略模式
策略模式定义了一系列的算法,并将每个算法封装到具有公共接口的独立类中。在运行时,根据需要选择合适的算法。
策略模式的实现通常包含三个角色:策略接口、具体策略类和上下文。上下文负责维护一个对策略接口的引用,并根据需求调用具体策略类的方法。
适用场景:
- 当一个系统需要在多个算法中选择一个进行动态调用时,可以考虑使用策略模式。
- 当一个类定义了多种行为,并且这些行为在运行时可以根据需要动态切换时,可以使用策略模式。
示例代码如下(Python):
```python
# 策略接口
class Strategy:
def do_operation(self, num1, num2):
pass
# 具体策略类
class AddStrategy(Strategy):
def do_operation(self, num1, num2):
return num1 + num2
class SubtractStrategy(Strategy):
def do_operation(self, num1, num2):
return num1 - num2
class MultiplyStrategy(Strategy):
def do_operation(self, num1, num2):
return num1 * num2
# 上下文类
class Context:
def __init__(self, strategy):
self.strategy = strategy
def execute_strategy(self, num1, num2):
return self.strategy.do_operation(num1, num2)
# 使用具体策略类进行计算
context = Context(AddStrategy())
print("10 + 5 =", context.execute_strategy(10, 5))
context.strategy = SubtractStrategy()
print("10 - 5 =", context.execute_strategy(10, 5))
context.strategy = MultiplyStrategy()
print("10 * 5 =", context.execute_strategy(10, 5))
```
运行结果:
```
10 + 5 = 15
10 - 5 = 5
10 * 5 = 50
```
由于篇幅限制,这里只介绍了观察者模式和策略模式,其他行为型设计模式的介绍请参考文章中的章节内容。
# 5. 将设计模式应用于实际场景
设计模式在日常企业级应用中扮演着至关重要的角色。无论是在传统的后端开发中,还是在现代的微服务架构和大数据系统中,设计模式都能够提供可靠、高效的解决方案。接下来,我们将深入探讨设计模式在企业级应用中的实际应用、在Java Web开发中使用设计模式的实例,以及设计模式与框架的关系。
#### 设计模式在企业级应用中的实际应用
在实际的企业级应用开发中,设计模式可以帮助开发团队解决各种复杂的问题,提高系统的可维护性和可扩展性。比如,在大型的电子商务系统中,可以使用工厂模式来创建不同类型的产品,使用单例模式来管理全局唯一的购物车对象,使用观察者模式来实现订单状态的实时更新等。设计模式使得代码更加可读,更易于维护和扩展,有助于团队协作和项目的长期发展。
#### 实例:在Java Web开发中使用设计模式
假设我们正在开发一个简单的在线商城系统,我们可以使用设计模式来优化代码结构和提高系统的性能。比如,我们可以使用工厂模式来创建不同商品类型的实例,通过策略模式来实现不同的促销策略,通过模板方法模式来定义订单处理流程等。以下是一个简化的示例代码:
```java
// 工厂模式示例
interface Product {
void sell();
}
class Phone implements Product {
@Override
public void sell() {
System.out.println("Sell Phone");
}
}
class Laptop implements Product {
@Override
public void sell() {
System.out.println("Sell Laptop");
}
}
class ProductFactory {
public Product createProduct(String type) {
if ("phone".equals(type)) {
return new Phone();
} else if ("laptop".equals(type)) {
return new Laptop();
}
return null;
}
}
// 策略模式示例
interface PromotionStrategy {
void execute();
}
class ChristmasPromotion implements PromotionStrategy {
@Override
public void execute() {
System.out.println("Christmas Promotion: 20% off");
}
}
class NewYearPromotion implements PromotionStrategy {
@Override
public void execute() {
System.out.println("New Year Promotion: 10% off");
}
}
class PromotionContext {
private PromotionStrategy strategy;
public PromotionContext(PromotionStrategy strategy) {
this.strategy = strategy;
}
public void executePromotion() {
strategy.execute();
}
}
public class Main {
public static void main(String[] args) {
// 使用工厂模式创建商品
ProductFactory factory = new ProductFactory();
Product phone = factory.createProduct("phone");
phone.sell();
// 使用策略模式执行不同促销策略
PromotionContext context = new PromotionContext(new ChristmasPromotion());
context.executePromotion();
context = new PromotionContext(new NewYearPromotion());
context.executePromotion();
}
}
```
在上面的示例中,我们使用了工厂模式来创建不同类型的商品,使用策略模式来实现不同的促销策略,增加了系统的灵活性和可扩展性。
#### 设计模式与框架的关系
设计模式并不是框架,而是一种在特定情境下解决问题的方案。然而,许多框架和库都内置了常用的设计模式,比如Spring框架中的依赖注入和AOP(面向切面编程)就是基于设计模式的实现。因此,熟练掌握设计模式有助于更好地理解和使用各种框架。
通过以上实例,我们可以清晰地看到在实际场景中设计模式的应用,设计模式的建模思想和开发实践的结合能够更好的帮助我们解决实际开发中碰到的问题。
# 6. 设计模式的最佳实践与注意事项
在使用设计模式时,我们需要思考如何选择合适的设计模式,并注意一些典型的误用和注意事项。下面将逐一介绍这些内容。
###### 6.1 如何选择合适的设计模式
在选择设计模式时,我们应该考虑以下几个方面:
- 理解需求和问题:首先,我们需要详细了解项目的需求和问题,分析它们的特点和关系。只有在充分理解了需求和问题后,才能选择合适的设计模式。
- 考虑项目规模和复杂度:不同的设计模式适用于不同的项目规模和复杂度。对于小规模、简单的项目,选择过于复杂的设计模式可能造成代码冗余和维护困难;而对于大规模、复杂的项目,选择适当的设计模式可以提高代码的可维护性和扩展性。
- 遵循设计原则:选择设计模式时需要遵循一些基本的设计原则,比如单一职责原则、开闭原则、依赖倒置原则等。设计模式应该帮助我们提高代码的可读性、复用性和灵活性,而不是违反基本的设计原则。
- 借鉴经验和实践:在选择设计模式时,可以借鉴一些经验和实践。学习其他项目或者优秀开源代码中是如何应用设计模式的,了解它们的优点和缺点,可以帮助我们更好地选择合适的设计模式。
###### 6.2 设计模式的典型误用与注意事项
在实际应用设计模式时,也需要注意一些典型的误用和注意事项:
- 不要滥用设计模式:设计模式并不是万能的,不应该被滥用。只有在真正需要时才选择使用设计模式,避免过度设计和过度引入复杂性。
- 不要为了使用设计模式而设计:设计模式是为了解决特定的问题而提出的,不应该为了使用设计模式而设计,这样会导致冗余和不必要的复杂性。
- 注意代码可读性和维护性:设计模式本身并不是最终目标,最重要的是代码的可读性和维护性。选择设计模式时要考虑到代码的可读性,避免过于复杂和难以理解的代码。
- 注意设计模式的演化和维护:随着项目的演化,可能需要对设计模式进行修改和调整。设计模式应该是灵活的,能够适应项目的变化和演化。在维护设计模式时,要注意对相关代码的修改和测试。
###### 6.3 设计模式的维护与演化
设计模式并不是一成不变的,随着项目的演化和需求的变化,可能需要对设计模式进行维护和演化。
维护设计模式的主要工作包括对设计模式的评估、修改和重构。在评估设计模式时,需要考虑设计模式是否仍然适用于当前的项目和需求,是否存在更好的设计模式可以替代。在修改设计模式时,需要根据需要对设计模式进行适当的修改和调整。在重构设计模式时,需要对相关的代码进行修改和测试,确保代码的正确性和可维护性。
设计模式的演化是一个持续的过程,需要不断地学习和实践。通过反思和总结设计模式的应用,我们可以不断提高自己的设计能力和代码质量,为项目的成功和发展做出贡献。
综上所述,选择合适的设计模式并遵循最佳实践,能够帮助我们更好地解决问题、提高代码质量和项目成功率。同时,我们也要注意设计模式的典型误用和注意事项,保持代码的可读性和维护性。最后,要不断学习和演化设计模式,提高自己的设计能力和代码质量。
0
0