代理模式(权限、远程调用、延迟加载、日志和缓存)

1、权限保护代理模式

使用 代理模式 实现一个"干饭村约会系统服务"的示例,能够通过代理控制对实际对象(比如用户的约会资料)访问、保护隐私、限制不正当操作等。

需求分析:

  1. 用户(Person):干饭村的用户有自己的个人资料(如名字、性别、兴趣等),他们可以查看自己的和其他人的资料,并且可以修改自己的资料。
  2. 代理 :分为两种代理:
    • 自己代理(Owner Proxy):允许用户查看和修改自己的资料。
    • 其他人代理(Non-Owner Proxy):允许其他人查看用户的资料,但不允许修改。
  3. 约会系统:负责管理用户资料的创建、展示和操作,使用代理控制访问权限。

解决方案:

  • 代理模式:通过创建代理对象来控制对实际对象的访问。我们创建不同的代理类,分别控制用户本人和其他人对其资料的操作权限。

代码实现

1. 创建 Person 接口

定义用户基本操作的接口。

java 复制代码
public interface Person {
    String getName();
    String getGender();
    String getInterests();
    int getHotOrNotRating();

    void setName(String name);
    void setGender(String gender);
    void setInterests(String interests);
    void setHotOrNotRating(int rating);
}
2. 实现 PersonImpl 类(实际的用户类)

这是实际的用户实现,存储用户的资料。

java 复制代码
public class PersonImpl implements Person {
    private String name;
    private String gender;
    private String interests;
    private int rating;
    private int ratingCount = 0;

    @Override
    public String getName() {
        return name;
    }

    @Override
    public String getGender() {
        return gender;
    }

    @Override
    public String getInterests() {
        return interests;
    }

    @Override
    public int getHotOrNotRating() {
        if (ratingCount == 0) return 0;
        return rating / ratingCount;
    }

    @Override
    public void setName(String name) {
        this.name = name;
    }

    @Override
    public void setGender(String gender) {
        this.gender = gender;
    }

    @Override
    public void setInterests(String interests) {
        this.interests = interests;
    }

    @Override
    public void setHotOrNotRating(int rating) {
        this.rating += rating;
        ratingCount++;
    }
}
3. 创建代理 OwnerProxyNonOwnerProxy
3.1 OwnerProxy 类(自己代理)

用户只能修改自己的资料,但不能为自己打分。

java 复制代码
public class OwnerProxy implements Person {
    private Person person;

    public OwnerProxy(Person person) {
        this.person = person;
    }

    @Override
    public String getName() {
        return person.getName();
    }

    @Override
    public String getGender() {
        return person.getGender();
    }

    @Override
    public String getInterests() {
        return person.getInterests();
    }

    @Override
    public int getHotOrNotRating() {
        return person.getHotOrNotRating();
    }

    @Override
    public void setName(String name) {
        person.setName(name);
    }

    @Override
    public void setGender(String gender) {
        person.setGender(gender);
    }

    @Override
    public void setInterests(String interests) {
        person.setInterests(interests);
    }

    @Override
    public void setHotOrNotRating(int rating) {
        throw new UnsupportedOperationException("You cannot rate yourself.");
    }
}
3.2 NonOwnerProxy 类(其他人代理)

其他人可以查看用户的资料并为其打分,但不能修改资料。

java 复制代码
public class NonOwnerProxy implements Person {
    private Person person;

    public NonOwnerProxy(Person person) {
        this.person = person;
    }

    @Override
    public String getName() {
        return person.getName();
    }

    @Override
    public String getGender() {
        return person.getGender();
    }

    @Override
    public String getInterests() {
        return person.getInterests();
    }

    @Override
    public int getHotOrNotRating() {
        return person.getHotOrNotRating();
    }

    @Override
    public void setName(String name) {
        throw new UnsupportedOperationException("You cannot change someone else's name.");
    }

    @Override
    public void setGender(String gender) {
        throw new UnsupportedOperationException("You cannot change someone else's gender.");
    }

    @Override
    public void setInterests(String interests) {
        throw new UnsupportedOperationException("You cannot change someone else's interests.");
    }

