Java常用设计模式(二)

2,563 阅读21分钟

Java设计模式是指针对软件工程师在设计Java程序时可以使用的一组常见的、经过验证的解决方案。它们是在多年开发经验的基础上形成的,可以用来解决在软件开发过程中经常遇到的问题。

Java常用设计模式(一) - 掘金 (juejin.cn)

抽象工厂模式(Abstract Factory Pattern)

  抽象工厂模式是一种创建型设计模式,它提供了一种创建相关或依赖对象的接口,而无需指定它们的具体类。它的基本思想是:定义一个用于创建一系列相关或相互依赖对象的接口,而不需要指定他们的具体类。

  在Java中,抽象工厂模式通常包括以下几个角色:

  • AbstractFactory:抽象工厂,声明了创建产品对象的方法。
  • ConcreteFactory:具体工厂,实现了创建产品对象的方法。
  • AbstractProduct:抽象产品,声明了产品对象的共性接口。
  • Product:具体产品,实现了抽象产品中的抽象方法,构成产品族。
  • Client:客户端,通过调用工厂类的方法创建产品对象。

  抽象工厂和工厂模式都是创建对象的设计模式,它们的主要区别什么呢?

  • 目的不同:工厂模式用于创建一类产品对象的实例,而抽象工厂模式用于创建一组相关的产品对象实例。
  • 实现方式不同:工厂模式中只有一个工厂类,该类负责创建所有的产品对象;而抽象工厂模式中有多个工厂类,每个工厂类负责创建一组相关的产品对象。
  • 范围不同:工厂模式通常用于创建单个对象,而抽象工厂模式通常用于创建一组相关的对象

使用场景

  • 当需要创建一组相关的产品对象,这些产品对象之间有共同的约束条件,需要一起使用时,可以使用抽象工厂模式。

  • 当系统需要独立于产品的创建,组装和表示时,可以使用抽象工厂模式

  • 当系统需要支持多个不同的产品族,且不希望依赖于具体产品类时,可以使用抽象工厂模式。

  • 当系统需要在运行时切换不同的产品族时,可以使用抽象工厂模式。

  • 当需要遵循“开闭原则”,即系统需要扩展新的产品族时,不需要修改已有代码,可以使用抽象工厂模式。

代码实现

  假设有一个在线商店需要提供两种支付方式:信用卡支付和网银支付,每种支付方式又包含两种具体的实现:Visa 信用卡支付和 MasterCard 信用卡支付,以及支付宝网银支付和微信网银支付。

  首先,定义支付方式的接口:

// 信用卡支付接口
public interface CreditCardPayment {
    void pay(double amount);
}

// 网银支付接口
public interface OnlineBankingPayment {
    void pay(double amount);
}

  接着,定义具体的实现:

// Visa 信用卡支付
public class VisaCreditCardPayment implements CreditCardPayment {
    @Override
    public void pay(double amount) {
        System.out.println("Visa credit card payment: $" + amount);
    }
}

// MasterCard 信用卡支付
public class MasterCardCreditCardPayment implements CreditCardPayment {
    @Override
    public void pay(double amount) {
        System.out.println("MasterCard credit card payment: $" + amount);
    }
}

// 支付宝网银支付
public class AlipayOnlineBankingPayment implements OnlineBankingPayment {
    @Override
    public void pay(double amount) {
        System.out.println("Alipay online banking payment: ¥" + amount);
    }
}

// 微信网银支付
public class WeChatOnlineBankingPayment implements OnlineBankingPayment {
    @Override
    public void pay(double amount) {
        System.out.println("WeChat online banking payment: ¥" + amount);
    }
}

  然后,定义抽象工厂类

// 抽象支付工厂
public abstract class PaymentFactory {
    public abstract CreditCardPayment createCreditCardPayment();
    public abstract OnlineBankingPayment createOnlineBankingPayment();
}

  定义具体的支付工厂类:

// Visa 信用卡支付工厂
public class VisaPaymentFactory extends PaymentFactory {
    @Override
    public CreditCardPayment createCreditCardPayment() {
        return new VisaCreditCardPayment();
    }

    @Override
    public OnlineBankingPayment createOnlineBankingPayment() {
        return new AlipayOnlineBankingPayment();
    }
}

