迹忆客 专注技术分享

当前位置:主页 > 学无止境 > 算法 >

Java中的策略设计模式和开闭原则

作者:迹忆客 最近更新:2023/02/01 浏览次数:

策略设计模式基于开闭设计原则,即著名的 SOLID 设计原则的“O”。 它是与装饰器模式观察者模式工厂模式一起在面向对象分析和设计领域中流行的模式之一。 策略模式允许我们封装流程中可能的更改并将其封装在 Strategy 类中。 通过这样做,我们的过程(主要是一种方法)取决于一种策略,一种比实现更高层次的抽象。 这使我们的流程可以通过提供新的 Strategy 实现来进行扩展,但不会进行修改,因为引入新策略不需要更改经过测试的方法。 这就是它如何确认开闭设计原则。

虽然与任何其他设计模式一样,它也会在代码库中引入更多的类,但这样做是值得的,因为它可以更好地组织代码并提供急需的灵活性。 它还使更改属性变得容易。

策略模式非常有用,特别是对于实现算法策略,例如 加密、压缩、比较等。来自 JDK 的策略模式的几个示例是 ComparatorLayoutManager。 我们可以将 Comparator 视为 Strategy 接口,定义 compare() 方法,现在取决于类如何比较它们自己。

此方法用于在 Collections.sort() 内部进行排序,这证实了 OCP,因为它不需要在比较逻辑发生变化时进行任何更改。 同样 LayoutManager 例如 GridLayout、BorderLayout 帮助我们以不同方式组织组件。


策略设计模式示例——开闭设计原则

这个策略模式的例子也可以看作是开闭设计原则的例子,它指出设计、代码(类或方法)应该对扩展保持开放,对修改保持封闭。

当我们说对修改关闭时,这意味着新的更改应该由新代码实现,而不是改变现有代码。 这减少了破坏现有的久经考验的代码的可能性。

策略模式也是GOF列表中的行为模式之一,最早出现在经典的GOF设计模式书中。

在此示例中,我们需要按特定标准过滤传入消息,例如 如果消息属于特定类型,则过滤消息。 但我们知道此标准可能会发生变化,我们可能需要根据邮件的大小或内容中的特定关键字来过滤邮件。 在这个操作的中心,我们在 MessageUtils 类中有一个 filter() 方法,这个类负责过滤消息。

在最简单的形式中,只需将它们从传入消息列表中删除。 为了即使在过滤策略因新需求而发生变化时也能保持此代码完整,我们将使用策略设计模式

为了实现策略模式,我们将定义一个 Strategy 接口,比如 FilteringStrategy,它包含 isFilterable(Message msg) 方法,如果传递给它的 Message 可按条件过滤,则此方法返回 true,由子类实现。

这种设计使得引入新策略变得非常容易。 我们有此 Strategy 接口 FilterByTypeFilterBySizeFilteryByKeyword 的三个实现。

我们的数据对象 Message 包含一个由 Java Enum 表示的类型、一个整数大小和 String 内容。 此设计也可扩展,因为我们可以引入任何过滤策略。

这是策略模式的 UML 图,这次,我们使用排序策略来实现不同的排序算法,例如 冒泡排序tml)、快速排序tml)和插入排序

 

Java 中策略模式UML图
Java 中策略模式UML图

 

策略设计模式在Java中的实现

这是 Java 中策略设计模式的完整代码示例。

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Java程序实现策略设计模式和开闭设计原则。 
 * filter() 方法使用策略模式来过滤消息。
 *
 * @author jiyik
 */
public class StrategyPattern {

    private static final Logger logger = LoggerFactory.getLogger(StrategyPattern.class);

    public static void main(String args[]) {
        List<Message> messages = new ArrayList<>();
        messages.add(new Message(MessageType.TEXT, 100, "This is test message"));
        messages.add(new Message(MessageType.XML, 200, "How are you "));
        messages.add(new Message(MessageType.TEXT, 300, "Does Strategy pattern follows OCP design principle?"));
        messages.add(new Message(MessageType.TEXT, 400, "Wrong Message, should be filtered"));
       
        messages = filter(messages, new FilterByType(MessageType.XML));
        messages = filter(messages, new FilterByKeyword("Wrong"));
        messages = filter(messages, new FilterBySize(200));
       
    }

    /*
     * 该方法符合开闭设计原则,开放修改,
     * 因为我们可以通过提供FilteringStrategy的实现来提供任何过滤条件,
     * 而无需更改此处的任何代码。 
     * 新功能将由新代码提供。
     */
    public static final List<Message> filter(List<Message> messageList, FilteringStrategy strategy){
       
        Iterator<Message> itr = messageList.iterator();
       
        while(itr.hasNext()){
            Message msg = itr.next();           
            if(strategy.isFilterable(msg)){
                logger.info(strategy.toString() + msg);
                itr.remove();
            }
        }
       
        return messageList;
    }
   
}

