首页 > 编程语言 >Java常见设计模式

Java常见设计模式

时间:2024-09-01 21:22:49浏览次数:5  
标签:Java void 常见 观察者 模式 class new 设计模式 public

设计模式(Design Patterns)是软件工程中用于解决特定问题的一系列最佳实践。它们是经过时间考验的、被广泛认可的软件设计经验,可以帮助开发者在面对常见问题时做出更好的设计决策。设计模式不是现成的代码,而是一套指导原则,用来指导开发者如何组织代码结构,以便于更好地应对变化和提高代码的可维护性。

设计模式三大类

创建型模式(Creational Patterns)

主要关注对象的创建,隐藏对象创建的复杂性,提高程序的可扩展性。常见的创建型模式包括单例模式、工厂方法模式、抽象工厂模式、建造者模式和原型模式。

结构型模式(Structural Patterns)

主要关注对象的组合,通过对象的组合提高程序的模块化。常见的结构型模式包括适配器模式、桥接模式、组合模式、装饰器模式、外观模式、享元模式和代理模式。

行为型模式(Behavioral Patterns)

主要关注对象之间的交互,描述对象如何协作以完成某种任务。常见的行为型模式包括责任链模式、命令模式、解释器模式、迭代器模式、中介者模式、备忘录模式、观察者模式、状态模式、策略模式和访问者模式。

单例模式(Singleton Pattern)

单例模式是一种常用的软件设计模式,用于确保一个类仅有一个实例,并提供一个全局访问点。在Java中,实现单例模式通常需要考虑线程安全和延迟加载(懒汉式)或立即加载(饿汉式)等因素。

饿汉式(线程安全)

饿汉式单例模式在类加载时就完成了初始化,所以类加载较慢,但获取对象的速度快。由于是在静态代码块中初始化的,所以它是线程安全的。

public class SingletonEager {
    // 在类加载时就完成了初始化,所以类加载较慢,但获取对象的速度快
    private static final SingletonEager INSTANCE = new SingletonEager();

    // 私有构造函数,防止外部通过new创建实例
    private SingletonEager() {}

    // 提供一个全局的静态方法,返回唯一实例
    public static SingletonEager getInstance() {
        return INSTANCE;
    }
}

懒汉式(线程不安全)

懒汉式单例模式在第一次使用时才进行初始化,实现了延迟加载。但是,如果多个线程同时访问getInstance()方法,并且此时实例还未被创建,那么就有可能导致多个实例被创建,从而违反了单例模式的原则。

public class SingletonLazyUnsafe {
    // 注意,这里不是final,也不是static
    private static SingletonLazyUnsafe instance;

    // 私有构造函数,防止外部通过new创建实例
    private SingletonLazyUnsafe() {}

    // 提供一个全局的静态方法,返回唯一实例
    public static SingletonLazyUnsafe getInstance() {
        if (instance == null) {
            instance = new SingletonLazyUnsafe();
        }
        return instance;
    }

    // 注意:上面的实现是线程不安全的
}

懒汉式(线程安全,但效率低)

为了解决懒汉式单例模式的线程安全问题,可以在getInstance()方法上加上synchronized关键字,但这会导致效率低下,因为每次调用getInstance()方法时都会进行线程锁定判断。

public class SingletonLazySafeButInefficient {
    private static SingletonLazySafeButInefficient instance;

    private SingletonLazySafeButInefficient() {}

    // 使用synchronized关键字保证线程安全
    public static synchronized SingletonLazySafeButInefficient getInstance() {
        if (instance == null) {
            instance = new SingletonLazySafeButInefficient();
        }
        return instance;
    }

    // 注意:这种方法虽然线程安全,但效率低下
}

双重检查锁定(Double-Checked Locking)

双重检查锁定是一种更加高效的实现懒汉式单例模式的方法。它首先检查实例是否存在,如果不存在才进行同步,从而减少了同步的开销。但是,需要注意的是,双重检查锁定必须配合volatile关键字使用,以确保实例创建的可见性。

public class SingletonDoubleChecked {
    // 使用volatile关键字保证多线程环境下instance变量的可见性和禁止指令重排序
    private static volatile SingletonDoubleChecked instance;

