第三章 设计模式(2023版本IDEA)

embedded/2024/11/14 2:22:09/

学习目标

  • 3.1 设计模式概述
  • 3.2 软件可复用问题和面向对象设计原则
      • 一、软件可复用问题
      • 二、面向对象设计原则
        • 1. 单一责任原则(Single Responsibility Principle, SRP)
        • 2. 开放-封闭原则(Open-Closed Principle, OCP)
        • 3. 里氏替换原则(Liskov Substitution Principle, LSP)
        • 4. 依赖倒置原则(Dependency Inversion Principle, DIP)
        • 5. 接口隔离原则(Interface Segregation Principle, ISP)
        • 6. 迪米特法则(Least Knowledge Principle,LKP)
        • 7. 合成/聚合复用原则(Composite/Aggregate Reuse Principle, CARP)
  • 3.3 设计模式的应用
    • 3.3.1 工厂方法模式
        • 1. 简单工厂模式(Simple Factory Pattern)
        • 2. 工厂方法模式(Factory Method Pattern)
        • 3.抽象工厂模式(Abstract Factory Pattern)
    • 3.3.2 代理模式
        • 代理模式包含如下角色

  前面课程中已经学习了面向对象的三大特征,在后续的学习过程中对面向对象的认识会不断深入,不断提高运用面向对象思想解决问题的能力。(如果没有了解可以去我主页看看Java开发之框架基础技术第1-2章(2023版本IEDA)来学习)本章学习面向对象的一些高级应用一一设计模式设计模式被广泛运用在java框架技术中,学习设计模式对于理解框架的工作原理会有所帮助。

学习方法
  设计模式虽有很多种,但总是可以从解锅台、提高复用性这些方向来理解。首先要明确每种设计模式的使用场景,明确其要解决的问题,进而理解其解决该问题的思路。

3.1 设计模式概述

  设计模式(Design Pattern)是人们在长期的软件开发中对一些经验的总结,是对某些特定问题经过实践检验的特定解决方法。就像兵法中的三十六计,总结了36种对于战争中某些场合的可行性计谋战术一一"围魏救赵"“声东击西”"走为上"等,可以说三十六计中的每一计都是一种模式。

  1. 创建型模式(Creational Patterns)
    创建型模式主要用于对象的创建,它们通过隐藏对象的创建逻辑来提供更大的灵活性。

示例:单例模式(Singleton Pattern)
单例模式确保一个类仅有一个实例,并提供一个全局访问点。

java">public class Singleton {  // 私有静态变量,保存类的唯一实例  private static Singleton instance;  // 私有构造函数,防止外部通过new创建实例  private Singleton() {}  // 提供一个全局的静态方法,返回唯一实例  public static Singleton getInstance() {  if (instance == null) {  instance = new Singleton();  }  return instance;  }  // 其他方法...  
}
  1. 结构型模式(Structural Patterns)
    结构型模式关注于类、接口和对象之间的组合关系,以创建更大的结构。

示例:适配器模式(Adapter Pattern)
适配器模式将一个类的接口转换成客户端期望的另一个接口形式,使类之间的接口不兼容问题可以通过一个中间类来解决。

java">// 目标接口  
public interface Target {  void request();  
}  // 需要适配的类  
public class Adaptee {  public void specificRequest() {  // 具体请求  }  
}  // 适配器类  
public class Adapter implements Target {  private Adaptee adaptee;  public Adapter(Adaptee adaptee) {  this.adaptee = adaptee;  }  @Override  public void request() {  adaptee.specificRequest();  }  
}
  1. 行为型模式(Behavioral Patterns)
    行为型模式主要关注对象之间的通信和交互方式。

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

java">// 抽象主题类  
public abstract class Subject {  // 维护一个观察者列表  private List<Observer> observers = new ArrayList<>();  // 注册观察者  public void registerObserver(Observer o) {  observers.add(o);  }  // 移除观察者  public void removeObserver(Observer o) {  observers.remove(o);  }  // 通知所有观察者  protected void notifyObservers() {  for (Observer observer : observers) {  observer.update(this);  }  }  // 抽象的通知方法  public abstract void stateChanged();  
}  // 抽象观察者类  
public interface Observer {  void update(Subject subject);  
}  // 具体实现  
// ...(具体主题类和观察者类的实现)

注意
  上述代码示例仅用于说明设计模式的基本思想和结构,并未包含完整的错误处理和优化逻辑。在实际应用中,您可能需要根据具体需求进行调整和完善。

设计模式的学习和应用需要结合具体的项目实践,通过不断尝试和反思来加深对设计模式的理解和应用能力。

3.2 软件可复用问题和面向对象设计原则

