深入理解Java中的23种设计模式

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:设计模式是Java编程中的模板或最佳实践,用于构建可扩展和可维护的软件系统。本文将详细探讨Java中的代理模式,并概括介绍其他22种设计模式。代理模式通过代理类控制对目标对象的访问,并可以增加额外功能。除了代理模式,文中还将解释工厂模式、抽象工厂模式、单例模式等,并强调掌握这些模式对于提高代码质量和设计面向对象软件的重要性。 Java中的23种设计模式

1. 设计模式概述及其在Java中的应用

设计模式是软件开发领域的经典话题,它帮助开发者利用已有的智慧结晶来应对软件开发中的挑战。当我们谈论设计模式时,我们实际上是在讨论那些经过时间和实践检验的、解决特定问题的通用模板。设计模式的引入,让我们能够编写出结构更清晰、更易于维护和扩展的代码。

在Java这样的面向对象编程语言中,设计模式的应用尤为突出。Java语言的灵活性和强大的类库支持,使得开发者可以轻松实现和运用各种设计模式。例如,我们可以利用单例模式确保某个类只有一个全局实例,也可以通过工厂模式简化对象的创建过程。通过应用设计模式,我们可以使代码更加模块化,降低类之间的耦合度,提高系统的整体质量和可维护性。

在深入了解具体的设计模式之前,我们需要先了解设计模式的分类。按照设计模式的功能和目的,我们可以将它们大致分为三类:创建型模式、结构型模式和行为型模式。创建型模式专注于对象的创建过程,如单例模式、工厂模式、建造者模式等。结构型模式关注类和对象的组合,如代理模式、适配器模式、装饰者模式等。行为型模式着重于对象之间的通信,例如观察者模式、策略模式、模板方法模式等。

现在,让我们开始深入探索这些模式,并了解它们如何在Java中得到应用。我们将从每个模式的概念、作用、适用场景以及在Java中的具体实现来逐一展开讨论。随着时间的推移,你会发现设计模式不仅仅是工具,它们是编程艺术的基石。

2. 代理模式详解,包括静态代理与动态代理

代理模式基础

代理模式(Proxy Pattern)属于结构型设计模式,它为其他对象提供一种代理以控制对这个对象的访问。代理模式可以作为一个中间层,拦截对某个对象的访问请求,然后根据需要决定是直接调用原对象的方法,还是做一些额外的处理后,再调用原对象的方法。

代理模式的典型结构包括三部分: 1. 主题(Subject):声明了真实主题和代理主题的共同接口。 2. 真实主题(RealSubject):定义了代理所表示的真实对象。 3. 代理(Proxy):持有对真实主题的引用,在访问真实主题前可以进行额外的处理。

静态代理与动态代理的区别

静态代理

静态代理是在编译时就已经实现,它需要程序员创建代理类或特定的工具自动生成源代码再对其编译。在运行之前代理类的.class文件就已经存在了。

静态代理的优缺点: - 优点 :结构清晰,易于理解。 - 缺点 :当需要代理的接口增加方法或者实现类发生变化时,代理类也需要相应地进行修改,这使得代理类的维护较为复杂。

// 真实主题接口
interface Subject {
    void request();
}

// 真实主题实现
class RealSubject implements Subject {
    @Override
    public void request() {
        System.out.println("RealSubject: Handling request.");
    }
}

// 静态代理类
class ProxySubject implements Subject {
    private Subject realSubject;

    public ProxySubject(Subject realSubject) {
        this.realSubject = realSubject;
    }

    @Override
    public void request() {
        System.out.println("ProxySubject: Doing some operations before handling the request.");
        realSubject.request();
    }
}

// 客户端代码
class Client {
    public static void main(String[] args) {
        Subject realSubject = new RealSubject();
        Subject proxySubject = new ProxySubject(realSubject);
        proxySubject.request();
    }
}

动态代理

动态代理是在运行时动态生成代理类。它不需要为每一个真实主题生成一个代理类,通过Java的反射机制实现在运行时动态地创建代理对象。

动态代理的优缺点: - 优点 :可以动态代理任何接口或类,减少了代码的重复,提高了程序的可维护性。 - 缺点 :需要额外的性能开销来处理代理过程,且由于实现方式的限制,动态代理只能代理接口类型。

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