    @Override
    public void setHotOrNotRating(int rating) {
        person.setHotOrNotRating(rating);
    }
}
4. 创建 PersonProxyFactory 工厂类

用于创建不同类型的代理。

java 复制代码
public class PersonProxyFactory {
    public static Person getOwnerProxy(Person person) {
        return new OwnerProxy(person);
    }

    public static Person getNonOwnerProxy(Person person) {
        return new NonOwnerProxy(person);
    }
}
5. 测试代码

展示如何使用代理模式控制用户资料的访问权限。

java 复制代码
public class DatingSystemTest {
    public static void main(String[] args) {
        Person joe = new PersonImpl();
        joe.setName("Joe");
        joe.setGender("Male");
        joe.setInterests("Eating, Cooking");

        // 作为自己访问
        Person ownerProxy = PersonProxyFactory.getOwnerProxy(joe);
        System.out.println("Name: " + ownerProxy.getName());
        ownerProxy.setInterests("Gaming, Running"); // 修改成功
        try {
            ownerProxy.setHotOrNotRating(10); // 无法为自己打分
        } catch (Exception e) {
            System.out.println("Cannot rate yourself.");
        }

        // 作为他人访问
        Person nonOwnerProxy = PersonProxyFactory.getNonOwnerProxy(joe);
        System.out.println("Name: " + nonOwnerProxy.getName());
        try {
            nonOwnerProxy.setInterests("Hiking"); // 无法修改他人资料
        } catch (Exception e) {
            System.out.println("Cannot modify interests.");
        }
        nonOwnerProxy.setHotOrNotRating(8); // 允许为他人打分
        System.out.println("Rating: " + nonOwnerProxy.getHotOrNotRating());
    }
}

输出结果:

plaintext 复制代码
Name: Joe
Cannot rate yourself.
Name: Joe
Cannot modify interests.
Rating: 8

代码解释:

  1. Person 接口:定义了用户的基本操作,如获取和设置姓名、性别、兴趣以及打分等功能。
  2. PersonImpl:用户的实际实现类,存储用户的基本信息。
  3. 代理类
    • OwnerProxy:允许用户修改自己的资料,但不允许为自己打分。
    • NonOwnerProxy:允许其他人查看用户的资料并为其打分,但不允许修改资料。
  4. PersonProxyFactory :通过该工厂类,可以为用户生成自己代理(OwnerProxy)或他人代理(NonOwnerProxy)。
  5. DatingSystemTest:测试代理模式的功能,展示不同代理如何控制对用户资料的访问。

代理模式的优点:

  • 控制访问:可以为不同的用户提供不同的权限。比如用户自己可以修改资料,而其他人只能查看并打分。
  • 保护敏感信息:通过代理可以防止不正当操作,保护用户隐私和资料安全。
  • 灵活性:代理类可以很容易地根据需求调整行为。

相关解释

代理模式 (Proxy Pattern) 详细解释

1. 代理模式的概念

代理模式为其他对象提供一个代理或占位符来控制对这个对象的访问。可以将代理模式理解为通过代理类来间接访问目标对象,从而可以增加一些额外的功能,如权限控制、延迟加载、日志记录等。

2. 代理模式的结构
  • Subject 接口:定义了目标对象和代理类的通用接口,确保它们具有一致的行为,使得客户端可以通过接口与真实对象或代理对象进行交互。

  • RealSubject 真实主题类:实际需要访问的对象,代理模式的目标对象,完成实际的操作和功能。

  • Proxy 代理类:代理对象,控制对真实对象的访问。代理类可以在访问真实对象之前或之后增加额外的行为,比如权限检查、日志记录、缓存等。

3. 代理模式的应用场景
  • 远程代理:为一个对象在不同地址空间提供局部代理。比如 RMI(Remote Method Invocation)中使用远程代理。
  • 虚拟代理:在需要时才创建复杂的对象。比如在需要时才加载图片等资源。
  • 保护代理:控制对目标对象的访问权限,确保只有授权用户才能进行某些操作。
  • 缓存代理:为目标对象的操作提供结果缓存,以便多次调用时减少重复计算。
  • 智能引用代理:在访问对象时增加一些附加操作,比如引用计数、对象锁定等。