// MasterCard 信用卡支付工厂
public class MasterCardPaymentFactory extends PaymentFactory {
    @Override
    public CreditCardPayment createCreditCardPayment() {
        return new MasterCardCreditCardPayment();
    }

    @Override
    public OnlineBankingPayment createOnlineBankingPayment() {
        return new WeChatOnlineBankingPayment();
    }
}

  最后,使用抽象工厂来创建支付对象

public class Client {
    public static void main(String[] args) {
        // 选择 Visa 信用卡支付工厂
        PaymentFactory paymentFactory = new VisaPaymentFactory();

        // 创建 Visa 信用卡支付对象
        CreditCardPayment creditCardPayment = paymentFactory.createCreditCardPayment();
        creditCardPayment.pay(100);

        // 创建支付宝网银支付对象
        OnlineBankingPayment onlineBankingPayment = paymentFactory.createOnlineBankingPayment();
        onlineBankingPayment.pay(200);

        // 选择 MasterCard 信用卡支付工厂
        PaymentFactory paymentFactory2 = new MasterCardPaymentFactory();

        // 创建  MasterCard 信用卡支付对象
        CreditCardPayment creditCardPayment2 = paymentFactory2.createCreditCardPayment();
        creditCardPayment2.pay(100);

        // 创建微信网银支付对象
        OnlineBankingPayment onlineBankingPayment2 = paymentFactory2.createOnlineBankingPayment();
        onlineBankingPayment2.pay(200);
        
}

使用小结

  Java 抽象工厂模式在很多框架和应用程序中都有广泛的应用。以下是一些具体的应用:

  1. Java 数据库连接框架 JDBC 中,使用抽象工厂模式来创建连接对象,例如 Connection、Statement 等。

  2. Java 中的 XML 处理器 DOM 和 SAX,也使用抽象工厂模式来创建解析器和生成器对象。

  3. Java 中的 Java Cryptography Architecture (JCA) 也使用抽象工厂模式,用于创建加密算法和密钥生成器对象。

  总之,Java 抽象工厂模式可以在许多应用程序和框架中找到,它可以帮助您更好地组织和管理代码,提高代码的可扩展性和灵活性。

状态模式(State Pattern)

  Java 状态模式是一种行为设计模式,它允许对象在内部状态改变时改变它的行为。状态模式通过将状态封装成一个对象来实现这一点,从而使得一个对象的行为取决于它的状态对象,而不是取决于对象本身。

使用场景

  • 当一个对象的行为取决于它的状态,并且该对象的状态可能在运行时发生改变时,可以使用状态模式。

  • 当一个对象需要根据不同的状态采取不同的行动时,可以使用状态模式。

  • 当一个对象的代码中包含大量与状态相关的条件语句时,可以使用状态模式来简化代码。

代码实现

  假设有一个订单对象,订单状态包括 "新建"、"处理中" 和 "完成" 三种状态,订单状态会随着订单处理的不同而改变。

  首先,我们需要定义订单状态的接口:

public interface OrderState {
    void processOrder(Order order);
}

  然后,我们定义订单状态的具体实现类,分别对应三种不同的状态

public class NewOrder implements OrderState {
    public void processOrder(Order order) {
        // 处理新建状态下的订单
        System.out.println("Processing new order.");
        order.setState(new ProcessingOrder());
    }
}

public class ProcessingOrder implements OrderState {
    public void processOrder(Order order) {
        // 处理处理中状态下的订单
        System.out.println("Processing order in progress.");
        order.setState(new CompletedOrder());
    }
}

public class CompletedOrder implements OrderState {
    public void processOrder(Order order) {
        // 处理完成状态下的订单
        System.out.println("Processing completed order.");
    }
}

  最后,我们定义订单对象,并在订单对象中实现状态的切换:

public class Order {
    private OrderState state;

    public Order() {
        state = new NewOrder();
    }

    public void setState(OrderState state) {
        this.state = state;
    }

    public void processOrder() {
        state.processOrder(this);
    }
}

  使用上面定义的订单对象和订单状态对象来处理订单了:

Order order = new Order();
order.processOrder();    // Output: Processing new order.
order.processOrder();    // Output: Processing order in progress.
order.processOrder();    // Output: Processing completed order.

