设计模式补

适配器模式(Adapter Pattern)

适配器类继承A目标接口,把B非适配器类当适配器类的参数,用适配器类使用A目标接口的方法

是一种结构型设计模式,它的主要目的是使原本接口不兼容的类可以一起工作。适配器模式通过创建一个适配器类来包装现有类的接口,使其看起来像另一个接口,从而使得原本不兼容的类可以协同工作。

适配器模式的目的

  1. 接口转换:使一个类的接口匹配另一个类的接口,即使它们之间没有继承关系。
  2. 增加灵活性:允许系统使用不兼容接口的对象,而无需修改现有代码。
  3. 复用现有类:通过适配器模式,可以轻松地复用已有的类而不必修改它们的源代码。
  4. 隔离变化:通过引入适配器,可以在不改变现有代码的情况下添加新的功能。

适配器模式的使用场景

  1. 现有类库的复用:当你有一个已经存在的类库,但是它的接口与你的应用不兼容时,可以通过适配器模式来桥接这两个类库之间的接口差异。
  2. 第三方组件集成:在集成第三方组件时,第三方组件的API可能与你的系统不兼容,这时可以使用适配器模式来解决接口不匹配的问题。
  3. 遗留系统的改造:当你需要更新遗留系统的一部分时,可以通过适配器模式将新旧接口进行转换,以维持系统的连续性。
  4. 简化复杂的接口:有时你需要使用的类具有非常复杂的接口,而你只需要其中的一部分功能。这时可以使用适配器模式来创建一个更简洁的接口。
  5. 统一多个相似接口:如果有多个类具有相似的功能但接口不同,可以通过适配器模式为这些类提供一个统一的接口。
   // 目标接口
   class Target {
       request() {
           return "Target: The default target's behavior.";
       }
   }

   // 不兼容的适配者类
   class Adaptee {
       specificRequest() {
           return "Adaptee: The adaptee's behavior.";
       }
   }

   // 适配器类
   class Adapter extends Target {
       constructor(adaptee) {
           super();
           this.adaptee = adaptee;
       }

       request() {
           const result = super.request();
           const additionalBehavior = this.adaptee.specificRequest();
           return `${result} Adapter: Also adapts the adaptee's behavior ${additionalBehavior}`;
       }
   }

   // 使用
   const adaptee = new Adaptee();
   const adapter = new Adapter(adaptee);

   console.log(adapter.request());
   // Output: Target: The default target's behavior. Adapter: Also adapts the adaptee's behavior Adaptee: The adaptee's behavior.

单例模式

是一种常用的软件设计模式,它的目的是确保一个类只有一个实例,并提供一个全局访问点来访问这个实例。这种模式在很多情况下都非常有用,特别是在需要频繁地访问同一个对象的情况下,而且这个对象的创建成本较高或其状态需要在整个系统中保持一致时。

饿汉式单例

目的
  • 确保线程安全:饿汉式单例在类加载时就完成了实例化,因此不需要担心多线程环境下的同步问题。
  • 简单直接:由于在类加载时就已经创建好了实例,所以无需额外的同步机制来保证实例的唯一性。
用途
  • 当单例对象的创建成本较低,且需要确保在应用程序启动时就准备好该对象时。
  • 当应用程序不需要考虑延迟加载单例对象的情况。

汉式单例

目的
  • 延迟加载:懒汉式单例只有在第一次被请求时才创建实例,这样可以节省资源,特别是在单例对象创建成本较高时。
  • 按需初始化:只有在真正需要时才会创建单例对象,适用于资源密集型对象。
用途
  • 当单例对象的创建成本较高,且可能并不总是需要使用它时。
  • 当希望最小化应用程序启动时的资源占用时。

原型模式(Prototype Pattern)

是一种创建型设计模式,它使用一个现有的对象作为原型,并通过复制该原型对象来创建新的对象。这种模式的主要目的是避免创建过程中的重复计算或初始化工作,提高性能和效率。