  软件可复用问题和面向对象设计原则是两个紧密相连的概念。在软件开发中,可复用性是一个重要的目标,它旨在通过重用已有的软件组件来降低开发成本、提高开发效率和软件质量。面向对象设计原则则为实现软件的可复用性提供了指导和支持。

一、软件可复用问题

  软件复用(Software Reuse)是使用已有的软件组件去实现或更新软件系统的过程。复用可以降低开发成本、缩短开发周期、提高软件质量,并促进软件标准化。然而,要实现软件的有效复用,需要解决以下几个关键问题:

  1. 组件的明确定义和标准化:可复用的组件需要具有明确的定义和标准化的接口,以便在不同的系统中被重用。
  2. 组件的独立性:组件之间应该尽可能少地相互依赖,以提高其独立性和可移植性。
  3. 组件的文档化和可理解性:良好的文档和易于理解的设计是复用组件的前提。
  4. 组件的测试和验证:复用前需要对组件进行充分的测试和验证,以确保其稳定性和可靠性。

二、面向对象设计原则

面向对象设计原则为软件的可复用性提供了指导和支持。以下是一些关键的面向对象设计原则:

1. 单一责任原则(Single Responsibility Principle, SRP)

一个类应该仅有一个引起它变化的原因。这有助于保持类的简洁和可维护性,从而提高其可复用性。

java">// 示例:一个类只负责一个功能  
public class UserService {  // 负责用户注册的逻辑  public void registerUser(User user) {  // 注册逻辑...  }  // 如果有其他与用户相关的功能,应该放在其他类中  
}
2. 开放-封闭原则(Open-Closed Principle, OCP)

软件实体(类、模块、函数等)应该对扩展开放,对修改关闭。这意味着在添加新功能时,应该通过扩展现有系统来实现,而不是修改现有代码。这有助于保持系统的稳定性和可复用性。

java">// 示例:通过策略模式实现OCP  
interface PaymentStrategy {  void pay(double amount);  
}  class CreditCardPayment implements PaymentStrategy {  public void pay(double amount) {  // 信用卡支付逻辑...  }  
}  class CashPayment implements PaymentStrategy {  public void pay(double amount) {  // 现金支付逻辑...  }  
}  class PaymentProcessor {  private PaymentStrategy strategy;  public PaymentProcessor(PaymentStrategy strategy) {  this.strategy = strategy;  }  public void processPayment(double amount) {  strategy.pay(amount);  }  
}  // 客户端代码  
PaymentProcessor processor = new PaymentProcessor(new CreditCardPayment());  
processor.processPayment(100.0);
3. 里氏替换原则(Liskov Substitution Principle, LSP)

子类型必须能够替换掉它们的基类型。这要求子类在继承基类时,必须保持与基类相同的行为特性,以确保在父类出现的地方可以使用子类来替换。

java">// 父类  
class Rectangle {  protected double width;  protected double height;  public Rectangle(double width, double height) {  this.width = width;  this.height = height;  }  // 计算面积  public double area() {  return width * height;  }  
}  // 子类,尝试遵循里氏替换原则  
class Square extends Rectangle {  // 由于Square的特殊性,这里不能直接使用父类的构造器  // 因为Square的宽和高必须相等  public Square(double side) {  super(side, side); // 调用父类构造器,强制宽和高相等  }  // 尝试修改面积方法(但这样做可能违反里氏替换原则)  // @Override  // public double area() {  //     return width * width; // 直接使用width的平方,但这会破坏里氏替换原则  // }  // 为了保持里氏替换原则,我们不覆盖area方法  // 而是添加一个新的方法来计算正方形的特定属性(比如周长)  public double perimeter() {  return 4 * width; // 因为是正方形,所以周长是4倍的边长  }  
}  // 客户端代码  
public class LiskovSubstitutionDemo {  public static void main(String[] args) {  Rectangle rect = new Rectangle(4, 5);  System.out.println("Rectangle area: " + rect.area()); // 输出矩形的面积  // 尝试用Square替换Rectangle  Rectangle square = new Square(5); // 这里Square被当作Rectangle使用,符合里氏替换原则  System.out.println("Square area (as Rectangle): " + square.area()); // 输出正方形的面积,通过Rectangle接口  // 如果需要调用Square特有的方法,需要进行类型转换  if (square instanceof Square) {  Square s = (Square) square;  System.out.println("Square perimeter: " + s.perimeter()); // 输出正方形的周长  }  }  
}
4. 依赖倒置原则(Dependency Inversion Principle, DIP)

高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象。这有助于减少模块之间的耦合度,提高系统的灵活性和可复用性。

java">// 定义一个日志记录的接口  
interface Logger {  void log(String message);  
}  // 实现日志记录接口的具体类  
class FileLogger implements Logger {  @Override  public void log(String message) {  System.out.println("Logging to file: " + message);  }  
}  // 另一个实现日志记录接口的具体类  
class ConsoleLogger implements Logger {  @Override  public void log(String message) {  System.out.println("Logging to console: " + message);  }  
}  // 定义一个依赖日志记录的类,这里依赖的是日志的抽象(接口)  
class Application {  private Logger logger;  // 通过构造器注入依赖的日志实现  public Application(Logger logger) {  this.logger = logger;  }  public void execute() {  // 使用日志记录功能,但不关心具体的实现  logger.log("Application is starting...");  // 其他业务逻辑...  logger.log("Application is ending...");  }  
}  // 客户端代码  
public class DependencyInversionDemo {  public static void main(String[] args) {  // 可以在运行时根据需要选择日志记录的实现  Application appWithFileLogger = new Application(new FileLogger());  Application appWithConsoleLogger = new Application(new ConsoleLogger());  appWithFileLogger.execute();  appWithConsoleLogger.execute();  }  
}
5. 接口隔离原则(Interface Segregation Principle, ISP)

不应该强迫客户依赖于它们不使用的方法。这要求将大的接口拆分成更小的、更具体的接口,以便客户只需要知道它们感兴趣的方法。

java">// 定义一个细粒度的接口,只包含打印功能  
interface Printable {  void print();  
}  // 另一个细粒度的接口,只包含扫描功能  
interface Scannable {  void scan();  
}  // 假设我们有一个多功能设备,它同时支持打印和扫描  
class MultiFunctionDevice implements Printable, Scannable {  @Override  public void print() {  System.out.println("Printing document...");  }  @Override  public void scan() {  System.out.println("Scanning document...");  }  
}  // 一个只需要打印功能的类  
class Printer {  private Printable printable;  public Printer(Printable printable) {  this.printable = printable;  }  public void performPrintTask() {  printable.print();  }  
}  // 一个只需要扫描功能的类  
class Scanner {  private Scannable scannable;  public Scanner(Scannable scannable) {  this.scannable = scannable;  }  public void performScanTask() {  scannable.scan();  }  
}  // 客户端代码  
public class InterfaceSegregationDemo {  public static void main(String[] args) {  // 创建一个多功能设备实例  MultiFunctionDevice mfd = new MultiFunctionDevice();  // 创建一个Printer实例,只依赖打印功能  Printer printer = new Printer(mfd);  printer.performPrintTask();  // 创建一个Scanner实例,只依赖扫描功能  Scanner scanner = new Scanner(mfd);  scanner.performScanTask();  }  
}
6. 迪米特法则(Least Knowledge Principle,LKP)

迪米特法则又称为最少知道原则,是指一个软件实体应当尽可能少地与其他实体发生相互作用。

java">// 示例:使用中介者模式减少类之间的直接通信  
interface Mediator {  void send(String message, Colleague colleague);  
}  interface Colleague {  void receive(String message);  
}  class ConcreteMediator implements Mediator {  private List<Colleague> colleagues = new ArrayList<>();  public void register(Colleague colleague) {  colleagues.add(colleague);  }  @Override  public void send(String message, Colleague colleague) {  for (Colleague c : colleagues) {  if (!c.equals(colleague)) {  c.receive(message);  }  }  }  
}  class ConcreteColleague implements Colleague {  private Mediator mediator;  public ConcreteColleague(Mediator mediator) {  this.mediator = mediator;  mediator.register(this);  }  @Override  public void receive(String message) {  // 处理接收到的消息  System.out.println("Received: " + message);  }  public void send(String message) {  mediator.send(message, this);  }  
}  // 客户端代码  
Mediator mediator = new ConcreteMediator();  
Colleague c1 = new ConcreteColleague(mediator);  
Colleague c2 = new ConcreteColleague(mediator);  
c1.send("Hello, World!"); // c2 会接收到这个消息,但c1不知道c2的存在
在这个例子中,ConcreteColleague 类通过 Mediator 类来与其他 Colleague 通信,而不是直接
7. 合成/聚合复用原则(Composite/Aggregate Reuse Principle, CARP)

尽量使用合成/聚合的方式来实现复用,而不是使用继承。这有助于保持类的独立性和灵活性,从而提高其可复用性。

java">// 定义一个车辆接口  
interface Vehicle {  void move();  
}  // 定义一个具体的车辆类,使用合成/聚合来复用  
class Car implements Vehicle {  private Engine engine; // 聚合关系  public Car(Engine engine) {  this.engine = engine;  }  @Override  public void move() {  System.out.println("Car is moving...");  engine.start();  }  
}  // 定义一个引擎接口  
interface Engine {  void start();  
}  // 定义一个具体的引擎类  
class GasolineEngine implements Engine {  @Override  public void start() {  System.out.println("Gasoline engine started.");  }  
}  // 客户端代码  
public class CompositionDemo {  public static void main(String[] args) {  // 创建一个引擎实例  Engine engine = new GasolineEngine();  // 创建一个车辆实例,并注入引擎  Car car = new Car(engine);  // 调用车辆移动方法,间接调用引擎的启动方法  car.move();  // 如果需要,可以轻松地替换引擎实现  // Engine electricEngine = new ElectricEngine();  // car = new Car(electricEngine);  // car.move();  }  
}  // 假设我们有一个电动引擎类(未实现,仅作为示例)  
// class ElectricEngine implements Engine {  
//     @Override  
//     public void start() {  
//         System.out.println("Electric engine started.");  
//     }  
// }

3.3 设计模式的应用

3.3.1 工厂方法模式

  工厂方法模式(Factory Method Pattern)是一种创建型设计模式,它在创建对象时不会暴露创建逻辑给客户端,并且是通过使用一个共同的接口来指向新创建的对象。这个模式涉及到一个单一的工厂类,但它允许客户端代码决定要实例化哪一个类。工厂方法让类的实例化推迟到子类中进行。

  在Java中,工厂方法模式通常通过定义一个创建对象的接口,让子类决定实例化哪一个类。工厂方法模式让类的实例化依赖于它们所含的类信息中,并且将实例化的工作延迟到子类中进行。

下面是一个简单的Java代码示例,展示了工厂方法模式:

java">// 定义一个产品接口  
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");  }  
}  // 定义一个创建产品的抽象工厂类  
abstract class Creator {  // 工厂方法,由子类实现  abstract Product factoryMethod();  // 使用工厂方法创建产品  public void someOperation() {  Product product = factoryMethod();  product.use();  }  
}  // 具体工厂类,实现了抽象工厂类中的工厂方法  
class ConcreteCreatorA extends Creator {  @Override  Product factoryMethod() {  return new ConcreteProductA();  }  
}  class ConcreteCreatorB extends Creator {  @Override  Product factoryMethod() {  return new ConcreteProductB();  }  
}  // 客户端代码  
public class FactoryMethodPatternDemo {  public static void main(String[] args) {  Creator creatorA = new ConcreteCreatorA();  creatorA.someOperation(); // 输出: Using ConcreteProductA  Creator creatorB = new ConcreteCreatorB();  creatorB.someOperation(); // 输出: Using ConcreteProductB  }  
}

  在这个例子中,Product 是一个产品接口,ConcreteProductA 和 ConcreteProductB 是实现了这个接口的具体产品类。Creator 是一个抽象工厂类,它定义了一个工厂方法 factoryMethod(),该方法在子类中具体实现以返回不同类型的产品。ConcreteCreatorA 和 ConcreteCreatorB 是具体工厂类,它们分别实现了 factoryMethod() 方法以返回不同的产品实例。

  客户端代码通过调用具体工厂类的 someOperation() 方法来创建和使用产品,而不需要知道具体的产品类是什么。这样,客户端代码与具体的产品类解耦,提高了系统的灵活性和可扩展性。

简答工厂模式包含如下角色

  • 工厂(Factory): 简单工厂模式的核心,复制实现创建有实例的逻辑。工厂类提供静态方法,根据传入参数创建所需的产品实例。
  • 抽象产品(Produt): 工厂创建的所有实例的父类型,是负责描述所有产品的公共接口。可以是接口或抽象类。
  • 具体产品(Conrete product): 抽象产品的实现类,是工厂的创建目标,工厂创建的实例就是某个具体产品类的实例。
    客户程序NewsServiceImpl只需要知道工厂和抽象的父类产品(NewsDao接口),不需要关心具体的产品如何创建(不需要知道接口的具体实现),内部如何变化。具体产品(接口实现类)被父类型(接口)包装,与客户程序解耦合,不影响客户程序(Service接口实现类)的复用。
1. 简单工厂模式(Simple Factory Pattern)

角色:
工厂类(Factory Class): 负责创建具体产品类的实例。
抽象产品类(Abstract Product Class): 定义产品的公共接口。
具体产品类(Concrete Product Classes): 实现了抽象产品类所定义的接口。

java">// 抽象产品类  
interface Car {  void drive();  
}  // 具体产品类1  
class Audi implements Car {  @Override  public void drive() {  System.out.println("Driving Audi");  }  
}  // 具体产品类2  
class BMW implements Car {  @Override  public void drive() {  System.out.println("Driving BMW");  }  
}  // 工厂类  
class CarFactory {  public static Car getCar(String type) {  if (type.equalsIgnoreCase("audi")) {  return new Audi();  } else if (type.equalsIgnoreCase("bmw")) {  return new BMW();  }  return null;  }  
}  // 客户端代码  
public class FactoryPatternDemo {  public static void main(String[] args) {  Car audi = CarFactory.getCar("audi");  audi.drive();  Car bmw = CarFactory.getCar("bmw");  bmw.drive();  }  
}

  简单工厂模式不适合创建逻辑比较复杂的情况,复杂的产品逻辑会导致工厂方法难以维护,并且增加新的产品就需要修改工厂方法判断逻辑,这与开闭原则相违背。而工厂方法模式是对简单工厂模式的进一步抽象化,工厂方法模式的主要角色如下(对工厂进一步抽象)。

  • 抽象产品(Product): 定义了产品的规范,描述了产品的主要特性和功能(Dao接口)。
  • 抽象工厂(Abstract Factory): 提供了创建产品的接口,声明创建方法,该方法返回值为抽象产品类型,调用者通过抽象工厂接口访问具体工厂的方法来创建产品(提供创建Dao接口实现类实例的接口)。
  • 具体工厂(Concrete Factory): 实现抽象工厂中的抽象创建方法,完成某个具体产品的创建,具体工厂和具体产品之间存在对应关系(负责创建Dao接口实现类的实例)。
2. 工厂方法模式(Factory Method Pattern)

角色:
抽象工厂类(Abstract Factory Class): 声明一个用于创建对象的操作接口。
具体工厂类(Concrete Factory Classes): 实现抽象工厂类声明的接口,创建具体产品的实例。
抽象产品类(Abstract Product Class): 定义产品的接口。
具体产品类(Concrete Product Classes): 实现了抽象产品类所定义的接口。

java">// 抽象产品类  
interface Car {  void drive();  
}  // 具体产品类1  
class Audi implements Car {  @Override  public void drive() {  System.out.println("Driving Audi");  }  
}  // 具体产品类2  
class BMW implements Car {  @Override  public void drive() {  System.out.println("Driving BMW");  }  
}  // 抽象工厂类  
interface CarFactory {  Car createCar();  
}  // 具体工厂类1  
class AudiFactory implements CarFactory {  @Override  public Car createCar() {  return new Audi();  }  
}  // 具体工厂类2  
class BMWFactory implements CarFactory {  @Override  public Car createCar() {  return new BMW();  }  
}  // 客户端代码  
public class FactoryMethodPatternDemo {  public static void main(String[] args) {  CarFactory audiFactory = new AudiFactory();  Car audi = audiFactory.createCar();  audi.drive();  CarFactory bmwFactory = new BMWFactory();  Car bmw = bmwFactory.createCar();  bmw.drive();  }  
}
3.抽象工厂模式(Abstract Factory Pattern)

角色:
抽象工厂类(Abstract Factory Class): 提供一个创建一系列相关或相互依赖对象的接口。
具体工厂类(Concrete Factory Classes): 实现抽象工厂类声明的接口,创建具体产品的实例。
抽象产品类(Abstract Product Classes): 定义了一组产品的接口。
具体产品类(Concrete Product Classes): 实现了抽象产品类所定义的接口。

代码示例:
由于抽象工厂模式涉及多个产品族和多个等级结构的产品,代码示例会相对复杂,这里仅给出框架性的描述。

java">// 抽象产品A  
interface ProductA {  void operationA();  
}  // 具体产品A1  
class ConcreteProductA1 implements ProductA {  @Override  public void operationA() {  // 实现  }  
}  // 抽象产品B  
interface ProductB {  void operationB();  
}  // 具体产品B1

3.3.2 代理模式

  在生活中,我们经常听说房产中介、婚介、经纪人等社会角色,这些都是代理模式的实现体现。这种模式其实也是单一职责原则的体现,就好像一个要买房,中间会涉及很多的环节,部分流程复杂而且专业。

代理模式包含如下角色
  • 抽象主题(Subject): 通过接口或抽象类声明业务方法(NewsDao接口)。
  • 真实主题(Real Subject): 实现了抽象主题中的具体业务,是实施代理的目标对象,即代理对象所代表的真实对象,是最终要引用的对象(NewsDao接口的实现类)。
  • 代理(Proxy): 提供了与真实主题相同的接口,其内部含有对真实主题的引用,可以访问、控制或扩展真实主题的功能。
    (在接口中定义一个买房的方法,真实对张三去实现买房操作。)

  代理模式(Proxy Pattern)是一种结构型设计模式,它为其他对象提供一种代理以控制对这个对象的访问。代理模式通常用于在客户端和目标对象之间创建一个中介,以增加功能、控制访问、减少系统间耦合等。

  代理模式主要有三种类型:静态代理、动态代理(包括JDK动态代理和CGLIB动态代理,主要基于Java语言)和远程代理。下面分别给出静态代理和JDK动态代理的简单代码示例。

静态代理
  静态代理通常是在编译时就确定了代理类,代理类和被代理类都实现了相同的接口。

java">// 接口  
interface Image {  void display();  
}  // 被代理类  
class RealImage implements Image {  private String fileName;  public RealImage(String fileName) {  this.fileName = fileName;  loadFromDisk(fileName);  }  private void loadFromDisk(String fileName) {  System.out.println("Loading " + fileName);  }  @Override  public void display() {  System.out.println("Displaying " + fileName);  }  
}  // 代理类  
class ProxyImage implements Image {  private RealImage realImage;  private String fileName;  public ProxyImage(String fileName) {  this.fileName = fileName;  }  @Override  public void display() {  if (realImage == null) {  realImage = new RealImage(fileName);  }  realImage.display();  }  
}  // 客户端  
public class ProxyPatternDemo {  public static void main(String[] args) {  Image image = new ProxyImage("test.jpg");  // 图像将从磁盘加载  image.display();  }  
}

JDK动态代理
JDK动态代理是在运行时动态地创建代理类,需要被代理的对象必须实现一个或多个接口。

java">import java.lang.reflect.InvocationHandler;  
import java.lang.reflect.Method;  
import java.lang.reflect.Proxy;  // 接口  
interface Image {  void display();  
}  // 被代理类  
class RealImage implements Image {  private String fileName;  public RealImage(String fileName) {  this.fileName = fileName;  }  @Override  public void display() {  System.out.println("Displaying " + fileName);  }  
}  // 代理类的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("test.jpg");  // 创建代理对象  Image proxyImage = (Image) Proxy.newProxyInstance(  Image.class.getClassLoader(),  new Class[]{Image.class},  new ImageInvocationHandler(realImage)  );  // 调用代理对象的方法  proxyImage.display();  }  
}

  在JDK动态代理中,Proxy.newProxyInstance() 方法用于动态地创建代理对象,它需要三个参数:类加载器、接口数组(被代理类实现的接口)和InvocationHandler实例。通过实现InvocationHandler接口的invoke方法,我们可以在方法调用前后添加自定义的逻辑。


http://www.ppmy.cn/embedded/58244.html

相关文章

Prompt Engineering overview

文章目录 High level look迭代法优化iterate参考 High level look 提示的重要性往往会被低估或者高估 低估是因为正确的提示技术如果使用得当&#xff0c;可以让我们走的更远。 高估是因为基于提示的应用程序&#xff0c;需要围绕提示进行大量的工程设计才能运行良好。 提示工…

新浪API系列:支付API打造无缝支付体验,畅享便利生活(3)

在当今数字化时代&#xff0c;支付功能已经成为各类应用和平台的必备要素之一。作为开发者&#xff0c;要构建出安全、便捷的支付解决方案&#xff0c;新浪支付API是你不可或缺的利器。新浪支付API提供了全面而强大的接口和功能&#xff0c;帮助开发者轻松实现在线支付的集成和…

后门攻击检测指南--windowsLinuxweb

免责声明:本文仅做技术交流与学习... 目录 Win d o w s - 后 门 - 常 规 & 权 限 维 持& 内 存 马 Lin u x - 后 门 - 常 规 & 权 限 维 持 & R o o t kit& 内 存 马 关于Rootkit的检测&#xff1a; web层面-后门--内存马 Win d o w s - 后 门 - 常 规…

VBA实现Excel的数据透视表

前言 本节会介绍通过VBA的PivotCaches.Create方法实现Excel创建新的数据透视表、修改原有的数据透视表的数据源以及刷新数据透视表内容。 本节测试内容以下表信息为例 1、创建数据透视表 语法&#xff1a;PivotCaches.Create(SourceType, [SourceData], [Version]) 说明&am…

发表EI会议论文-对考研生和研究生都有好处!

EI论文对考研和保研的帮助主要体现在以下几个方面&#xff1a; 对考研的帮助 1.复试加分&#xff1a;在考研过程中&#xff0c;复试阶段是关键&#xff0c;拥有EI论文可以证明考生具备一定的科研能力&#xff0c;给考官留下深刻印象&#xff0c;有助于提高复试通过率。 2.学…

谷粒商城----通过缓存和分布式锁获取数据。

高并发下缓存失效的问题 高并发下缓存失效的问题--缓存穿透 指查询一个一定不存在的数据&#xff0c;由于缓存是不命中&#xff0c;将去查询数据库&#xff0c;但是数据库也无此记录&#xff0c;我们没有将这次查询的不写入缓存&#xff0c;这将导致这个不存在的数据每次请求…

go获取正在运行的函数并及时捕获panic

Go 语言中&#xff0c;panic 是一种运行时错误&#xff0c;它会导致当前 goroutine 立即停止执行&#xff0c;并开始逐层向上返回&#xff0c;直到被 recover 捕获或者程序崩溃。panic 通常用于异常情况&#xff0c;比如程序遇到了无法恢复的错误。 捕获 panic 的主要作用包括…

Unity3D 资源管理YooAsset原理分析与详解

引言 Unity3D 是一款广泛应用于游戏开发、虚拟现实&#xff08;VR&#xff09;、增强现实&#xff08;AR&#xff09;等领域的强大游戏开发引擎。在开发过程中&#xff0c;资源管理是一项至关重要的任务&#xff0c;它直接影响到游戏的性能和用户体验。YooAsset 是一个基于 Un…