// 动态代理实现类
class DynamicProxyHandler implements InvocationHandler {
    private Object originalObject;

    public DynamicProxyHandler(Object originalObject) {
        this.originalObject = originalObject;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("DynamicProxy: Doing some operations before handling the request.");
        return method.invoke(originalObject, args);
    }
}

// 客户端代码使用动态代理
class ClientWithDynamicProxy {
    public static void main(String[] args) {
        Subject realSubject = new RealSubject();
        // 生成代理对象
        Subject proxySubject = (Subject) Proxy.newProxyInstance(
                Subject.class.getClassLoader(),
                new Class[]{Subject.class},
                new DynamicProxyHandler(realSubject)
        );
        proxySubject.request();
    }
}

JDK动态代理与Cglib动态代理

JDK动态代理

JDK动态代理只能代理实现了接口的类。它生成的代理类和被代理的类实现了相同的接口,并且持有了被代理类的引用。

Cglib动态代理

Cglib动态代理可以代理没有实现接口的类。Cglib库通过继承被代理类并重写其方法来实现代理功能。

Cglib代理使用示例:

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

class CglibProxyExample implements MethodInterceptor {
    private Object target;

    public CglibProxyExample(Object target) {
        this.target = target;
    }

    public Object createProxy() {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(target.getClass());
        enhancer.setCallback(this);
        return enhancer.create();
    }

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("CglibProxyExample: Doing some operations before handling the request.");
        return proxy.invokeSuper(obj, args);
    }
}

// 客户端代码
class ClientWithCglibProxy {
    public static void main(String[] args) {
        RealSubject realSubject = new RealSubject();
        CglibProxyExample proxy = new CglibProxyExample(realSubject);
        Subject proxySubject = (Subject) proxy.createProxy();
        proxySubject.request();
    }
}

表格:静态代理与动态代理的对比

| 特性/代理类型 | 静态代理 | JDK动态代理 | Cglib动态代理 | | ------------ | -------------- | --------------- | ---------------- | | 实现方式 | 手动编码 | 自动代理生成 | 自动代理生成 | | 应用范围 | 接口 | 接口 | 类 | | 性能开销 | 较低 | 较高 | 较低 | | 代码侵入性 | 较高 | 较低 | 较低 | | 适用场景 | 接口方法较少时 | 接口方法较多时 | 无需接口时 |

代码块逻辑分析

在上面的代码示例中,静态代理通过定义一个共同的接口 Subject ,然后分别实现 RealSubject 作为被代理对象和 ProxySubject 作为代理对象。动态代理中,JDK动态代理是通过 Proxy.newProxyInstance 方法动态创建代理类的实例,而Cglib动态代理则是通过 Enhancer 类创建子类来实现代理。

动态代理实现过程:

  1. 创建 InvocationHandler MethodInterceptor 接口实现类 :定义了在代理对象的方法被调用时,代理逻辑是什么。
  2. 生成代理对象 :JDK通过反射机制获取被代理对象的所有接口,并创建代理类;Cglib则是通过继承被代理类来创建一个子类。
  3. 执行代理方法 :在代理方法中可以控制对真实主题方法的调用。

这些代码实现展示了代理模式在Java中的具体应用。根据不同的场景,开发者可以灵活选择使用静态代理或者动态代理来实现需求。

3. 其他22种设计模式简述

在软件开发的丰富实践中,除了代理模式,还有许多其他设计模式对Java开发者来说至关重要。这些模式帮助我们在各种设计场景中保持清晰的设计思路和灵活的代码结构。本章将列举剩余的22种设计模式,并对它们进行简要介绍。每个模式都会包含其定义、使用场景、关键特点以及在Java中的实现方式。

工厂模式

工厂模式是一种创建型设计模式,它提供了一种在不暴露对象创建逻辑的情况下创建对象的方法。工厂模式有几种不同的形式,包括简单工厂、工厂方法和抽象工厂。

简单工厂模式

简单工厂模式适用于创建的产品较少且不会频繁变更的情况。它通过一个工厂类来创建所有产品的实例,而不是直接通过new操作符。

// 定义一个产品接口
interface Product {
    void use();
}

// 两个具体的产品实现类
class ConcreteProductA implements Product {
    public void use() {
        System.out.println("Using Product A");
    }
}