原型模式的目的

  1. 避免创建过程中的开销:当创建一个新对象的成本很高时(例如,需要复杂的初始化或从数据库获取数据),使用原型模式可以避免这些开销。
  2. 简化对象创建:通过克隆一个已有实例,可以简化对象的创建过程,特别是当对象的创建过程涉及大量的配置或设置时。
  3. 增强灵活性:原型模式允许对象的创建在运行时进行定制,这增加了系统的灵活性。

使用场景

  1. 创建复杂的对象:当对象的创建过程很复杂时,使用原型模式可以避免每次创建新对象时都需要重新初始化的过程。

  2. 性能敏感的应用:在性能敏感的应用中,避免重复的创建和初始化过程可以显著提高性能。

  3. 对象池:在需要大量相似对象的情况下,使用原型模式可以有效地管理对象池,减少资源消耗。

  4. 配置对象:当需要根据不同的配置创建不同的对象实例时,使用原型模式可以简化配置过程。

    import java.util.Date;

    // 定义一个原型接口
    interface Prototype<T> extends Cloneable {
    T clone();
    }

    // 具体的实现类
    class Person implements Prototype<Person> {
    private String name;
    private Date birthday;

     public Person(String name, Date birthday) {
         this.name = name;
         this.birthday = birthday;
     }
    
     public String getName() {
         return name;
     }
    
     public Date getBirthday() {
         return birthday;
     }
    
     @Override
     public Person clone() {
         try {
             // 浅拷贝
             Person person = (Person) super.clone();
             // 深拷贝
             person.birthday = (Date) this.birthday.clone();
             return person;
         } catch (CloneNotSupportedException e) {
             throw new AssertionError();
         }
     }
    

    }

    // 使用示例
    public class PrototypeExample {
    public static void main(String[] args) {
    Date date = new Date();
    Person original = new Person("Alice", date);

         Person cloned = original.clone();
         
         System.out.println("Original: " + original.getName() + ", " + original.getBirthday());
         System.out.println("Cloned: " + cloned.getName() + ", " + cloned.getBirthday());
     }
    

    }

观察者模式(Observer Pattern)

是一种行为型设计模式,它定义了对象之间的一种一对多依赖关系,使得每当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并自动更新。这种模式在Java中有着广泛的应用,尤其是在事件驱动的系统和GUI应用程序中。

观察者模式的目的

  1. 解耦:观察者模式有助于解耦主体(Subject)和观察者(Observer)。主体不知道观察者的存在,观察者也不知道其他观察者的存在。
  2. 动态订阅/取消订阅:观察者可以动态地订阅或取消订阅主题的变化通知。
  3. 扩展性:当需要添加新的观察者时,不需要修改主体的代码,符合开闭原则。