  使用了状态模式来实现订单对象的状态转换,可以动态地改变订单对象的状态,并且无需修改订单对象本身的代码。使代码更加灵活和易于维护。

使用小结

  状态模式在 Java 中的应用非常广泛,比如,线程池也是一个常见的状态机。通过使用状态模式,我们可以轻松地管理线程池的状态,并实现线程池状态的动态变更。

策略模式(Strategy Pattern)

  Java 策略模式是一种行为型设计模式,它定义了一系列的算法,将每个算法都封装起来,并使它们可以互相替换,从而使算法的变化不会影响到使用算法的客户端。这种模式可以使算法的变化更加灵活和可控,同时也可以提高代码的可读性和可维护性。

使用场景

  • 当一个对象具有多种行为或算法,并且需要在运行时动态选择其中一种时,策略模式可以派上用场。

  • 当需要对同一种行为或算法进行多种实现时,我们可以使用策略模式。

  • 当需要减少大量的 if/else 语句时,我们可以使用策略模式来优化代码。

代码实现

  假设有一个电商网站,它需要根据不同的促销策略来计算订单的价格。促销策略包括打折、满减、直降等等。

  首先,我们定义一个促销策略接口,其中包含一个计算订单价格的方法

public interface PromotionStrategy {
    double calculatePrice(double price);
}

  然后,我们实现具体的促销策略,例如打折、满减和直降策略:

public class DiscountPromotionStrategy implements PromotionStrategy {
    private double discount;

    public DiscountPromotionStrategy(double discount) {
        this.discount = discount;
    }

    public double calculatePrice(double price) {
        return price * (1 - discount);
    }
}

public class FullReductionPromotionStrategy implements PromotionStrategy {
    private double threshold;
    private double reduction;

    public FullReductionPromotionStrategy(double threshold, double reduction) {
        this.threshold = threshold;
        this.reduction = reduction;
    }

    public double calculatePrice(double price) {
        return price >= threshold ? price - reduction : price;
    }
}

public class DirectReductionPromotionStrategy implements PromotionStrategy {
    private double reduction;

    public DirectReductionPromotionStrategy(double reduction) {
        this.reduction = reduction;
    }

    public double calculatePrice(double price) {
        return price - reduction;
    }
}

  最后,我们定义一个订单类,其中包含一个 PromotionStrategy 对象和一个 calculatePrice 方法:

public class Order {
    private double price;
    private PromotionStrategy promotionStrategy;

    public Order(double price, PromotionStrategy promotionStrategy) {
        this.price = price;
        this.promotionStrategy = promotionStrategy;
    }

    public double calculatePrice() {
        return promotionStrategy.calculatePrice(price);
    }
}

  创建一个订单,并指定不同的促销策略来计算订单价格:

Order order = new Order(100, new DiscountPromotionStrategy(0.1));
double price = order.calculatePrice(); // 90

order = new Order(200, new FullReductionPromotionStrategy(150, 50));
price = order.calculatePrice(); // 150

order = new Order(300, new DirectReductionPromotionStrategy(50));
price = order.calculatePrice(); // 250

使用小结

  策略模式是 Java 中经常使用的一种设计模式,它可以在很多场景中使用,比如:

  1. 可以使用策略模式定义多种日志记录方式(例如控制台输出、文件输出、网络传输等)并动态选择使用哪种方式进行日志记录。

  2. 可以使用策略模式定义多种支付方式(例如微信支付、支付宝支付、银行卡支付等)并让用户动态选择使用哪种支付方式进行支付。

  3. 可以使用策略模式定义多种加密算法(例如对称加密、非对称加密、哈希算法等)并让用户动态选择使用哪种加密算法进行数据加密。

  通过使用策略模式,我们可以更灵活地实现不同的功能需求,并让用户根据实际情况选择最合适的策略进行操作

模板方法模式(Template Method Pattern)

  Java 模板方法模式是一种行为型设计模式,它定义了一个算法的骨架,并将一些步骤延迟到子类中实现。模板方法模式使得子类可以在不改变算法结构的情况下重定义算法中的某些步骤。

使用场景

  • 算法骨架固定:如果一个算法的基本结构已经固定,但具体的实现步骤可能因为不同的场景而不同,这个时候可以使用模板方法模式。