class ConcreteProductB implements Product {
    public void use() {
        System.out.println("Using Product B");
    }
}

// 简单工厂类
class SimpleFactory {
    public static Product createProduct(String type) {
        if ("A".equals(type)) {
            return new ConcreteProductA();
        } else if ("B".equals(type)) {
            return new ConcreteProductB();
        }
        return null;
    }
}

// 客户端代码
public class Client {
    public static void main(String[] args) {
        Product product = SimpleFactory.createProduct("A");
        product.use();
    }
}
工厂方法模式

工厂方法模式是一种更为抽象的创建型设计模式,它定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法把类的实例化推迟到子类。

// 定义产品接口
interface Product {
    void use();
}

// 具体产品类
class ConcreteProduct implements Product {
    public void use() {
        System.out.println("Using ConcreteProduct");
    }
}

// 工厂接口
interface Factory {
    Product create();
}

// 具体工厂类
class ConcreteFactory implements Factory {
    public Product create() {
        return new ConcreteProduct();
    }
}

// 客户端代码
public class Client {
    public static void main(String[] args) {
        Factory factory = new ConcreteFactory();
        Product product = factory.create();
        product.use();
    }
}
抽象工厂模式

抽象工厂模式是创建型设计模式的一种,它提供了一种方式,可以创建一系列相关或相互依赖的对象,而无需指定这些对象具体的类。

// 抽象产品A
interface AbstractProductA {
    void use();
}

// 具体产品A1
class ConcreteProductA1 implements AbstractProductA {
    public void use() {
        System.out.println("Using ConcreteProductA1");
    }
}

// 具体产品A2
class ConcreteProductA2 implements AbstractProductA {
    public void use() {
        System.out.println("Using ConcreteProductA2");
    }
}

// 抽象产品B
interface AbstractProductB {
    void use();
}

// 具体产品B1
class ConcreteProductB1 implements AbstractProductB {
    public void use() {
        System.out.println("Using ConcreteProductB1");
    }
}

// 具体产品B2
class ConcreteProductB2 implements AbstractProductB {
    public void use() {
        System.out.println("Using ConcreteProductB2");
    }
}

// 抽象工厂类
interface AbstractFactory {
    AbstractProductA createProductA();
    AbstractProductB createProductB();
}

// 具体工厂类
class ConcreteFactory implements AbstractFactory {
    public AbstractProductA createProductA() {
        return new ConcreteProductA1();
    }

    public AbstractProductB createProductB() {
        return new ConcreteProductB1();
    }
}

// 客户端代码
public class Client {
    public static void main(String[] args) {
        AbstractFactory factory = new ConcreteFactory();
        AbstractProductA productA = factory.createProductA();
        AbstractProductB productB = factory.createProductB();
        productA.use();
        productB.use();
    }
}

单例模式

单例模式是一种常用的结构型设计模式,它确保一个类只有一个实例,并提供一个全局访问点。单例模式适用于当类的实例化是资源消耗较大或者控制实例数量的场景。

// 单例类
class Singleton {
    // 私有构造方法
    private Singleton() {}

    // 静态成员变量存储单例实例
    private static Singleton instance = new Singleton();

    // 公共静态方法获取实例
    public static Singleton getInstance() {
        return instance;
    }

    public void use() {
        System.out.println("Using Singleton");
    }
}

// 客户端代码
public class Client {
    public static void main(String[] args) {
        Singleton singleton = Singleton.getInstance();
        singleton.use();
    }
}

建造者模式

建造者模式是一种创建型设计模式,它允许用户通过指定复杂对象的类型和内容来构建它们,然后构建者会将各个部分组织成最终产品。

// 产品类
class Product {
    private String partA;
    private String partB;

    public void setPartA(String partA) {
        this.partA = partA;
    }

    public void setPartB(String partB) {
        this.partB = partB;
    }

    public void use() {
        System.out.println("Product: " + partA + ", " + partB);
    }
}

// 抽象建造者
abstract class Builder {
    protected Product product = new Product();

    public abstract void buildPartA();
    public abstract void buildPartB();
    public abstract Product getResult();
}

// 具体建造者
class ConcreteBuilder extends Builder {
    public void buildPartA() {
        product.setPartA("PartA");
    }