使用场景

  1. 事件处理:在事件驱动的系统中,观察者模式可以用来处理各种事件,例如按钮点击事件、窗口关闭事件等。

  2. GUI应用程序:在图形用户界面中,当用户与界面交互时,观察者模式可以用来更新界面元素的状态。

  3. 数据绑定:在MVC架构中,观察者模式可以用来实现视图对模型的自动更新。

  4. 多线程应用:在多线程环境中,观察者模式可以用来处理异步事件的通知。

  5. 日志记录:当系统中的日志级别发生变化时,可以使用观察者模式来通知所有相关组件进行相应的更新。

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

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

    // 具体的主题实现
    class NewsAgency implements Subject {
    // 存储观察者的列表
    private List<Observer> observers = new ArrayList<>();
    private String news;

     // 设置新闻,并通知所有观察者
     public void setNews(String news) {
         this.news = news;
         notifyObservers(news);
     }
    
     // 注册观察者
     @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 NewsPaper implements Observer {
    private String name;

     // 构造函数
     public NewsPaper(String name) {
         this.name = name;
     }
    
     // 当有新闻更新时,更新方法会被调用
     @Override
     public void update(String message) {
         System.out.println(name + " received: " + message);
     }
    

    }

    // 使用示例
    public class ObserverExample {
    public static void main(String[] args) {
    // 创建新闻机构
    NewsAgency agency = new NewsAgency();

         // 创建报纸观察者
         NewsPaper newspaper1 = new NewsPaper("Daily News");
         NewsPaper newspaper2 = new NewsPaper("Evening Post");
    
         // 注册报纸观察者
         agency.registerObserver(newspaper1);
         agency.registerObserver(newspaper2);
    
         // 发布新闻
         agency.setNews("New event occurred.");
     }
    

    }

策略模式(Strategy Pattern)

建一个对象,可以用同一个方法,调用功能相同类不同的方法.

是一种行为型设计模式,它定义了一系列算法,并将每一个算法封装起来,使它们可以互相替换。策略模式让算法的变化独立于使用算法的客户。

策略模式的目的

  1. 封装算法:将算法封装成独立的类,使得算法的实现可以独立于使用它的客户。
  2. 算法互换:可以在运行时根据需要选择不同的算法来解决问题。
  3. 易于扩展:当需要添加新的算法时,只需要添加新的策略类即可,不需要修改原有的代码。
  4. 解耦:策略模式通过定义一系列的策略类来解耦算法与使用算法的客户。

使用场景

  1. 算法的多种实现:当一个系统中存在多种算法实现,且这些算法可以互相替换时,可以使用策略模式。

  2. 条件判断的替代 :如果在一个系统中频繁地使用条件语句(如 if-elseswitch-case)来选择不同的行为,则可以使用策略模式来简化这些条件判断。

  3. 行为参数化:当需要将行为参数化时,可以使用策略模式来实现。

    // 策略接口
    interface SortingStrategy {
    void sort(int[] numbers);
    }

    // 具体策略实现 - 冒泡排序
    class BubbleSortStrategy implements SortingStrategy {
    @Override
    public void sort(int[] numbers) {
    bubbleSort(numbers);
    }

     // 冒泡排序算法
     private void bubbleSort(int[] numbers) {
         int n = numbers.length;
         boolean swapped;
         for (int i = 0; i < n - 1; i++) {
             swapped = false;
             for (int j = 0; j < n - 1 - i; j++) {
                 if (numbers[j] > numbers[j + 1]) {
                     int temp = numbers[j];
                     numbers[j] = numbers[j + 1];
                     numbers[j + 1] = temp;
                     swapped = true;
                 }
             }
             if (!swapped) break;
         }
     }
    

    }

    // 具体策略实现 - 快速排序
    class QuickSortStrategy implements SortingStrategy {
    @Override
    public void sort(int[] numbers) {
    quickSort(numbers, 0, numbers.length - 1);
    }

     // 快速排序算法
     private void quickSort(int[] arr, int low, int high) {
         if (low < high) {
             int pi = partition(arr, low, high);
             quickSort(arr, low, pi - 1);
             quickSort(arr, pi + 1, high);
         }
     }
    
     // 分区算法
     private int partition(int[] arr, int low, int high) {
         int pivot = arr[high];
         int i = (low - 1);
         for (int j = low; j < high; j++) {
             if (arr[j] < pivot) {
                 i++;
                 int temp = arr[i];
                 arr[i] = arr[j];
                 arr[j] = temp;
             }
         }
         int temp = arr[i + 1];
         arr[i + 1] = arr[high];
         arr[high] = temp;
         return i + 1;
     }
    

    }

    // 上下文类
    class SortContext {
    private SortingStrategy strategy;

     // 构造函数
     public SortContext(SortingStrategy strategy) {
         this.strategy = strategy;
     }
    
     // 设置策略
     public void setStrategy(SortingStrategy strategy) {
         this.strategy = strategy;
     }
    
     // 排序方法
     public void sort(int[] numbers) {
         strategy.sort(numbers);
     }
    

    }

    // 使用示例
    public class StrategyExample {
    public static void main(String[] args) {
    int[] numbers = {5, 3, 2, 4, 1};

         // 创建上下文对象并设置初始策略为冒泡排序
         SortContext context = new SortContext(new BubbleSortStrategy());
         
         // 排序并打印结果
         context.sort(numbers);
         printArray(numbers);
    
         // 更改策略为快速排序
         context.setStrategy(new QuickSortStrategy());
         
         // 再次排序并打印结果
         context.sort(numbers);
         printArray(numbers);
     }
    
     // 打印数组
     private static void printArray(int[] numbers) {
         for (int number : numbers) {
             System.out.print(number + " ");
         }
         System.out.println();
     }
    

    }

责任链模式(Chain of Responsibility Pattern)

是一种行为型设计模式,它允许请求沿着处理者链传递,直到有一个处理者处理它为止。每个处理者都包含对下一个处理者的引用,因此可以形成一条链。

责任链模式的目的

  1. 解耦:通过将请求的发送者和接收者解耦,使得发送者不需要知道请求最终由谁处理。
  2. 动态分配责任:可以在运行时动态地添加、删除或重新排列处理者,从而改变请求的处理顺序。
  3. 简化对象:每个处理者对象只负责自己的职责,不关心后续处理者的行为,这使得对象更加单一职责化。

使用场景

  1. 异常处理:在异常处理中,可以使用责任链模式来处理不同类型的异常,每个处理者负责处理特定类型的异常。
  2. 权限验证:在权限管理系统中,可以使用责任链模式来处理不同级别的权限验证。
  3. 日志记录:在日志记录系统中,可以使用责任链模式来处理不同级别的日志信息。
  4. HTTP请求处理:在Web框架中,可以使用责任链模式来处理HTTP请求,例如过滤器(Filter)模式就是责任链模式的一个应用场景。
  5. GUI事件处理:在GUI应用程序中,可以使用责任链模式来处理鼠标点击、键盘输入等事件。

通过这个示例,我们可以清楚地看到责任链模式如何应用于Web框架中的过滤器模式。每个过滤器都有机会处理请求,并可以决定是否继续传递给下一个过滤器。这种模式使得我们可以轻松地添加新的过滤器,而不需要修改现有的代码结构。

// 处理者接口
interface Filter {
    void doFilter(Request request, Response response, FilterChain filterChain);
}

// 请求对象
class Request {
    private String url;

    public Request(String url) {
        this.url = url;
    }

    public String getUrl() {
        return url;
    }
}

// 响应对象
class Response {
    // 可以添加响应相关的属性和方法
}

// 过滤器链
class FilterChain {
    private List<Filter> filters = new ArrayList<>();

    public void addFilter(Filter filter) {
        filters.add(filter);
    }

    public void doFilter(Request request, Response response) {
        for (Filter filter : filters) {
            filter.doFilter(request, response, this);
        }
    }
}

// 具体过滤器实现
class AuthenticationFilter implements Filter {
    @Override
    public void doFilter(Request request, Response response, FilterChain filterChain) {
        System.out.println("AuthenticationFilter: Checking authentication for request to " + request.getUrl());
        filterChain.doFilter(request, response); // 传递给下一个过滤器
    }
}

class LoggingFilter implements Filter {
    @Override
    public void doFilter(Request request, Response response, FilterChain filterChain) {
        System.out.println("LoggingFilter: Logging request to " + request.getUrl());
        filterChain.doFilter(request, response); // 传递给下一个过滤器
    }
}

class PerformanceFilter implements Filter {
    @Override
    public void doFilter(Request request, Response response, FilterChain filterChain) {
        long startTime = System.currentTimeMillis();
        filterChain.doFilter(request, response); // 传递给下一个过滤器
        long endTime = System.currentTimeMillis();
        System.out.println("PerformanceFilter: Request took " + (endTime - startTime) + " ms");
    }
}

// 客户端类
public class WebFrameworkExample {
    public static void main(String[] args) {
        // 创建过滤器链
        FilterChain filterChain = new FilterChain();

        // 添加过滤器
        filterChain.addFilter(new AuthenticationFilter());
        filterChain.addFilter(new LoggingFilter());
        filterChain.addFilter(new PerformanceFilter());

        // 创建请求
        Request request = new Request("/some-resource");

        // 创建响应
        Response response = new Response();

        // 发送请求
        filterChain.doFilter(request, response);
    }
}

下面是关于这段代码的调用流程和内部关系的详细说明,包括各个组件之间的关系是包含还是并列。

处理者接口 `Filter`

  • **定义**:定义了一个 `doFilter` 方法,用于处理请求,并传递给下一个过滤器。

  • **关系**:这是一个接口,被具体的过滤器实现类继承。

请求对象 `Request`

  • **定义**:代表了一个HTTP请求,包含了URL等信息。

  • **关系**:请求对象是过滤器链中的传递对象,每个过滤器都需要访问它。

响应对象 `Response`

  • **定义**:代表了一个HTTP响应,可以包含响应相关的属性和方法。

  • **关系**:响应对象是过滤器链中的传递对象,每个过滤器都可以修改它。

过滤器链 `FilterChain`

  • **定义**:包含了过滤器列表,并提供了方法来添加过滤器和遍历过滤器列表来处理请求。

  • **关系**:过滤器链包含多个过滤器对象,这些过滤器对象按顺序排列,形成了一个链表结构。

具体过滤器实现

  • **AuthenticationFilter**:检查用户的认证状态。

  • **LoggingFilter**:记录请求的日志信息。

  • **PerformanceFilter**:记录请求的处理时间。

  • **关系**:这些过滤器实现类实现了 `Filter` 接口,并在 `doFilter` 方法中处理请求,并可以决定是否继续传递给下一个过滤器。

客户端类 `WebFrameworkExample`

  • **定义**:创建过滤器链、添加过滤器、创建请求和响应,并发送请求。

  • **关系**:客户端类是外部调用者,它创建过滤器链和请求/响应对象,并触发过滤器链的处理过程。

调用流程

  1. **创建过滤器链**:在 `WebFrameworkExample` 中,创建了一个 `FilterChain` 对象。

  2. **添加过滤器**:通过调用 `addFilter` 方法,向过滤器链中添加了三个过滤器:`AuthenticationFilter`、`LoggingFilter` 和 `PerformanceFilter`。

  3. **创建请求和响应**:创建了一个 `Request` 对象和一个 `Response` 对象。

  4. **发送请求**:通过调用 `filterChain.doFilter(request, response)` 来发送请求。

  5. **过滤器处理**:

  • 首先,`AuthenticationFilter` 被调用,它会输出认证信息,并调用 `filterChain.doFilter(request, response)` 来传递给下一个过滤器。

  • 接下来,`LoggingFilter` 被调用,它会输出日志信息,并同样调用 `filterChain.doFilter(request, response)` 来传递给下一个过滤器。

  • 最后,`PerformanceFilter` 被调用,它会记录处理时间,并结束整个过滤器链的处理过程。

内部关系

  • **包含关系**:`FilterChain` 包含了多个过滤器对象。

  • **并列关系**:各个过滤器实现类之间是并列关系,它们都实现了 `Filter` 接口,但各自负责不同的任务。

总结

在这个示例中,`FilterChain` 是一个中心组件,它包含了多个过滤器对象,并负责将请求传递给这些过滤器。每个过滤器都实现了相同的接口,并可以决定是否继续传递请求给下一个过滤器。这种结构使得我们可以轻松地添加新的过滤器,而不影响现有的过滤器链。

相关推荐
工业3D_大熊几秒前
【虚拟仿真】CEETRON SDK在船舶流体与结构仿真中的应用解读
java·python·科技·信息可视化·c#·制造·虚拟现实
喵叔哟3 分钟前
重构代码之用委托替代继承
开发语言·重构
lzb_kkk9 分钟前
【JavaEE】JUC的常见类
java·开发语言·java-ee
SEEONTIME9 分钟前
python-24-一篇文章彻底掌握Python HTTP库Requests
开发语言·python·http·http库requests
起名字真南28 分钟前
【OJ题解】C++实现字符串大数相乘:无BigInteger库的字符串乘积解决方案
开发语言·c++·leetcode
爬山算法33 分钟前
Maven(28)如何使用Maven进行依赖解析?
java·maven
tyler_download39 分钟前
golang 实现比特币内核:实现基于椭圆曲线的数字签名和验证
开发语言·数据库·golang
小小小~40 分钟前
qt5将程序打包并使用
开发语言·qt
hlsd#40 分钟前
go mod 依赖管理
开发语言·后端·golang
小春学渗透41 分钟前
Day107:代码审计-PHP模型开发篇&MVC层&RCE执行&文件对比法&1day分析&0day验证
开发语言·安全·web安全·php·mvc