  • 实现代码复用:如果有多个类的某些方法结构相似,但是实现细节不同,这个时候可以将这些相同的结构抽象到父类中,由子类来实现不同的细节。

  • 简化代码实现:模板方法模式可以将复杂的代码实现分离成几个简单的步骤,从而降低代码实现的难度和复杂度。

  • 框架和库的设计:模板方法模式是设计框架和库的重要方式之一,它可以提供统一的接口和标准的实现流程,方便用户进行扩展和定制

代码实现

  AbstractClass 是一个抽象类,它定义了算法的骨架,其中 templateMethod() 是模板方法,它定义了算法的流程,由一些抽象方法 primitiveOperation1()primitiveOperation2() 组成。

  ConcreteClassAbstractClass 的具体子类,它实现了抽象方法,定义了具体的算法细节。在客户端使用时,创建 ConcreteClass ,然后调用其 templateMethod() 方法,即可完成算法的执行。

// 抽象类,定义算法骨架
public abstract class AbstractClass {
    
    // 模板方法,定义算法流程
    public final void templateMethod() {
        primitiveOperation1();
        primitiveOperation2();
    }

    // 抽象方法1,由子类实现
    public abstract void primitiveOperation1();

    // 抽象方法2,由子类实现
    public abstract void primitiveOperation2();
}

// 具体子类,实现具体的算法细节
public class ConcreteClass extends AbstractClass {
    
    @Override
    public void primitiveOperation1() {
        System.out.println("ConcreteClass.primitiveOperation1()");
    }

    @Override
    public void primitiveOperation2() {
        System.out.println("ConcreteClass.primitiveOperation2()");
    }
}

// 客户端使用
public class Client {
    
    public static void main(String[] args) {
        AbstractClass abstractClass = new ConcreteClass();
        abstractClass.templateMethod();
    }
}

  需要注意的是,在模板方法模式中,模板方法通常被声明为 final,以防止子类对其进行重写。同时,由于模板方法是一个抽象方法,因此在实现时需要注意不同抽象方法的实现顺序,以确保算法的正确性。

使用小结

  很多框架类的设计都采用了模板方法模式,例如 Spring 中的 JdbcTemplate,其中定义了一套执行 SQL 的流程,并由子类实现具体的 SQL 语句

命令模式(Command Pattern)

  Java命令模式(Command Pattern)是一种行为型设计模式,它将请求封装成一个对象,从而使您可以将不同的请求参数化,将它们放入队列中,或者将请求记录在日志中,以及支持可撤销的操作。这种模式的主要组成部分包括命令接口、具体命令、调用者和接收者。

使用场景

  • 当需要将请求发送者和接收者解耦时,可以使用命令模式,因为命令对象充当请求发送者和接收者之间的媒介。

  • 当需要支持命令的撤销(undo)操作时,可以使用命令模式,因为每个命令对象都可以保存执行所需的状态。

  • 当需要在不同的时间指定、排队或记录请求时,可以使用命令模式,因为命令对象可以像其他对象一样传递、存储和操作。

  • 当需要实现日志记录功能时,可以使用命令模式,因为命令对象可以保存操作的历史记录,以便将来需要时进行检查和恢复。

  • 当需要将一组简单的操作组合成更复杂的操作时,可以使用命令模式,因为您可以将多个命令组合成一个复合命令。

代码实现

  假设有一个电视机,它具有开机、关机和切换频道等操作

  定义命令接口和具体命令类

public interface Command {
    void execute();
}

public class TvOnCommand implements Command {
    private TV tv;
    
    public TvOnCommand(TV tv) {
        this.tv = tv;
    }
    
    public void execute() {
        tv.on();
    }
}

public class TvOffCommand implements Command {
    private TV tv;
    
    public TvOffCommand(TV tv) {
        this.tv = tv;
    }
    
    public void execute() {
        tv.off();
    }
}

public class TvChangeChannelCommand implements Command {
    private TV tv;
    private int channel;
    
    public TvChangeChannelCommand(TV tv, int channel) {
        this.tv = tv;
        this.channel = channel;
    }
    
    public void execute() {
        tv.changeChannel(channel);
    }
}