    public void buildPartB() {
        product.setPartB("PartB");
    }

    public Product getResult() {
        return product;
    }
}

// 指挥者
class Director {
    public void construct(Builder builder) {
        builder.buildPartA();
        builder.buildPartB();
    }
}

// 客户端代码
public class Client {
    public static void main(String[] args) {
        Builder builder = new ConcreteBuilder();
        Director director = new Director();
        director.construct(builder);
        Product product = builder.getResult();
        product.use();
    }
}

观察者模式

观察者模式是一种行为型设计模式,它允许对象定义它们依赖于其他对象的状态,当那些对象的状态发生变化时,它们会自动得到通知。

import java.util.ArrayList;
import java.util.List;

// 主题接口
interface Subject {
    void registerObserver(Observer o);
    void removeObserver(Observer o);
    void notifyObservers();
}

// 观察者接口
interface Observer {
    void update(String message);
}

// 具体主题
class ConcreteSubject implements Subject {
    private List<Observer> observers = new ArrayList<>();
    private String message;

    public void registerObserver(Observer o) {
        observers.add(o);
    }

    public void removeObserver(Observer o) {
        observers.remove(o);
    }

    public void notifyObservers() {
        for (Observer observer : observers) {
            observer.update(message);
        }
    }

    public void setState(String message) {
        this.message = message;
        notifyObservers();
    }
}

// 具体观察者
class ConcreteObserver implements Observer {
    private String name;

    public ConcreteObserver(String name) {
        this.name = name;
    }

    public void update(String message) {
        System.out.println(name + " received: " + message);
    }
}

// 客户端代码
public class Client {
    public static void main(String[] args) {
        ConcreteSubject subject = new ConcreteSubject();
        subject.registerObserver(new ConcreteObserver("Observer A"));
        subject.registerObserver(new ConcreteObserver("Observer B"));

        subject.setState("State changed");
        subject.setState("State changed again");
    }
}

通过上述代码示例,我们对简单工厂、工厂方法、抽象工厂、单例、建造者和观察者模式有了初步的了解。每个模式都有其独特之处,适用于不同的场景,且都是为了解决软件设计中的特定问题而设计的。在实际开发中,我们会根据需求和问题选择合适的模式来应用。随着实践经验的积累,开发者将更加熟悉这些设计模式,并能够在项目中巧妙地运用它们来提升软件质量。

(由于篇幅限制,剩余模式的介绍将在后续段落中以类似方式提供。)

4. 设计模式对软件质量和开发流程的影响

4.1 设计模式与代码质量

设计模式是软件开发中常用的解决方案模板,它们对于提高代码质量和维护性有着显著的作用。在软件开发过程中,代码的可读性和可维护性至关重要。设计模式通过定义一系列的规则和原则,比如单一职责原则、开闭原则等,帮助开发者创建出清晰、简洁、易于理解和修改的代码。

4.1.1 可读性

可读性强的代码能够使其他开发者更容易理解程序的工作原理,缩短团队成员熟悉项目的时间。设计模式通过引入标准的命名和结构,比如在工厂模式中,工厂类负责对象的创建,客户端不需要关心具体的实现类,从而使得代码的意图变得明确。

4.1.2 可维护性

可维护的代码意味着在不影响系统其他部分的前提下,容易添加新功能或修改现有功能。例如,使用观察者模式,可以在不修改主题(Subject)和观察者(Observer)类的情况下,轻松增加新的观察者。

4.1.3 代码复用

设计模式能够增加代码的复用性。在Java中,我们可以使用模板方法模式来定义算法的骨架,将一些步骤延迟到子类中实现。这样,不同的子类可以复用相同的算法结构,而只改变特定的步骤。

4.2 设计模式与团队协作

团队协作在软件开发中同样重要。设计模式可以作为团队内部沟通的一种工具,统一开发人员对特定问题的认识和解决方案的实施。

4.2.1 明确的架构指导

通过采用统一的设计模式,团队可以减少设计决策时的歧义,提高开发效率。比如,应用策略模式可以清晰地分离算法,使得开发人员更容易理解和维护代码。

4.2.2 标准化的接口和约定

设计模式定义了接口和类之间交互的标准方式,为团队成员提供了一种通用的语言。这样,当一个开发人员看到一个类遵循了某个设计模式,他就能预期到类的行为和接口。

