百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程字典 > 正文

Java中常用的五种设计模式(java中常用的五种设计模式是)

toyiye 2024-09-16 05:57 3 浏览 0 评论

前言:

当代软件开发中,设计模式已经成为一种标准的编程实践。在Java编程中,设计模式也同样重要。Java设计模式是软件开发中广泛应用的一种编程方法,它可以帮助开发人员更快地编写出高效、可靠和可维护的代码。

其中五种设计模式,是我们开发过程中经常使用的,分别是单例模式、工厂模式、观察者模式和适配器模式。下面文章分别从每个设计模式的应用场景、代码实现和使用小结做细致的分析。

1.单例模式(Singleton Pattern)

1.1应用场景

  1. 日志记录器 在一个应用程序中,通常会有多个模块或类需要记录日志。为了避免创建多个日志记录器实例,使用单例模式可以确保只有一个日志记录器实例,从而避免重复记录日志并提高应用程序的性能。
  2. 数据库连接 在一个应用程序中,如果需要频繁地与数据库交互,使用单例模式可以确保只有一个数据库连接实例,从而减少数据库连接的数量,提高应用程序的性能。
  3. 系统配置 在一个应用程序中,通常会有一些全局的配置参数,如数据库连接字符串、服务器地址、缓存大小等。使用单例模式可以确保只有一个配置实例,从而方便管理和修改配置参数。

1.2代码实现

懒汉式

public class Singleton {
    private static Singleton instance;
    
    private Singleton() {
        // 私有构造函数,防止外部实例化
    }
    
    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

静态内部类方式

SingletonHolder是一个静态内部类,它包含一个静态的INSTANCE成员变量,用于存储单例对象。在第一次调用getInstance方法时,静态内部类会被加载,从而创建单例对象。这种方式既兼顾了线程安全又兼顾了延迟加载的需求。

public class Singleton {
    private Singleton() {
        // 私有构造函数,防止外部实例化
    }
    
    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
    
    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }
}

饿汉式

饿汉式在类加载时就创建了单例对象,所以不存在线程安全问题。不过,这种方式可能会导致不必要的资源浪费,因为单例对象的创建可能在应用程序启动时就完成了,而有些应用场景中可能并不需要使用单例对象。

public class Singleton {
    // 在类加载时就创建单例对象
    private static Singleton instance = new Singleton();
    
    // 将构造函数设为私有,禁止外部创建实例
    private Singleton() {}
    
    // 提供获取单例对象的方法
    public static Singleton getInstance() {
        return instance;
    }
}

双重检查锁

它可以在保证线程安全的同时实现延迟加载

public class Singleton {
    private static volatile Singleton instance;

    private Singleton() {}

    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

枚举方式

使用枚举实现单例模式的好处是,可以避免反射和序列化攻击。因为枚举类型的构造函数是私有的,所以无法使用反射来创建实例;而且枚举类型的实例在序列化和反序列化时会自动处理好,所以也无法通过序列化和反序列化来破坏单例。这种方式不常用的。

public enum Singleton {
    INSTANCE;

    public void doSomething() {
        // TODO: 实现单例对象的功能
    }
}

1.3使用小结

  • 对线程安全和性能要求较高,可以考虑使用饿汉式双重检查锁方式实现单例模式。这两种方式都能保证线程安全,而且在大多数情况下性能也比较好。
  • 如果你对线程安全要求不是很高,或者希望在第一次访问时才创建单例对象,可以考虑使用懒汉式或者静态内部类方式。这两种方式都是延迟加载的,只有在需要时才会创建单例对象。懒汉式不是线程安全的,需要通过加锁等方式来保证线程安全;而静态内部类方式则是天生线程安全的,不需要额外的处理。
  • 希望实现简单、代码少,且不需要考虑线程安全和延迟加载的问题,可以考虑使用枚举方式。这种方式不仅代码简单,而且天生线程安全、单例对象创建和调用都很方便。

2.工厂模式(Factory Pattern)

工厂模式是一种创建型模式,它可以为开发人员提供一种在不直接实例化对象的情况下创建对象的方法。工厂模式通过提供一个通用的接口和一组实现,来隐藏具体实现的细节,从而降低了代码的耦合度和依赖性。

2.1应用场景