    private SingletonDoubleChecked() {}

    public static SingletonDoubleChecked getInstance() {
        if (instance == null) {
            // 第一次检查
            synchronized (SingletonDoubleChecked.class) {
                // 第二次检查
                if (instance == null) {
                    instance = new SingletonDoubleChecked();
                }
            }
        }
        return instance;
    }
}

工厂模式

在Java中,实现单例模式时需要注意线程安全和性能之间的平衡。通常,推荐使用饿汉式单例模式或双重检查锁定的懒汉式单例模式。

在Java中,工厂模式是一种常用的设计模式,它主要用于创建对象,而无需指定将要创建的具体类。这种模式通过定义一个共同的接口来创建对象,但让子类决定要实例化的类是哪一个。工厂模式隐藏了创建逻辑,并且可以在不修改客户端代码的情况下引入新的具体产品类。

工厂模式主要分为三种类型:简单工厂模式、工厂方法模式和抽象工厂模式。

1. 简单工厂模式

简单工厂模式不是GoF(四人帮)设计模式中的一种,但它经常被作为学习工厂模式的起点。在这种模式下,我们有一个工厂类,它根据传入的参数来决定创建哪个类的实例。

interface Product {
    void use();
}

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

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

// 简单工厂类
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 FactoryPatternDemo {
    public static void main(String[] args) {
        Product productA = SimpleFactory.createProduct("A");
        productA.use();

        Product productB = SimpleFactory.createProduct("B");
        productB.use();
    }
}

2. 工厂方法模式

工厂方法模式将对象的创建延迟到其子类中进行。在工厂方法模式中,我们定义了一个创建对象的接口,但让子类决定要实例化的类是哪一个。工厂方法让类的实例化推迟到子类中进行。

interface Product {
    void use();
}

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

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

interface Creator {
    Product factoryMethod();
}

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

class ConcreteCreatorB implements Creator {
    @Override
    public Product factoryMethod() {
        return new ConcreteProductB();
    }
}

// 客户端代码
public class FactoryMethodPatternDemo {
    public static void main(String[] args) {
        Creator creatorA = new ConcreteCreatorA();
        Product productA = creatorA.factoryMethod();
        productA.use();

        Creator creatorB = new ConcreteCreatorB();
        Product productB = creatorB.factoryMethod();
        productB.use();
    }
}

3. 抽象工厂模式

抽象工厂模式提供了一种创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。与工厂方法模式相比,抽象工厂模式创建的是产品族,而不是单个产品。

interface AbstractProductA {
    void use();
}

interface AbstractProductB {
    void use();
}

class ProductA1 implements AbstractProductA {
    @Override
    public void use() {
        System.out.println("Using ProductA1");
    }
}

class ProductA2 implements AbstractProductA {
    @Override
    public void use() {
        System.out.println("Using ProductA2");
    }
}

class ProductB1 implements AbstractProductB {
    @Override
    public void use() {
        System.out.println("Using ProductB1");
    }
}

class ProductB2 implements AbstractProductB {
    @Override
    public void use() {
        System.out.println("Using ProductB2");
    }
}

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

class ConcreteFactory1 implements AbstractFactory {
    @Override
    public AbstractProductA createProductA() {
        return new ProductA1();
    }

    @Override
    public AbstractProductB createProductB() {
        return new ProductB
    }
}

代理模式

代理模式(Proxy Pattern)在Java中是一种常用的结构型设计模式,它为其他对象提供一种代理以控制对这个对象的访问。代理模式通过创建一个代理对象,在客户端和目标对象之间起到中介的作用,从而实现对目标对象的间接访问和操作。这种方式可以增加额外的功能,如权限控制、日志记录、事务处理等,同时又不修改目标对象的代码。

在Java中实现代理模式,主要有两种方式:静态代理和动态代理。

1. 静态代理

静态代理是在编译时就确定代理类,代理类和目标类实现了相同的接口,并在代理类中通过调用目标类的方法来增强原有功能。

// 接口
interface Image {
    void display();
}

// 目标类
class RealImage implements Image {
    @Override
    public void display() {
        System.out.println("Displaying Real Image");
    }
}

// 代理类
class ProxyImage implements Image {
    private RealImage realImage;