4.3 设计模式与软件架构稳定性

设计模式通过提供经过验证的结构方案,有助于构建稳定和可扩展的软件架构。

4.3.1 建立清晰的层次结构

使用设计模式,如MVC(Model-View-Controller)模式,可以明确不同组件之间的职责,建立清晰的层次结构,这样有助于独立修改和扩展组件而不影响整体结构。

4.3.2 促进模块化设计

模块化设计允许系统被划分为独立的模块,每个模块执行一个子功能。设计模式如外观模式(Facade)和中介者模式(Mediator)可以减少模块间的耦合,从而提高整体的模块化。

4.4 设计模式与维护成本

使用设计模式可以降低软件的长期维护成本。

4.4.1 降低维护难度

设计模式通过限制依赖关系和分离关注点,帮助开发者更轻松地定位和解决问题。例如,使用单一职责模式,一个类只负责一项任务,这样更容易定位问题所在。

4.4.2 增强系统的可扩展性

设计模式如装饰器模式和桥接模式提供了一种添加新功能而不修改现有代码的方式,减少了代码的重构次数,降低了未来可能的维护成本。

在这一章节中,我们已经深入探讨了设计模式对软件质量和开发流程的正面影响。通过提高代码的可读性和可维护性,加强团队合作,确立稳固的软件架构,以及减少长期维护成本,设计模式证明了其在软件工程实践中的核心价值。下一章节,我们将深入探讨工厂模式,包括它的不同实现以及在各种场景下的应用。

5. 工厂模式的深入探讨与实践

工厂模式简介

工厂模式(Factory Pattern)是一种创建型设计模式,旨在提供一个接口用于创建对象,而让子类决定实例化哪一个类。工厂模式将对象的创建和使用分离,这样可以在不修改使用该对象的代码的情况下引入新的具体类。这种模式特别适用于在创建对象时涉及复杂逻辑,或者当系统需要根据不同的条件创建不同类型对象的情况。

工厂模式的三种基本形态

工厂模式有三种基本形态:简单工厂模式(Simple Factory)、工厂方法模式(Factory Method)和抽象工厂模式(Abstract Factory)。

简单工厂模式

简单工厂模式由一个工厂类根据传入的参数决定创建出哪一种产品类的实例。

public class SimpleFactory {
    public static Product createProduct(String type) {
        if ("A".equals(type)) {
            return new ConcreteProductA();
        } else if ("B".equals(type)) {
            return new ConcreteProductB();
        }
        // 可以在这里添加更多产品的创建逻辑
        throw new IllegalArgumentException("Unknown product type");
    }
}

在上述代码中, createProduct 方法根据传入的 type 参数决定实例化 Product 接口的哪一个子类。

工厂方法模式

工厂方法模式定义了一个创建对象的接口,但由实现该接口的工厂类来决定实例化哪一个类。

public interface Product {
    void use();
}

public class ConcreteProductA implements Product {
    public void use() {
        System.out.println("Using Product A");
    }
}

public class ConcreteProductB implements Product {
    public void use() {
        System.out.println("Using Product B");
    }
}

public interface Creator {
    Product factoryMethod();
}

public class ConcreteCreatorA implements Creator {
    public Product factoryMethod() {
        return new ConcreteProductA();
    }
}

public class ConcreteCreatorB implements Creator {
    public Product factoryMethod() {
        return new ConcreteProductB();
    }
}
抽象工厂模式

抽象工厂模式创建一系列相关或相互依赖的对象,不需要指定它们具体的类。

public interface AbstractFactory {
    AbstractProductA createProductA();
    AbstractProductB createProductB();
}

public class ConcreteFactory1 implements AbstractFactory {
    public AbstractProductA createProductA() {
        return new ProductA1();
    }
    public AbstractProductB createProductB() {
        return new ProductB1();
    }
}

public class ConcreteFactory2 implements AbstractFactory {
    public AbstractProductA createProductA() {
        return new ProductA2();
    }
    public AbstractProductB createProductB() {
        return new ProductB2();
    }
}

工厂模式的应用场景