  定义接收者类

public class TV {
    public void on() {
        System.out.println("TV is on");
    }
    
    public void off() {
        System.out.println("TV is off");
    }
    
    public void changeChannel(int channel) {
        System.out.println("TV channel is changed to " + channel);
    }
}

  定义调用者类

public class RemoteController {
    private Command onCommand;
    private Command offCommand;
    private Command changeChannelCommand;
    
    public RemoteController(Command onCommand, Command offCommand, Command changeChannelCommand) {
        this.onCommand = onCommand;
        this.offCommand = offCommand;
        this.changeChannelCommand = changeChannelCommand;
    }
    
    public void turnOn() {
        onCommand.execute();
    }
    
    public void turnOff() {
        offCommand.execute();
    }
    
    public void changeChannel() {
        changeChannelCommand.execute();
    }
}

  使用命令模式

TV tv = new TV();
Command onCommand = new TvOnCommand(tv);
Command offCommand = new TvOffCommand(tv);
Command changeChannelCommand = new TvChangeChannelCommand(tv, 2);

RemoteController remoteController = new RemoteController(onCommand, offCommand, changeChannelCommand);
remoteController.turnOn(); // 输出 "TV is on"
remoteController.turnOff(); // 输出 "TV is off"
Command changeChannelCommand2 = new TvChangeChannelCommand(tv, 5);
RemoteController remoteController2 = new RemoteController(onCommand, offCommand, changeChannelCommand2);
remoteController2.changeChannel(); // 输出 "TV channel is changed to 5"

  上述代码中,RemoteController 充当了调用者角色,TvOnCommand、TvOffCommandTvChangeChannelCommand 充当了具体命令角色,而 TV 充当了接收者角色。

  当 RemoteController 调用某个命令的 execute() 方法时,具体命令将通过接收者TV来执行相应的操作。通过这种方式,调用者和接收者之间的耦合得以解除。

使用小结

  • 可以使用命令模式来实现撤销和恢复操作。例如,在编辑器中,可以使用命令模式来实现对文本的撤销和恢复操作。

  • 可以使用命令模式来实现日志记录。例如,在Web应用中,可以将每个请求封装成一个命令对象,并将命令对象记录到日志文件中。

  • 可以使用命令模式来实现消息队列。例如,在JMS(Java消息服务)中,可以将每个消息封装成一个命令对象,并将命令对象加入到消息队列中。

原型模式(Prototype Pattern)

  Java原型模式(Prototype Pattern)是一种创建型设计模式,其目的是通过复制现有对象来创建新的对象。

使用场景

  • 当对象创建的过程比较耗时或者比较复杂,例如需要进行复杂的计算或者涉及到网络请求等操作,可以使用原型模式来避免重复的初始化过程。

  • 当需要创建的对象需要和其他对象进行协同工作时,例如需要创建一个包含多个对象的组合对象,可以使用原型模式来复制一个已有的组合对象,然后进行修改来创建新的组合对象。

  • 当需要动态地增加或者删除一些对象时,可以使用原型模式来复制一个已有的对象,然后进行修改来创建新的对象。

  • 当需要保护对象的复杂状态时,例如当一个对象的创建需要大量的数据初始化时,可以使用原型模式来保护这些数据,避免因为对象的复制而产生意外的副作用。

代码实现

// 定义一个原型接口
interface Prototype {
    public Prototype clone();
}

// 具体的原型类
class ConcretePrototype implements Prototype {
    public Prototype clone() {
        return new ConcretePrototype();
    }
}

// 客户端代码
class Client {
    public static void main(String[] args) {
        Prototype prototype = new ConcretePrototype();
        Prototype clone = prototype.clone();
    }
}

使用小结

  1. Java中的Object类实现了Cloneable接口,这就意味着Java中的任何对象都可以实现原型模式。通过实现Cloneable接口,并重写Object类中的clone()方法,可以实现原型模式。例如 ArrayList、HashMap 等集合类都实现了Cloneable 接口,可以通过复制现有对象来创建新的对象。

  2. Java中的线程池也是使用了原型模式,线程池中的每个线程都是从原型线程中复制而来,而不是每次创建新的线程。

建造者模式(Builder Pattern)