代理模式的类型

  1. 远程代理:代理对象在客户端上本地表示,而真实对象在远程服务器上执行。客户端调用代理对象方法,代理通过网络通信调用远程对象的方法,典型场景是分布式系统。

  2. 虚拟代理:用于延迟创建代价高的对象或在创建对象时提供一些开销优化。比如大型图片只有在显示时才加载。

  3. 保护代理:控制对对象的访问权限,保护敏感操作。保护代理可以确保调用者是否有足够的权限访问对象的某些功能。

  4. 智能引用代理:代理会做额外的操作,比如引用计数、资源管理、缓存等。每次访问对象时,代理可以自动处理某些额外的任务。

干饭村约会系统中的代理模式

4. 干饭村约会系统代理模式详解

在这个场景中,我们使用保护代理来控制对用户个人资料的访问权限。具体来说,系统根据不同的用户身份(是本人还是他人)提供不同的访问代理类。

4.1 核心类结构
  • Person 接口 :定义了用户的基本信息和操作,包括获取和设置姓名、性别、兴趣以及打分等操作。Person 接口相当于代理模式中的 Subject,表示所有操作的接口规范。

  • PersonImpl :用户的实际实现类,它包含了用户的基本资料信息。这个类相当于代理模式中的 RealSubject,是真正存储和处理用户资料的类。

  • OwnerProxy :为用户自己提供的代理类,它允许用户修改自己的资料,但不允许自己为自己打分。这个代理类控制对用户的实际对象(PersonImpl)的访问权限。

  • NonOwnerProxy:为其他用户提供的代理类,它允许其他用户查看并打分,但不允许他们修改用户的资料。它限制了其他用户对目标对象的访问。