工厂模式主要用于以下场景:

  1. 当创建逻辑比较复杂,需要抽象出来以便于复用时。
  2. 客户端不关心对象的创建细节,只知道需要什么,但不知道具体是哪个类的实例。
  3. 当系统需要根据不同的条件创建不同的对象时,而这些条件在运行时动态变化。

工厂模式的实现

实现步骤
  1. 创建一个接口,声明需要创建对象的方法。
  2. 创建一个或多个具体的类实现这个接口。
  3. 创建一个工厂类,实现创建接口中的方法,根据参数返回不同类的实例。
  4. 在客户端代码中,使用工厂类来获取实例。
关键点解析
  • 封装对象的创建 :工厂模式将对象创建封装在工厂类中,客户端无需关心具体实现。
  • 解耦客户端和对象创建 :客户端依赖工厂类而不是具体的对象类,这样可以轻松地切换不同的产品类而无需修改客户端代码。
  • 支持多种类型的产品创建 :一个工厂类可以负责多个类型产品的创建。

实际案例演示

假设我们有一个电子商务系统,其中需要根据不同的订单类型创建不同的订单对象。

public interface Order {
    void processOrder();
}

public class StandardOrder implements Order {
    public void processOrder() {
        System.out.println("Processing standard order.");
    }
}

public class ExpressOrder implements Order {
    public void processOrder() {
        System.out.println("Processing express order.");
    }
}

public class OrderFactory {
    public static Order getOrder(String orderType) {
        if ("standard".equals(orderType)) {
            return new StandardOrder();
        } else if ("express".equals(orderType)) {
            return new ExpressOrder();
        }
        throw new IllegalArgumentException("Unknown order type");
    }
}

public class Client {
    public static void main(String[] args) {
        Order order = OrderFactory.getOrder("standard");
        order.processOrder();
    }
}

在这个例子中, OrderFactory 根据不同的 orderType 返回不同的 Order 实现类的实例,客户端代码通过工厂方法获取 Order 对象并处理订单。

工厂模式的优缺点

优点
  • 降低客户端和具体类的耦合度 :客户端不需要直接创建对象,从而减少了依赖关系。
  • 符合开闭原则 :当引入新产品时,无需修改现有代码。
  • 扩展性好 :易于引入新的产品和工厂类。
缺点
  • 增加了系统的抽象层次 :可能需要引入多个具体工厂类和抽象工厂接口,增加复杂性。
  • 具体产品创建过程中的逻辑可能难以跟踪 :如果工厂方法过于复杂,会影响代码的清晰度。

总结

工厂模式是设计模式中非常重要的一环,它通过抽象和封装创建对象的逻辑,极大地提高了代码的可维护性和可扩展性。在实际开发中,合理地使用工厂模式可以有效地管理复杂对象的创建过程,并能适应未来产品族的变化。在选择具体类型的工厂模式时,应根据具体的应用场景和需求来决定使用简单工厂模式、工厂方法模式还是抽象工厂模式。

6. 单例模式的多角度剖析与运用

单例模式是创建型设计模式的一种,它确保一个类只有一个实例,并提供一个全局访问点。在Java开发中,单例模式的应用非常广泛,它不仅可以减少内存的消耗,还能保证对共享资源访问的一致性。本章将从单例模式的基本概念出发,分析其在多线程环境下的线程安全问题,并通过实例展示如何在Java中安全地实现单例模式。同时,将探讨单例模式的高级应用场景,比如懒汉式与饿汉式的区别和选择。

单例模式的基本概念

单例模式的核心思想是确保一个类在任何情况下都只有一个实例,并且提供一个全局的访问点。这通常通过私有的构造函数和一个公共的静态方法来实现。在Java中,实现单例模式可以有多种方式,包括懒汉式、饿汉式、双重检查锁定、枚举单例和静态内部类等。

懒汉式与饿汉式

懒汉式单例模式是在需要时才创建实例,而饿汉式单例模式在类加载时就立即创建实例。两者的主要区别在于实例的创建时机和内存使用情况:

  • 懒汉式 :通过延迟实例化,节省了资源,但在高并发的情况下需要考虑线程安全问题。
  • 饿汉式 :简单且线程安全,但不管是否使用,都会加载类。

线程安全的实现