  Java建造者模式(Builder Pattern)是一种创建型设计模式,它通过将一个复杂的对象的创建过程分解成多个简单的步骤,并将这些步骤封装到一个Builder对象中,从而可以灵活地创建不同的对象

使用场景

  • 当需要创建复杂的对象,并且对象的构建过程包含多个步骤时,可以使用建造者模式

  • 当需要创建不同配置的对象时,可以使用建造者模式。

  • 当需要创建可变的对象时,可以使用建造者模式。

代码实现

public class User {
    private String name;
    private String email;
    private int age;
    
    private User(Builder builder) {
        this.name = builder.name;
        this.email = builder.email;
        this.age = builder.age;
    }
    
    public static class Builder {
        private String name;
        private String email;
        private int age;
        
        public Builder setName(String name) {
            this.name = name;
            return this;
        }
        
        public Builder setEmail(String email) {
            this.email = email;
            return this;
        }
        
        public Builder setAge(int age) {
            this.age = age;
            return this;
        }
        
        public User build() {
            return new User(this);
        }
    }
}

  建造者模式创建User对象

User user = new User.Builder()
    .setName("John")
    .setEmail("john@example.com")
    .setAge(30)
    .build();

使用小结

  1. StringBuilder 类就是一个建造者模式的典型应用。它允许使用者逐步构建一个字符串,并最终返回构建好的字符串。

  2. Guava 库是一个常用的Java库,其中的 ImmutableList.Builder 类也是一个典型的建造者模式应用。它允许使用者逐步构建一个不可变的列表,并最终返回构建好的列表。

  总之,建造者模式在Java中的应用非常广泛,特别是在构建复杂对象时,它可以使构建过程更加灵活、可扩展和可维护。

桥接模式(Bridge Pattern)

  Java桥接模式(Bridge Pattern)是一种结构型设计模式,它将一个对象的抽象部分与它的实现部分分离,使它们可以独立地变化。桥接模式的目的是将抽象与实现解耦,从而实现系统的灵活性和可扩展性。

使用场景

  • 当一个类存在多个实现版本时,可以使用桥接模式将其分离开来,从而使得这些实现版本可以独立地变化。

  • 当需要将一个抽象部分与它的实现部分分离开来,以便它们可以独立地进行修改和扩展时,可以使用桥接模式。

  • 当需要在抽象类中定义抽象方法,以便在不同的实现类中实现具体的行为时,可以使用桥接模式。

  • 当需要在运行时动态地改变一个对象的实现时,可以使用桥接模式。

  • 当需要将一个大类拆分成多个独立的层级时,可以使用桥接模式

代码实现

  假设有一个图形绘制应用程序,它支持绘制不同颜色的图形。图形可以是矩形、圆形等等。我们可以使用桥接模式来将颜色和图形分离开来。

  首先,我们定义一个颜色接口 Color 和它的两个实现类 Red 和 Blue

public interface Color {
    public void applyColor();
}

public class Red implements Color {
    @Override
    public void applyColor() {
        System.out.println("Applying red color");
    }
}

public class Blue implements Color {
    @Override
    public void applyColor() {
        System.out.println("Applying blue color");
    }
}

  然后,我们定义一个抽象的图形类 Shape,它包含一个颜色接口的引用

public abstract class Shape {
    protected Color color;
    
    public Shape(Color color) {
        this.color = color;
    }
    
    public abstract void draw();
}

  接下来,定义两个实现类 RectangleCircle,它们继承自 Shape

public class Rectangle extends Shape {
    public Rectangle(Color color) {
        super(color);
    }
    
    @Override
    public void draw() {
        System.out.print("Drawing rectangle. ");
        color.applyColor();
    }
}

public class Circle extends Shape {
    public Circle(Color color) {
        super(color);
    }
    
    @Override
    public void draw() {
        System.out.print("Drawing circle. ");
        color.applyColor();
    }
}

  现在,我们可以使用这些类来绘制不同颜色的图形

Color red = new Red();
Color blue = new Blue();

Shape rectangle = new Rectangle(red);
rectangle.draw();//Drawing rectangle. Applying red color 

Shape circle = new Circle(blue);
circle.draw();//Drawing circle. Applying blue color