4.2 功能解析
  • 自己代理(OwnerProxy :该代理类为用户本人提供服务,它允许用户查看和修改自己的个人信息,但限制了用户为自己打分。这是为了防止用户自己提升自己的受欢迎程度(HotOrNotRating),从而保持评分的公正性。

  • 他人代理(NonOwnerProxy:该代理类为其他用户提供服务,它允许其他用户查看用户的资料,并且可以为其打分(表示好感度或受欢迎度)。但是,该代理类限制了其他用户对个人信息的修改权限,防止篡改资料。

4.3 行为细节
  • 代理行为 :当客户端调用代理对象的方法时(例如调用 setHotOrNotRating()setInterests()),代理对象会首先检查是否具有执行该操作的权限。如果有权限,它会将操作转发给真实的 PersonImpl 对象,否则会抛出 UnsupportedOperationException 异常。

  • 透明代理 :代理类实现了和真实对象相同的接口 Person,使得客户端代码无须关心它是直接操作的真实对象还是通过代理对象操作,确保客户端代码的一致性和透明性。

代理模式的优点和适用性

5. 优点
  1. 控制访问:代理模式能够灵活地控制对真实对象的访问,比如干饭村约会系统中的访问权限控制。

  2. 增加额外功能:代理类可以在访问真实对象之前或之后执行一些额外的功能,比如权限校验、日志记录、性能监控、结果缓存等。

  3. 解耦客户端和实际对象:代理模式通过代理对象将客户端与真实对象隔离,使得真实对象的更改对客户端的影响最小。

  4. 支持远程调用或延迟加载:代理模式允许远程调用或者延迟加载,当对象创建代价较高时,代理可以帮助进行优化。

6. 缺点
  1. 增加复杂性:代理模式增加了系统的复杂性和间接性,尤其是在系统中有多个代理层时,可能导致代码难以维护和调试。

  2. 性能开销:代理模式引入了额外的间接调用,可能带来一定的性能开销,特别是在远程代理或虚拟代理中,网络延迟和额外处理可能会影响性能。

7. 适用场景
  1. 权限控制:需要对某些方法调用进行权限控制,比如干饭村的约会系统中,不同的用户对其他人的资料有不同的操作权限。

  2. 远程调用:当对象位于不同的地址空间时,可以使用远程代理模式来处理分布式系统中的通信。

  3. 延迟加载:当对象创建代价较高时,使用虚拟代理可以在需要时才创建对象,避免不必要的开销。

  4. 日志和缓存:可以使用代理模式在真实对象方法调用前后记录日志或缓存结果,提高性能。

代理模式的总结

代理模式通过代理对象控制对真实对象的访问,既可以提高系统的安全性和可控性,也可以减少客户端和真实对象之间的耦合。干饭村的约会系统通过代理模式保护用户的个人资料,保证只有自己能够修改资料,而其他人只能查看和打分,从而增强系统的安全性和公平性。

2、远程调用

远程调用(Remote Method Invocation, RMI)是一种允许对象在不同的 Java 虚拟机中调用方法的机制,适用于分布式系统。在 RMI 中,客户端可以通过代理(代理模式)调用远程服务器上的方法,而不需要知道方法的具体实现。

Java RMI 示例

我们将通过一个简单的 Java RMI 示例来展示如何进行远程调用。假设我们有一个远程接口 Calculator,客户端可以调用远程服务器上的加法和减法操作。

步骤:
  1. 定义远程接口 Calculator
  2. 实现远程接口
  3. 启动 RMI 注册表并绑定远程对象
  4. 编写客户端来调用远程对象

1. 创建远程接口 Calculator

远程接口需要继承 java.rmi.Remote 接口,并声明所有的远程方法可能抛出的 RemoteException

java 复制代码
import java.rmi.Remote;
import java.rmi.RemoteException;

public interface Calculator extends Remote {
    // 声明远程方法
    int add(int a, int b) throws RemoteException;
    int subtract(int a, int b) throws RemoteException;
}

2. 实现远程接口

远程对象的实现类需要继承 UnicastRemoteObject,这是为了使对象可以被远程调用。

java 复制代码
import java.rmi.server.UnicastRemoteObject;
import java.rmi.RemoteException;

public class CalculatorImpl extends UnicastRemoteObject implements Calculator {
    
    // 必须定义一个构造器,并抛出 RemoteException
    protected CalculatorImpl() throws RemoteException {
        super();
    }

    @Override
    public int add(int a, int b) throws RemoteException {
        return a + b;
    }

    @Override
    public int subtract(int a, int b) throws RemoteException {
        return a - b;
    }
}

3. 创建服务器端

服务器端需要启动 RMI 注册表,并将远程对象绑定到注册表中,使客户端能够通过注册表获取该远程对象。

java 复制代码
import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;

public class CalculatorServer {
    public static void main(String[] args) {
        try {
            // 启动 RMI 注册表,默认端口是 1099
            LocateRegistry.createRegistry(1099);

            // 创建远程对象
            Calculator calculator = new CalculatorImpl();

            // 绑定远程对象到 RMI 注册表
            Naming.rebind("rmi://localhost:1099/CalculatorService", calculator);

            System.out.println("Calculator Service is running...");
        } catch (RemoteException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

4. 创建客户端

客户端通过 RMI 注册表查找远程对象,并调用其方法。

java 复制代码
import java.rmi.Naming;

public class CalculatorClient {
    public static void main(String[] args) {
        try {
            // 查找远程对象
            Calculator calculator = (Calculator) Naming.lookup("rmi://localhost:1099/CalculatorService");

            // 调用远程方法
            int sum = calculator.add(5, 3);
            int difference = calculator.subtract(10, 4);

            System.out.println("Sum: " + sum);
            System.out.println("Difference: " + difference);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

5. 编译和运行

编译步骤:
  1. 先编译所有类:

    bash 复制代码
    javac Calculator.java CalculatorImpl.java CalculatorServer.java CalculatorClient.java
  2. 运行 RMI 注册表:

    在命令行中输入:

    bash 复制代码
    rmiregistry

    注意:RMI 注册表必须在服务器启动之前启动。否则,服务器端程序将无法绑定远程对象。

  3. 启动服务器:

    在命令行中运行:

    bash 复制代码
    java CalculatorServer
  4. 运行客户端:

    在客户端机器或同一台机器上,运行客户端程序:

    bash 复制代码
    java CalculatorClient

6. 运行结果:

当客户端成功调用远程方法时,输出类似如下:

bash 复制代码
Sum: 8
Difference: 6

解释:

  • Calculator 接口:定义了远程服务的行为,客户端和服务器都需要这个接口。
  • CalculatorImpl:这是远程服务的实际实现,服务器将它绑定到 RMI 注册表中。
  • 服务器端 :通过 LocateRegistry.createRegistry() 创建一个 RMI 注册表,然后使用 Naming.rebind() 方法将远程对象绑定到特定名称(在这个例子中是 CalculatorService)。
  • 客户端 :通过 Naming.lookup() 查找远程对象,然后调用远程对象的方法。RMI 底层处理网络通信,客户端可以像调用本地方法一样调用远程方法。

总结:

  • RMI 的核心思想是让客户端可以透明地调用位于远程服务器上的方法,就像调用本地对象的方法一样。
  • 在实现时,接口用于定义服务契约,客户端和服务器都依赖接口进行通信。
  • 代理模式在 RMI 中的应用体现在客户端通过代理对象(远程对象的存根)调用远程服务,而不是直接操作远程服务对象。

3、延迟加载

延迟加载代理模式(Virtual Proxy)是代理模式的一种,它用于推迟对昂贵资源的加载,直到真正需要时才初始化该资源。这种模式适用于那些初始化成本高但并非总是立即使用的对象,比如图像、文件等。

下面是一个基于延迟加载代理模式的 Java 示例:假设我们有一个图像浏览器,图像可能非常大,所以我们希望在需要显示时才加载图像。

示例场景:

  • Image 接口:定义图像的行为。
  • RealImage:实际的图像类,加载图像的过程可能比较耗时。
  • ImageProxy :代理类,只有在需要显示图像时才加载 RealImage
  • Client:客户端使用图像代理来控制加载。

代码实现

1. 定义 Image 接口

首先定义 Image 接口,包含一个 display() 方法,用于显示图像。

java 复制代码
public interface Image {
    void display();
}
2. 创建 RealImage

RealImage 类是图像的实际实现类,加载过程可能比较耗时,比如从磁盘加载大文件。在构造函数中模拟加载过程。

java 复制代码
public class RealImage implements Image {
    private String filename;

    // 模拟加载过程
    public RealImage(String filename) {
        this.filename = filename;
        loadImageFromDisk();
    }

    // 模拟图像从磁盘加载的操作
    private void loadImageFromDisk() {
        System.out.println("Loading image from disk: " + filename);
        try {
            Thread.sleep(3000); // 模拟耗时的加载
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void display() {
        System.out.println("Displaying image: " + filename);
    }
}
3. 创建 ImageProxy 类(延迟加载代理)

代理类 ImageProxy 实现 Image 接口,但它不会立即加载图像,只有在调用 display() 方法时才初始化 RealImage

java 复制代码
public class ImageProxy implements Image {
    private RealImage realImage;
    private String filename;

    public ImageProxy(String filename) {
        this.filename = filename;
    }

    @Override
    public void display() {
        // 延迟加载 RealImage,只有在真正需要时才初始化
        if (realImage == null) {
            realImage = new RealImage(filename);
        }
        realImage.display();
    }
}
4. 测试客户端

客户端不直接与 RealImage 交互,而是通过代理 ImageProxy 访问图像。

java 复制代码
public class Client {
    public static void main(String[] args) {
        Image image1 = new ImageProxy("photo1.jpg");
        Image image2 = new ImageProxy("photo2.jpg");

        // 图像还未被加载,此时不显示加载信息
        System.out.println("Images are created, but not loaded yet.");

        // 当需要显示图像时,图像才会被加载
        image1.display();  // 第一次显示图像,加载后再显示
        image1.display();  // 第二次显示图像,已经加载过,直接显示

        image2.display();  // 第一次显示图像,加载后再显示
    }
}

输出结果

plaintext 复制代码
Images are created, but not loaded yet.
Loading image from disk: photo1.jpg
Displaying image: photo1.jpg
Displaying image: photo1.jpg
Loading image from disk: photo2.jpg
Displaying image: photo2.jpg

代码分析

  1. Image 接口 :定义了图像的基本行为 display(),它是 RealImageImageProxy 共同的接口。

  2. RealImage :这是实际的图像实现类,包含从磁盘加载图像的模拟过程(耗时操作),并实现了 display() 方法来显示图像。

  3. ImageProxy :代理类实现了 Image 接口,但它控制 RealImage 的创建和加载。只有在 display() 方法被调用时,ImageProxy 才会创建 RealImage 对象并调用它的 display() 方法。这种设计避免了不必要的图像加载,从而提高了性能。

  4. 延迟加载 :图像只有在需要显示时才会加载,这就是延迟加载的特点。如果客户端从不调用 display() 方法,图像也不会被加载。

延迟加载代理模式的优点

  1. 性能优化:通过延迟加载减少不必要的初始化操作,特别是在加载过程非常耗时的情况下。
  2. 资源节省:当图像、文件或其他资源较大或较多时,代理模式可以有效节省内存和CPU资源。
  3. 透明性 :客户端代码不需要改变,仍然使用统一的 Image 接口,代理类控制实际的加载过程。

适用场景

  • 当对象的创建开销非常大,且并非每次都会使用到时。
  • 当需要延迟加载某些资源,比如大文件、图像、数据库连接等。
  • 在某些情况下,可以在需要时才执行某些昂贵的操作,而不是在对象初始化时立即执行。

通过这个示例,延迟加载代理模式 通过代理对象在需要时才实例化真正的对象,避免了不必要的资源浪费,提高了系统性能。

4、日志和缓存代理模式

日志代理模式缓存代理模式都是代理模式的扩展,用于在不改变原始类代码的情况下添加额外的功能。日志代理用于记录方法调用日志,而缓存代理用于减少重复计算或数据获取,提升性能。

下面是基于 日志代理模式缓存代理模式 的示例代码,展示如何使用代理模式记录方法调用日志和缓存计算结果。

示例场景

假设我们有一个简单的计算器类,进行一些复杂计算。我们将使用日志代理记录每次计算的调用,使用缓存代理存储计算结果以避免重复计算。

1. 创建 Calculator 接口

定义一个基本的 Calculator 接口,包含一个用于进行复杂计算的方法 compute()

java 复制代码
public interface Calculator {
    int compute(int a, int b);
}

2. 实现 Calculator

RealCalculator 类是计算器的实际实现类,它执行复杂的计算操作。在此示例中,假设 compute() 方法执行复杂的乘法运算。

java 复制代码
public class RealCalculator implements Calculator {
    @Override
    public int compute(int a, int b) {
        // 假设这是一个耗时的计算
        System.out.println("Performing complex calculation...");
        return a * b;
    }
}

3. 日志代理类 LoggingProxy

LoggingProxy 代理类用于记录每次调用 compute() 方法时的日志信息。该代理类会在方法调用前后打印日志,但不改变原始计算逻辑。

java 复制代码
public class LoggingProxy implements Calculator {
    private Calculator calculator;

    public LoggingProxy(Calculator calculator) {
        this.calculator = calculator;
    }

    @Override
    public int compute(int a, int b) {
        System.out.println("Logging: About to compute (" + a + ", " + b + ")");
        int result = calculator.compute(a, b);
        System.out.println("Logging: Computation result is " + result);
        return result;
    }
}

4. 缓存代理类 CachingProxy

CachingProxy 类用于缓存先前的计算结果,以避免重复计算。它使用一个 Map 来存储输入参数和对应的计算结果。

java 复制代码
import java.util.HashMap;
import java.util.Map;

public class CachingProxy implements Calculator {
    private Calculator calculator;
    private Map<String, Integer> cache = new HashMap<>();

    public CachingProxy(Calculator calculator) {
        this.calculator = calculator;
    }

    @Override
    public int compute(int a, int b) {
        String key = a + "," + b;

        // 检查缓存中是否已有该计算结果
        if (cache.containsKey(key)) {
            System.out.println("Cache hit for (" + a + ", " + b + ")");
            return cache.get(key);
        }

        // 如果缓存中没有,进行计算并存入缓存
        int result = calculator.compute(a, b);
        cache.put(key, result);
        return result;
    }
}

5. 测试客户端

在客户端中,我们先创建一个 RealCalculator 实例,然后通过代理类进行调用。

java 复制代码
public class Client {
    public static void main(String[] args) {
        // 创建实际的计算器对象
        Calculator realCalculator = new RealCalculator();

        // 包装成日志代理
        Calculator loggingCalculator = new LoggingProxy(realCalculator);

        // 包装成缓存代理
        Calculator cachingCalculator = new CachingProxy(loggingCalculator);

        // 执行一些计算操作
        System.out.println("First calculation:");
        System.out.println("Result: " + cachingCalculator.compute(3, 4));

        System.out.println("\nSecond calculation (same values, should use cache):");
        System.out.println("Result: " + cachingCalculator.compute(3, 4));

        System.out.println("\nThird calculation (different values):");
        System.out.println("Result: " + cachingCalculator.compute(5, 6));
    }
}

6. 运行结果

plaintext 复制代码
First calculation:
Logging: About to compute (3, 4)
Performing complex calculation...
Logging: Computation result is 12
Result: 12

Second calculation (same values, should use cache):
Cache hit for (3, 4)
Result: 12

Third calculation (different values):
Logging: About to compute (5, 6)
Performing complex calculation...
Logging: Computation result is 30
Result: 30

代码解释

  1. RealCalculator :这是实际的计算类,它执行真实的计算操作。每次调用 compute() 方法时,它都会进行一次"复杂"的计算(在此示例中为乘法)。

  2. LoggingProxy :它是日志代理类,用于记录计算的调用过程。在调用 compute() 方法之前,代理类会记录日志,表示即将开始计算;在调用方法之后,代理类会记录计算结果。

  3. CachingProxy:它是缓存代理类,负责缓存先前的计算结果,避免重复计算。如果相同的输入参数之前已经计算过,代理类会直接返回缓存中的结果,而不再调用实际的计算逻辑。

  4. 客户端 :客户端使用代理类来操作计算器对象,而不直接操作 RealCalculator。客户端并不关心计算器是直接执行的还是通过代理执行的,只关心计算的结果。

总结

  • 日志代理模式 可以在不修改原始类的前提下增加方法调用日志记录,方便调试和监控。
  • 缓存代理模式 可以通过缓存计算结果来提高性能,避免重复计算,适用于昂贵的计算任务。
  • 代理模式通过使用代理类包装原始类,增强了系统的灵活性,同时保持了客户端的调用方式不变。
相关推荐
世间万物皆对象7 分钟前
Spring Boot核心概念:日志管理
java·spring boot·单元测试
没书读了36 分钟前
ssm框架-spring-spring声明式事务
java·数据库·spring
小二·43 分钟前
java基础面试题笔记(基础篇)
java·笔记·python
开心工作室_kaic1 小时前
ssm161基于web的资源共享平台的共享与开发+jsp(论文+源码)_kaic
java·开发语言·前端
懒洋洋大魔王1 小时前
RocketMQ的使⽤
java·rocketmq·java-rocketmq
武子康1 小时前
Java-06 深入浅出 MyBatis - 一对一模型 SqlMapConfig 与 Mapper 详细讲解测试
java·开发语言·数据仓库·sql·mybatis·springboot·springcloud
转世成为计算机大神2 小时前
易考八股文之Java中的设计模式?
java·开发语言·设计模式
qq_327342732 小时前
Java实现离线身份证号码OCR识别
java·开发语言
阿龟在奔跑3 小时前
引用类型的局部变量线程安全问题分析——以多线程对方法局部变量List类型对象实例的add、remove操作为例
java·jvm·安全·list
飞滕人生TYF3 小时前
m个数 生成n个数的所有组合 详解
java·递归