在多线程环境中,懒汉式的单例模式可能会导致创建多个实例。为了解决这个问题,可以采取以下几种方式:

  • 同步方法 :在获取实例的方法上加 synchronized 关键字,确保线程安全,但会降低性能。
  • 双重检查锁定 :使用 volatile 关键字确保可见性,减少不必要的同步,提高了性能。
  • 静态内部类 :利用JVM类加载机制,实现线程安全的延迟初始化。

高级应用场景

在实际的开发中,单例模式还可以扩展到更复杂的使用场景中,比如:

  • 多例模式 :用于创建固定数量的实例。
  • 注册式单例 :通过注册中心来管理单例对象。
  • 分布式单例 :在分布式系统中,确保单例状态的一致性。

单例模式在Java中的实现

下面是一个简单的单例模式实现示例,采用懒汉式和双重检查锁定机制来保证线程安全:

public class Singleton {
    // 私有构造函数,防止外部直接创建对象
    private Singleton() {}

    // 单例实例引用,volatile保证多线程可见性
    private volatile static Singleton instance = null;

    // 公共的访问点
    public static Singleton getInstance() {
        // 第一次检查,避免不必要的同步
        if (instance == null) {
            synchronized (Singleton.class) {
                // 第二次检查,确保只创建一个实例
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

代码逻辑逐行解读

  1. 私有构造函数:防止类在外部被实例化。
  2. volatile 关键字:确保 instance 变量在多线程中的可见性,即一个线程修改了 instance 变量后,其他线程能够立即得知。
  3. 第一次检查 instance == null :这是为了避免每次方法调用时都进行同步,只在 instance 未创建时才进行同步。
  4. synchronized (Singleton.class) :当多个线程同时到达同步块时,确保只有一个线程能够执行块内的代码。
  5. 第二次检查 instance == null :这是为了防止在 instance 已经被创建后,多个线程同时进入同步块时再次创建实例。

高级实现方式

除了上述懒汉式和双重检查锁定的实现方式外,还有其他高级的实现方式,比如使用枚举实现单例:

public enum Singleton {
    INSTANCE;

    public void doSomething() {
        // 实例方法
    }
}

枚举实现单例的优势在于简单且自动保证了线程安全,因为枚举的实例化是由JVM保证的,不可能被反射等手段破坏。

单例模式的应用案例

在实际开发中,单例模式的应用场景非常广泛。比如在数据库连接池、日志记录器、配置管理器等系统资源中,单例模式提供了一种高效的管理方式。

数据库连接池的单例应用

数据库连接池是一种创建连接、管理连接、销毁连接的资源池。为了避免频繁创建和销毁连接带来的性能开销,使用单例模式可以有效地管理连接池资源。

public class DatabaseConnectionPool {
    // 单例实例
    private static DatabaseConnectionPool instance;
    // 连接池资源,如连接集合等

    // 私有构造函数
    private DatabaseConnectionPool() {
        // 初始化连接池资源
    }

    // 获取单例实例
    public static synchronized DatabaseConnectionPool getInstance() {
        if (instance == null) {
            instance = new DatabaseConnectionPool();
        }
        return instance;
    }

    // 获取数据库连接的方法
    public Connection getConnection() {
        // 实现细节...
        return null;
    }

    // 归还数据库连接的方法
    public void releaseConnection(Connection connection) {
        // 实现细节...
    }

    // 其他连接池相关方法...
}

总结

单例模式作为一种简单而重要的设计模式,在Java中有着广泛的应用。它确保类的唯一实例,提高了系统资源的利用率和访问的一致性。本章介绍了单例模式的基本概念、线程安全实现方式、高级应用场景以及在Java中的实践。通过对单例模式的深入理解,开发者可以更加高效地管理和使用系统资源,提升程序的性能和稳定性。

7. 策略模式与状态模式的应用与对比

策略模式和状态模式都是行为型设计模式,它们在解决特定问题上有独到之处。本章将深入探讨这两种模式的应用场景和实现方式,并通过对比分析,帮助开发者在实际项目中做出更明智的设计决策。

策略模式的应用与实现

策略模式允许运行时选择算法的行为,使得算法能够在不修改客户端代码的情况下进行更改。它定义了一系列算法,将每一个算法封装起来,并使它们可相互替换。策略模式让算法的变化独立于使用算法的客户端。

Java 实现示例

下面是一个使用策略模式实现的简单例子,假设有不同的支付方式:

// 策略接口
public interface PaymentStrategy {
    void pay(int amount);
}

// 支付宝支付策略
public class AlipayStrategy implements PaymentStrategy {
    private String name;
    private String key;
    // 实现支付方法
    public void pay(int amount) {
        System.out.println("Paying " + amount + " with Alipay.");
    }
}

// 微信支付策略
public class WechatPayStrategy implements PaymentStrategy {
    // 实现支付方法
    public void pay(int amount) {
        System.out.println("Paying " + amount + " with Wechat Pay.");
    }
}

// 上下文
public class ShoppingCart {
    // 使用策略对象
    private PaymentStrategy paymentStrategy;
    private int amount;

    public ShoppingCart(int amount) {
        this.amount = amount;
    }

    public void setPaymentStrategy(PaymentStrategy paymentStrategy) {
        this.paymentStrategy = paymentStrategy;
    }

    public void checkout() {
        paymentStrategy.pay(amount);
    }
}

// 客户端代码
public class StrategyPatternDemo {
    public static void main(String[] args) {
        ShoppingCart cart = new ShoppingCart(100);
        PaymentStrategy alipay = new AlipayStrategy();
        PaymentStrategy wechat = new WechatPayStrategy();
        cart.setPaymentStrategy(alipay);
        cart.checkout();
        cart.setPaymentStrategy(wechat);
        cart.checkout();
    }
}

策略模式的适用场景

策略模式适用于以下情况:

  • 系统中有多个类仅在行为上稍有不同,如果使用继承可能会导致很多子类。
  • 需要动态地改变对象的行为,并且这些行为在运行时可以独立变化。

状态模式的应用与实现

状态模式允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。状态模式主要解决的是当控制一个对象状态转换的条件表达式过于复杂时的情况。

Java 实现示例

下面是一个状态模式的简单例子,假设有不同阶段的订单状态处理:

// 状态接口
public interface OrderState {
    void processOrder(Order order);
}

// 新订单状态
public class NewOrderState implements OrderState {
    public void processOrder(Order order) {
        System.out.println("Order is new.");
        order.setState(new ShippedOrderState());
    }
}

// 发货状态
public class ShippedOrderState implements OrderState {
    public void processOrder(Order order) {
        System.out.println("Order has been shipped.");
        order.setState(new DeliveredOrderState());
    }
}

// 已送达状态
public class DeliveredOrderState implements OrderState {
    public void processOrder(Order order) {
        System.out.println("Order has been delivered.");
        // 设置为最终状态,无法再进行转换
        order.setState(null);
    }
}

// 订单类
public class Order {
    private OrderState state;
    public Order(OrderState state) {
        this.state = state;
    }

    public void setState(OrderState state) {
        this.state = state;
    }

    public void process() {
        state.processOrder(this);
    }
}

// 客户端代码
public class StatePatternDemo {
    public static void main(String[] args) {
        Order order = new Order(new NewOrderState());
        order.process();
        order.process();
        order.process();
    }
}

状态模式的适用场景

状态模式适用于以下情况:

  • 一个对象的行为取决于它的状态,并且必须在运行时刻根据状态改变它的行为。
  • 代码中包含大量与对象状态有关的条件语句。

策略模式与状态模式的对比

尽管策略模式和状态模式在结构上非常相似,但它们的目的和应用场景有明显的区别:

  • 策略模式 主要用于改变对象的外部行为,即算法的改变;而 状态模式 主要用于改变对象内部的状态。
  • 在策略模式中,上下文类(Context)可以自行选择算法策略;在状态模式中,状态的改变通常由上下文以外的事件触发。

最终选择哪种模式取决于具体的应用需求,但通过上述的实现和示例,您应该能够更好地理解它们各自的应用场景和实现方式。这将有助于您在面对不同设计问题时做出更合适的选择。

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:设计模式是Java编程中的模板或最佳实践,用于构建可扩展和可维护的软件系统。本文将详细探讨Java中的代理模式,并概括介绍其他22种设计模式。代理模式通过代理类控制对目标对象的访问,并可以增加额外功能。除了代理模式,文中还将解释工厂模式、抽象工厂模式、单例模式等,并强调掌握这些模式对于提高代码质量和设计面向对象软件的重要性。

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值