  1. 对象的创建过程比较复杂,需要进行封装:如果创建一个对象需要进行复杂的初始化过程,或者需要从多个地方获取数据才能创建对象,那么使用工厂模式可以将这些过程封装起来,让客户端代码更加简洁和易于理解。
  2. 需要动态扩展或修改对象的创建过程:如果需要增加或修改某个对象的创建过程,而又不希望对客户端代码产生影响,那么使用工厂模式可以很方便地实现这个需求。
  3. 需要统一管理对象的创建:如果需要统一管理对象的创建过程,或者需要对创建的对象进行某些统一的处理,那么使用工厂模式可以很好地实现这个需求。
  4. 需要根据不同的条件创建不同的对象:如果需要根据不同的条件来创建不同类型的对象,那么使用工厂模式可以很方便地实现这个需求。

2.2代码实现

通过一个工厂类来封装对象的创建过程,客户端只需要告诉工厂类需要创建哪种类型的对象即可。将对象的创建过程与客户端代码分离开来,使代码更加灵活和易于扩展。

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

// 具体产品类A
public class ConcreteProductA implements Product {
    @Override
    public void operation() {
        System.out.println("ConcreteProductA operation.");
    }
}

// 具体产品类B
public class ConcreteProductB implements Product {
    @Override
    public void operation() {
        System.out.println("ConcreteProductB operation.");
    }
}

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

客户端可以通过调用SimpleFactory.createProduct方法来创建不同类型的产品对象。

Product productA = SimpleFactory.createProduct("A");
productA.operation(); // 输出 "ConcreteProductA operation."

Product productB = SimpleFactory.createProduct("B");
productB.operation(); // 输出 "ConcreteProductB operation."

2.3使用小结

在Java中,工厂模式广泛应用于各种框架和类库中,例如JDBC中的DataSource工厂、Spring框架中的Bean工厂、MyBatis框架中的SqlSessionFactory等等。

3.观察者模式(Observer Pattern)

观察者模式是一种行为型模式,它定义了对象之间的一种一对多的依赖关系。在这种模式中,一个对象发生变化时,所有依赖于它的对象都会得到通知并自动更新。

3.1应用场景

  1. 事件处理机制:Java中的Swing GUI框架就是基于观察者模式实现的,当用户与组件交互时,组件会向注册的监听器发送事件通知,以触发相应的事件处理方法。
  2. 日志记录:Java中的日志系统也是基于观察者模式实现的,当日志发生变化时,它会通知所有注册的观察者,例如文件输出流、控制台输出流等,从而实现日志的输出和记录。
  3. 用户界面设计:在Java中,用户界面设计中的许多元素都可以使用观察者模式实现,例如菜单项、按钮、文本框等,当用户与这些元素交互时,它们会向注册的监听器发送事件通知,以触发相应的事件处理方法。
  4. 多线程编程:在Java中,观察者模式还可以用于多线程编程中,当一个线程发生了某些变化时,它可以向其他线程发送通知,以实现线程间的协作和同步。

3.2代码实现

这个示例中,ConcreteSubject 实现了 Subject 接口,它维护了一个 observers 列表,用于保存注册的观察者对象。当被观察者发生变化时,它会遍历观察者列表,调用每个观察者的 update 方法。

ConcreteObserver 实现了 Observer 接口,它可以接收来自被观察者的通知,并执行相应的操作。

在测试类 ObserverPatternDemo 中,我们创建了一个具体的被观察者对象 ConcreteSubject,并注册了两个具体的观察者对象 observer1 和 observer2。当被观察者发生变化时,它会通知所有注册的观察者对象,并调用它们的 update 方法。

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

// 被观察者接口
interface Subject {
    void registerObserver(Observer observer);
    void removeObserver(Observer observer);
    void notifyObservers(String message);
}

// 具体的被观察者实现类
class ConcreteSubject implements Subject {
    private List<Observer> observers = new ArrayList<>();