  Shape 类充当桥接模式中的抽象部分,Color 接口充当实现部分。使用桥接模式,我们可以轻松地添加新的图形和颜色类,而不会影响现有的代码。

使用小结

  一个经典的桥接模式例子是 JDBC API。在 JDBC API 中,DriverManager 充当桥接模式中的抽象部分,它负责管理多个 JDBC 驱动程序的实现部分。开发人员可以通过 DriverManager 类来连接数据库,而无需了解底层数据库驱动程序的具体实现。

过滤器模式(Filter Pattern)

  Java 过滤器模式(Filter Pattern)是一种结构型设计模式,它允许你使用不同的标准来过滤一组对象,从而去除其中不需要的元素。

使用场景

  • 当你需要从一个集合中过滤出一部分元素时。

  • 当你需要根据不同的标准来过滤同一个集合时。

  • 当你需要在一个集合中根据不同标准来组合过滤器时。

代码实现

  假设有一个 Person 类,包含姓名、年龄、性别等属性,我们可以定义一个过滤器接口 Filter,其中包含一个过滤方法 filter,用于过滤出符合某种条件的人。

public interface Filter {
    List<Person> filter(List<Person> persons);
}

  定义实现了 Filter 接口的具体过滤器

public class MaleFilter implements Filter {
    @Override
    public List<Person> filter(List<Person> persons) {
        return persons.stream()
                .filter(person -> person.getGender().equals("MALE"))
                .collect(Collectors.toList());
    }
}

public class FemaleFilter implements Filter {
    @Override
    public List<Person> filter(List<Person> persons) {
        return persons.stream()
                .filter(person -> person.getGender().equals("FEMALE"))
                .collect(Collectors.toList());
    }
}

public class AgeFilter implements Filter {
    @Override
    public List<Person> filter(List<Person> persons) {
        return persons.stream()
                .filter(person -> person.getAge() > 18)
                .collect(Collectors.toList());
    }
}

  最后,我们可以定义一个 FilterChain 类,用于组合多个过滤器

public class FilterChain implements Filter {
    private List<Filter> filters;

    public FilterChain(List<Filter> filters) {
        this.filters = filters;
    }

    @Override
    public List<Person> filter(List<Person> persons) {
        List<Person> result = persons;
        for (Filter filter : filters) {
            result = filter.filter(result);
        }
        return result;
    }
}

  这样,我们就可以使用过滤器来过滤出符合不同条件的人了

public static void main(String[] args) {
    List<Person> persons = new ArrayList<>();
    persons.add(new Person("Tom", 20, "MALE"));
    persons.add(new Person("Lucy", 19, "FEMALE"));
    persons.add(new Person("John", 17, "MALE"));
    persons.add(new Person("Lily", 21, "FEMALE"));

    Filter maleFilter = new MaleFilter();
    Filter femaleFilter = new FemaleFilter();
    Filter ageFilter = new AgeFilter();

    FilterChain maleAndAgeFilter = new FilterChain(Arrays.asList(maleFilter, ageFilter));
    FilterChain femaleAndAgeFilter = new FilterChain(Arrays.asList(femaleFilter, ageFilter));

    System.out.println("Male and age filter:");
    maleAndAgeFilter.filter(persons).forEach(System.out::println);
    //Person(name=Tom, age=20, gender=MALE)
    //Person(name=Lily, age=21, gender=FEM

    System.out.println("Female and age filter:");
    femaleAndAgeFilter.filter(persons).forEach(System.out::println);
    //Person(name=Lily, age=21, gender=FEMALE)
}

使用小结

  滤器模式的核心是 Filter 接口,该接口包含了一个方法 filter(),用于对对象进行过滤。根据具体实现,filter() 方法可以接受一个对象作为参数,然后根据特定的标准判断该对象是否应该被过滤掉。过滤器模式在Java中可以应用于许多场景,例如:

  1. Web开发中,对HTTP请求进行过滤,例如身份验证,日志记录等。

  2. 在Spring框架中,过滤器可以用于过滤请求并对请求进行预处理,例如对请求参数进行验证等。

  3. Java 中的 Stream API 提供了丰富的过滤器方法,如 filter()、distinct()、map()

  过滤器模式可以用于任何需要对数据进行过滤和处理的场景。