    public ProxyImage(RealImage realImage) {
        this.realImage = realImage;
    }

    @Override
    public void display() {
        // 在调用真实对象的方法前后添加额外操作
        prepareImage();
        realImage.display();
        unloadImage();
    }

    private void prepareImage() {
        System.out.println("Preparing Image");
    }

    private void unloadImage() {
        System.out.println("Unloading Image");
    }
}

// 客户端代码
public class ProxyPatternDemo {
    public static void main(String[] args) {
        Image image = new ProxyImage(new RealImage());
        image.display();
    }
}

2. 动态代理

动态代理是在运行时动态生成代理类,并创建代理对象。Java的动态代理主要利用java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口来实现。

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

interface Image {
    void display();
}

class RealImage implements Image {
    @Override
    public void display() {
        System.out.println("Displaying Real Image");
    }
}

// 代理的InvocationHandler实现
class ImageInvocationHandler implements InvocationHandler {
    private Object target;

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

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 在方法调用之前可以添加额外处理
        System.out.println("Before method: " + method.getName());

        // 调用目标对象的方法
        Object result = method.invoke(target, args);

        // 在方法调用之后可以添加额外处理
        System.out.println("After method: " + method.getName());

        return result;
    }
}

// 客户端代码
public class DynamicProxyPatternDemo {
    public static void main(String[] args) {
        Image realImage = new RealImage();
        InvocationHandler handler = new ImageInvocationHandler(realImage);

        // 创建代理对象
        Image proxyImage = (Image) Proxy.newProxyInstance(
                realImage.getClass().getClassLoader(),
                realImage.getClass().getInterfaces(),
                handler);

        // 调用代理对象的方法
        proxyImage.display();
    }
}

在动态代理中,代理类的字节码是在运行时动态生成的,并且代理对象是在运行时创建的。这种方式提供了更大的灵活性,因为你可以在不修改目标类代码的情况下,通过实现InvocationHandler接口来添加新的功能。

观察者模式

在Java中,观察者模式(Observer Pattern)是一种行为设计模式,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。

组成部分

  1.  

    Subject(主题)‌:它知道它的观察者,并提供注册和删除观察者的接口。主题会维护一个观察者列表,并在其状态改变时通知所有观察者。

  2.  

    Observer(观察者)‌:是一个抽象类或接口,为所有的具体观察者定义一个更新接口,以便在得到主题的通知时更新自己。

  3.  

    ConcreteSubject(具体主题)‌:将有关状态存入具体观察者对象,并在状态发生改变时,向它的各个观察者发出通知。

  4.  

    ConcreteObserver(具体观察者)‌:实现Observer接口,以便在得到主题的通知时更新自身的状态。

实现步骤

  1.  

    定义Observer接口‌:通常包括一个update方法,当被观察对象的状态发生变化时,这个方法会被调用。

  2.  

    创建Subject类‌:包含观察者列表,以及注册、删除和通知观察者的方法。

  3.  

    创建具体观察者类‌:实现Observer接口,并在update方法中实现具体的更新逻辑。

  4.  

    创建具体主题类‌:继承Subject类,并在需要通知观察者时调用notifyObservers方法。

示例代码


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

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

// Subject类
class Subject {
    private List<Observer> observers = new ArrayList<>();

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

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

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

// ConcreteObserver类
class ConcreteObserver implements Observer {
    private String name;

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

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

// 使用示例
public class ObserverPatternDemo {
    public static void main(String[] args) {
        Subject subject = new Subject();

        Observer observer1 = new ConcreteObserver("Observer1");
        Observer observer2 = new ConcreteObserver("Observer2");

        subject.registerObserver(observer1);
        subject.registerObserver(observer2);

        subject.notifyObservers("Hello Observers!");

        subject.removeObserver(observer1);

        subject.notifyObservers("Hello again, but Observer1 is gone!");
    }
}

在上面的示例中,Subject类维护了一个观察者列表,并提供方法来注册和删除观察者。当Subject的状态发生变化时(在这个例子中是通过调用notifyObservers方法模拟的),它会通知所有已注册的观察者。每个ConcreteObserver实例在接收到通知时都会更新自己的状态,这里是通过打印一条消息来实现的。

houXuBuChong

标签:Java,void,常见,观察者,模式,class,new,设计模式,public
From: https://blog.csdn.net/WZX17307935391/article/details/141789681

相关文章