    @Override
    public void registerObserver(Observer observer) {
        observers.add(observer);
    }

    @Override
    public void removeObserver(Observer observer) {
        observers.remove(observer);
    }

    @Override
    public void notifyObservers(String message) {
        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 ObserverPatternDemo {
    public static void main(String[] args) {
        Subject subject = new ConcreteSubject();
        Observer observer1 = new ConcreteObserver("Observer1");
        Observer observer2 = new ConcreteObserver("Observer2");
        subject.registerObserver(observer1);
        subject.registerObserver(observer2);
        subject.notifyObservers("Hello, World!");
    }
}

3.3使用小结

观察者模式的优点在于它提供了一种松耦合的方式,让观察者和主题之间的依赖关系变得更加灵活,同时也可以使得程序更易于扩展和维护。

观察者模式的应用场景包括:当一个抽象模型有两个方面,其中一个方面依赖于另一个方面时;当一个对象的改变需要同时改变其他对象的时候;当一个对象的改变需要通知其他对象而又不希望与被通知对象形成紧耦合关系时。

4.适配器模式(Adapter Pattern)

适配器模式是一种结构型模式,它可以将一个类的接口转换成客户端所期望的另一种接口。适配器模式可以帮助开发人员在不修改现有代码的情况下,将不兼容的类组合在一起。适配器模式包括以下几个组成部分:

  • 目标接口(Target Interface):客户端期望的接口。
  • 适配器(Adapter):充当两个不兼容接口之间的桥梁,使得它们可以互相通信。
  • 适配者(Adaptee):需要被适配的对象,它的接口与目标接口不兼容。
  • 客户端(Client):使用目标接口的对象。

4.1应用场景

  1. 当需要将一个已有的类或接口与另一个不兼容的类或接口进行协同工作时。
  2. 当需要对一个已有的类或接口进行修改,以满足客户端的需求时,但是不希望修改该类或接口的源代码。
  3. 当需要重新使用一个已有的类或接口,但是不能直接使用该类或接口的方法时。

4.2代码实现

在这个示例中,我们有一个目标接口 Target 和一个不兼容的适配者 Adaptee,我们需要创建一个适配器 Adapter 来让它们能够一起工作。

适配器实现了目标接口 Target,并在构造函数中接受一个适配者对象 Adaptee,然后在实现目标接口的 request 方法中调用适配者的 specificRequest 方法。

在客户端中,我们创建了一个适配者对象 adaptee,并将其传递给适配器的构造函数创建一个适配器对象 adapter。最后,我们使用目标接口 Target 中定义的方法 request 来访问适配器,从而调用适配者的方法。

// 目标接口
interface Target {
    void request();
}

// 适配者
class Adaptee {
    void specificRequest() {
        System.out.println("Adaptee specificRequest.");
    }
}

// 适配器
class Adapter implements Target {
    private Adaptee adaptee;

    public Adapter(Adaptee adaptee) {
        this.adaptee = adaptee;
    }

    @Override
    public void request() {
        adaptee.specificRequest();
    }
}

// 客户端
public class AdapterPatternDemo {
    public static void main(String[] args) {
        Adaptee adaptee = new Adaptee();
        Target target = new Adapter(adaptee);
        target.request();
    }
}

4.3使用小结

适配器模式是一种非常有用的设计模式,在JDK中被广泛应用,可以提供一致的接口,比如:

  1. Java IO 流是一个常见的适配器模式的例子。它提供了一组标准的接口来访问各种类型的数据源,包括文件、网络连接、内存等等。每个数据源都有自己的接口,但是 Java IO 流可以将这些不同的接口转换为标准的接口,从而提供一致的访问方式。
  2. Java Servlet API 也是一个常见的适配器模式的例子。它定义了一组接口来处理 HTTP 请求和响应,包括 doGet()、doPost()、doPut() 等等。每个 Servlet 都必须实现这些接口,但是用户只需要实现其中的一部分即可。这些 Servlet 之间的适配工作由 Servlet 容器完成。

5.装饰器模式(Decorator Pattern)

5.1应用场景

  1. 当需要在不修改现有对象结构的前提下增加新的功能或特性时,可以使用装饰器模式。这样可以保持原有代码的稳定性和兼容性,同时也可以增加代码的灵活性和可扩展性。
  2. 当需要动态地向对象添加或删除功能时,可以使用装饰器模式。这样可以在运行时动态地添加或删除功能,而不需要修改现有的代码。
  3. 当需要为多个对象添加相同的功能时,可以使用装饰器模式。这样可以将相同的功能封装在装饰器中,以便于复用和管理。

5.2代码实现

该示例代码中,Shape 是一个接口,定义了一个 draw 方法,表示绘制图形的操作。Circle 是一个实现 Shape 接口的类,表示一个圆形。

ShapeDecorator 是一个装饰器抽象类,实现了 Shape 接口,并包含一个 Shape 类型的变量 decoratedShape,表示要装饰的对象。RedShapeDecorator 是一个具体的装饰器类,继承了 ShapeDecorator 类,并实现了 draw 方法,在绘制图形时添加了一个红色的边框。

在 main 方法中,我们创建了原始对象 Circle,以及两个装饰器对象 RedShapeDecorator,分别装饰了 Circle 和 Rectangle 对象。通过调用 draw 方法,我们可以看到对象被动态地添加了一个红色的边框,而不需要修改原有的代码。

// 定义接口
interface Shape {
    void draw();
}

// 实现接口
class Circle implements Shape {
    @Override
    public void draw() {
        System.out.println("Shape: Circle");
    }
}

class Rectangle implements Shape {
    @Override
    public void draw() {
        System.out.println("Shape: Rectangle");
    }
}

// 装饰器抽象类
abstract class ShapeDecorator implements Shape {
    protected Shape decoratedShape;

    public ShapeDecorator(Shape decoratedShape){
        this.decoratedShape = decoratedShape;
    }

    public void draw(){
        decoratedShape.draw();
    }
}

// 具体装饰器类
class RedShapeDecorator extends ShapeDecorator {
    public RedShapeDecorator(Shape decoratedShape) {
        super(decoratedShape);
    }

    @Override
    public void draw() {
        decoratedShape.draw();
        setRedBorder(decoratedShape);
    }

    private void setRedBorder(Shape decoratedShape){
        System.out.println("Border Color: Red");
    }
}

// 测试代码
public class DecoratorPatternDemo {
    public static void main(String[] args) {
        // 创建原始对象
        Shape circle = new Circle();

        // 创建装饰器对象
        Shape redCircle = new RedShapeDecorator(new Circle());
        Shape redRectangle = new RedShapeDecorator(new Rectangle());

        // 调用方法
        System.out.println("Circle with normal border");
        circle.draw();

        System.out.println("\nCircle of red border");
        redCircle.draw();

        System.out.println("\nRectangle of red border");
        redRectangle.draw();
    }
}

5.3使用小结

在实际应用中,装饰器模式经常用于图形界面(GUI)开发、输入/输出流处理、缓存机制、日志记录等领域,可以有效地提高程序的可扩展性和可维护性。比如

  1. 装饰器模式被广泛应用于Java IO流中,以提供各种不同的功能,如缓存、压缩、加密等等。例如,可以使用 BufferedReader 来缓存读取文件的数据,使用 GZIPOutputStream 来压缩数据,使用 CipherOutputStream 来加密数据等等。
  2. Java Swing 组件是一个经典的装饰器模式的例子。它允许在运行时动态地向组件添加功能,如边框、背景、文本等等。例如,可以使用 BorderFactory 来向组件添加边框,使用 Color 来设置组件的背景颜色,使用 Font 来设置组件的字体等等。
  3. 在 Spring 框架中,装饰器模式被广泛应用于实现 AOP。AOP通过代理模式和装饰器模式实现。JDK 动态代理和 CGLIB 动态代理两种方式实现代理模式,使用装饰器模式对目标对象进行包装,从而实现通知 (Advice) 的织入。例如,可以使用 @Transactional 来添加事务处理的功能,使用 @Cacheable 来添加缓存处理的功能,等等。

写在最后

前面介绍了java开发中我们经常用到的五种设计模式。我们在开发中有时候很难想到一个业务具体可以用到哪种设计模式。运用设计模式可以使我们的代码非常优雅和健壮。所以我们要有意识的应用设计模式到我们的项目。

相关推荐

为何越来越多的编程语言使用JSON(为什么编程)

JSON是JavascriptObjectNotation的缩写,意思是Javascript对象表示法,是一种易于人类阅读和对编程友好的文本数据传递方法,是JavaScript语言规范定义的一个子...

何时在数据库中使用 JSON(数据库用json格式存储)

在本文中,您将了解何时应考虑将JSON数据类型添加到表中以及何时应避免使用它们。每天?分享?最新?软件?开发?,Devops,敏捷?,测试?以及?项目?管理?最新?,最热门?的?文章?,每天?花?...

MySQL 从零开始:05 数据类型(mysql数据类型有哪些,并举例)

前面的讲解中已经接触到了表的创建,表的创建是对字段的声明,比如:上述语句声明了字段的名称、类型、所占空间、默认值和是否可以为空等信息。其中的int、varchar、char和decimal都...

JSON对象花样进阶(json格式对象)

一、引言在现代Web开发中,JSON(JavaScriptObjectNotation)已经成为数据交换的标准格式。无论是从前端向后端发送数据,还是从后端接收数据,JSON都是不可或缺的一部分。...

深入理解 JSON 和 Form-data(json和formdata提交区别)

在讨论现代网络开发与API设计的语境下,理解客户端和服务器间如何有效且可靠地交换数据变得尤为关键。这里,特别值得关注的是两种主流数据格式:...

JSON 语法(json 语法 priority)

JSON语法是JavaScript语法的子集。JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔花括号保存对象方括号保存数组JS...

JSON语法详解(json的语法规则)

JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔大括号保存对象中括号保存数组注意:json的key是字符串,且必须是双引号,不能是单引号...

MySQL JSON数据类型操作(mysql的json)

概述mysql自5.7.8版本开始,就支持了json结构的数据存储和查询,这表明了mysql也在不断的学习和增加nosql数据库的有点。但mysql毕竟是关系型数据库,在处理json这种非结构化的数据...

JSON的数据模式(json数据格式示例)

像XML模式一样,JSON数据格式也有Schema,这是一个基于JSON格式的规范。JSON模式也以JSON格式编写。它用于验证JSON数据。JSON模式示例以下代码显示了基本的JSON模式。{"...

前端学习——JSON格式详解(后端json格式)

JSON(JavaScriptObjectNotation)是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。它基于JavaScriptProgrammingLa...

什么是 JSON:详解 JSON 及其优势(什么叫json)

现在程序员还有谁不知道JSON吗?无论对于前端还是后端,JSON都是一种常见的数据格式。那么JSON到底是什么呢?JSON的定义...

PostgreSQL JSON 类型:处理结构化数据

PostgreSQL提供JSON类型,以存储结构化数据。JSON是一种开放的数据格式,可用于存储各种类型的值。什么是JSON类型?JSON类型表示JSON(JavaScriptO...

JavaScript:JSON、三种包装类(javascript 包)

JOSN:我们希望可以将一个对象在不同的语言中进行传递,以达到通信的目的,最佳方式就是将一个对象转换为字符串的形式JSON(JavaScriptObjectNotation)-JS的对象表示法...

Python数据分析 只要1分钟 教你玩转JSON 全程干货

Json简介:Json,全名JavaScriptObjectNotation,JSON(JavaScriptObjectNotation(记号、标记))是一种轻量级的数据交换格式。它基于J...

比较一下JSON与XML两种数据格式?(json和xml哪个好)

JSON(JavaScriptObjectNotation)和XML(eXtensibleMarkupLanguage)是在日常开发中比较常用的两种数据格式,它们主要的作用就是用来进行数据的传...

取消回复欢迎 发表评论:

请填写验证码