我们可以看到我们的列表包含四个消息,一个是 XML 类型,三个是 TEXT 类型。 所以当我们第一次按 XML 类型过滤消息时,可以看到只有 XML 类型的消息被过滤了。 接下来,当我们根据关键字“Wrong”过滤消息时,只会过滤包含该关键字的消息。

最后,当我过滤大小为 200 的消息时,只过滤大小大于 200 的消息。 所以我们可以通过提供 Strategy 接口的新实现来用相同的代码做不同的事情,这就是策略设计模式的力量。

我们还可以使用 lambda 表达式在 Java 中实现策略模式,因为我们可以在 Java 中使用 lambda 代替匿名类。 这将使我们的代码更具可读性和简洁性。

本例的重要类

以下是执行此示例的其他重要类:

Message.java

class Message{
    private MessageType type;
    private int size;
    private String content;

    public Message(MessageType type, int size, String content) {
        this.type = type;
        this.size = size;
        this.content = content;
    }

    public String getContent() {
        return content;
    }

    public int getSize() {
        return size;
    }

    public MessageType getType() {
        return type;
    }

    @Override
    public String toString() {
        return " Message{" + "type=" + type + ", size=" + size + ", content=" + content + '}';
    }
   
}

该类用于定义不同的消息类型

MessageType.java

/*
 * 枚举以表示不同的消息类型
 */
public enum MessageType {
    TEXT, BYTE, XML;
}

这是定义Strategy的核心接口

FilteringStrategy.java

/*
 * 为该模式定义策略的接口。
 */
public interface FilteringStrategy{
    public boolean isFilterable(Message msg);
}

这是 Strategy 接口的一种实现,它按类型过滤消息。

FilterByType.java

/*
 * Strategy 接口的实现,它决定按类型过滤消息。
 */
public class FilterByType implements FilteringStrategy{
    private MessageType type;

    public FilterByType(MessageType type) {
        this.type = type;
    }

    @Override
    public boolean isFilterable(Message msg) {
        return type == msg.getType();
    }

    @Override
    public String toString() {
        return "Filtering By type: " + type;
    }
   
}

这是 Strategy 接口的另一个实现,它按大小过滤消息:

FilterBySize.java

/*
 * 另一种按大小过滤消息的策略实现
 */
public class FilterBySize implements FilteringStrategy{
    private int maxSize;

    public FilterBySize(int maxSize) {
        this.maxSize = maxSize;
    }   

    @Override
    public boolean isFilterable(Message msg) {
        return msg.getSize() > maxSize;
    }
   
    @Override
    public String toString() {
        return "Filtering By maxSize: " + maxSize;
    }
}

这是 Strategy 接口的另一种实现,它将按关键字过滤消息

FilterByKeyword.java

/*
 * 另一种通过内容中的关键字过滤消息的策略实现。
 */
public class FilterByKeyword implements FilteringStrategy{
    private String keyword;

    public FilterByKeyword(String keyword) {
        this.keyword = keyword;
    }

    public String getKeyword() {
        return keyword;
    }

    public void setKeyword(String keyword) {
        this.keyword = keyword;
    }

    @Override
    public boolean isFilterable(Message msg) {
        return msg.getContent()==null || msg.getContent().contains(keyword);
    }
   
    @Override
    public String toString() {
        return "Filtering By keyword: " + keyword;
    }
   
}

这就是 Java 中策略设计模式的真实示例中的全部内容。 正如我所说,由于策略模式确认了开闭设计原则,因此我们也可以将其视为 Java 中开闭设计原则的示例。 设计模式是在特定上下文中解决问题的久经考验的方法,核心模式的知识确实可以帮助我们编写更好的代码。

转载请发邮件至 1244347461@qq.com 进行申请,经作者同意之后,转载请以链接形式注明出处

本文地址:

相关文章

使用 Java 在 MongoDB 中生成 ObjectId

发布时间:2023/04/20 浏览次数:179 分类:MongoDB

本文将讨论 ObjectId 以及我们如何使用 Java 程序生成它。 为了使主题更简单,我们将看到一个带有解释的示例,以使主题更容易。

扫一扫阅读全部技术教程

社交账号
  • https://www.github.com/onmpw
  • qq:1244347461

最新推荐

教程更新

热门标签

扫码一下
查看教程更方便