  • Java的高级特性
    Java的高级特性概述:Lambda表达式Lambda表达式是Java8及更高版本中引入的一个重要特性,它提供了一种简洁的方式来表示匿名方法(即没有名称的方法)。Lambda表达式特别适用于实现仅有一个抽象方法的接口(这类接口被称为函数式接口)。Lambda表达式使得代码更加简洁、易于阅读,并且提高......
  • java开发学习资料集合
    针对java的学习,不同阶段采用的方式是不一样的。本文把java的学习分为入门、实战、进阶三个阶段。下面分开来说技术社区1、 CSDNCSDN在线学习平台,集合了各领域资深技术专家.覆盖领域:人工智能、大数据、区块链、数据库、大学课程、认证考试、系统/网络、游戏开发、Web开发......
  • java入门:安装开发环境
    安装JDK1:百度搜索JDK8,找到下载地址2:登录并同意协议3:下载电脑对应的版本4:双击安装JDK5:记住安装的路径(重要)6:配置环境变量我的电脑>右键>属性找到高级系统设置>环境变量>在系统设置下找到新建>变量名是JAVA_HOME>变量值填安装的路径7:配置path变量在系统变量中找到path......
  • 【JavaScript】LeetCode:6-10
    文章目录6轮转数组7买卖股票的最佳时机Ⅰ8买卖股票的最佳时机Ⅱ9两数之和10字母异位词分组6轮转数组数组题目要求最终结果返回nums。方法1:拼接数组,n=nums.concat(nums);。方法2:数组直接截取,这里提供方法2的代码。/***@param{number[]}nums*@param......
  • ecmascript和javascript的区别
    1.简介1.1.概述1.1.1.ecmascriptECMAScript(简称ES)是JavaScript编程语言的一个标准化版本。它是为网络开发设计的一种轻量级的脚本语言,主要用于在网页上实现交互性和动态效果。ECMAScript是该语言的标准名称,而JavaScript是其最知名和广泛使用的实现。1.1.2.javascrip......
  • 【JAVA系列】java命令注入科普
    名词科普原理科普注入科普原创medi0cr1tyMedi0cr1ty这里只讨论使用java执行命令的情况(Runtime/ProcessBuilder),结合之前挖过过的一些case或者群里见到过的case来讲。名词科普命令解释器shell:是一种软件程序(可视作一门编程语言的代码解释器),它接收用户在命令行界面......
  • JavaSE-递归法解决二分查找、快速排序
    704.二分查找https://leetcode.cn/problems/binary-search/packagedemo;publicclassBinarySearch{publicstaticvoidmain(String[]args){BinarySearchbr=newBinarySearch();System.out.println(br.search(newint[]{1,2,3,4,5,6,7......
  • 如何解决《罗马2全面战争》中的twitchsdk_32_release.dll错误模块跳出问题?实用技巧与
    当您启动《罗马2全面战争》时,可能会遇到与twitchsdk_32_release.dll相关的错误提示,这可能导致游戏无法正常运行。本篇文章将深入探讨这一问题的原因以及提供多种解决方法,帮助您顺利启动游戏。twitchsdk_32_release.dll错误模块跳出的原因twitchsdk_32_release.dll文件出现......
  • 为什么Java仍旧生机盎然——对“为什么Java正在消亡”的回应
    [图片上传失败...(image-599293-1649288200226)]0.阅读完本文你将会了解Java作为热门语言之一所面临的争议了解Java的生态环境和未来1.前言原文标题:WhyJavaIsPerfectlyAlive——Aresponseto"WhyJavaIsDying"原文地址:https://betterprogramming.pub/why-......
  • 互联网 Java 工程师面试题(Java 面试题四)
    下面列出这份Java面试问题列表包含的主题多线程,并发及线程基础数据类型转换的基本原则垃圾回收(GC)Java集合框架数组字符串GOF设计模式SOLID抽象类与接口Java基础,如equals和hashcode泛型与枚举JavaIO与NIO常用网络协议Java中的数据结构和算法正则表达式JVM底......