行为设计模式及其在JVM中的应用

简介: 本文介绍设计模式中的行为设计模式及其在JVM中的应用

责任链模式

责任链模式允许对象实现一个公共接口,并且每个实现都可以在适当的情况下委托给下一个实现。 然后,这允许我们构建一个实现链,其中每个实现在调用链中的下一个元素之前或之后执行一些操作:

例如:

interface ChainOfResponsibility {
    void perform();
}
class LoggingChain {
    private ChainOfResponsibility delegate;

    public void perform() {
        System.out.println("Starting chain");
        delegate.perform();
        System.out.println("Ending chain");
    }
}

在这里,我们可以看到一个示例,其中我们的实现在委托调用之前和之后打印出来。

我们没有必要调用委托。我们可以决定我们不应该这样做,而是提前终止链。 例如,如果有一些输入参数,我们可以验证它们并在它们无效时提前终止。

JVM实例

Servlet 过滤器是 JEE 生态系统中以这种方式工作的一个示例。 单个实例接收 servlet 请求和响应,一个 FilterChain 实例代表整个过滤器链。 然后每个人都应该执行其工作,然后终止链或调用 chain.doFilter() 以将控制权传递给下一个过滤器:

public class AuthenticatingFilter implements Filter {
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 
      throws IOException, ServletException {
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        if (!"MyAuthToken".equals(httpRequest.getHeader("X-Auth-Token")) {
             return;
        }
        chain.doFilter(request, response);
    }
}

命令模式

命令模式允许我们将一些具体的行为——或命令——封装在一个公共接口后面,这样它们就可以在运行时被正确地触发。

通常我们会有一个 Command 接口,一个接收命令实例的 Receiver 实例,以及一个负责调用正确命令实例的 Invoker。 然后,我们可以定义 Command 接口的不同实例来在接收器上执行不同的操作:

interface DoorCommand {
    perform(Door door);
}
class OpenDoorCommand implements DoorCommand {
    public void perform(Door door) {
        door.setState("open");
    }
}

在这里,我们有一个命令实现,它将门作为接收器,并导致门“打开”。 然后,我们的调用者可以在希望打开给定门时调用此命令,并且该命令封装了如何执行此操作。

将来,我们可能需要更改我们的 OpenDoorCommand 以检查门是否首先被锁定。 此更改将完全在命令中,并且接收者和调用者类不需要进行任何更改。

JVM实例

这种模式的一个非常常见的例子是 Swing 中的 Action 类:

Action saveAction = new SaveAction();
button = new JButton(saveAction)

这里,SaveAction 是命令,使用这个类的 Swing JButton 组件是调用者,Action 实现被调用,ActionEvent 作为接收者。

迭代器模式

迭代器模式允许我们跨集合中的元素工作并依次与每个元素交互。 我们使用它来编写对某些元素进行任意迭代的函数,而不考虑它们来自何处。 源可以是有序列表、无序集或无限流:

void printAll<T>(Iterator<T> iter) {
    while (iter.hasNext()) {
        System.out.println(iter.next());
    }
}

JVM实例

所有 JVM 标准集合都通过公开一个 iterator() 方法来实现迭代器模式,该方法在集合中的元素上返回一个 Iterator。 流也实现了相同的方法,除了在这种情况下,它可能是一个无限流,因此迭代器可能永远不会终止。

备忘录模式

备忘录模式允许我们编写能够改变状态的对象,然后恢复到之前的状态。 本质上是对象状态的“撤消”功能。

这可以通过在任何时候调用 setter 时存储先前的状态来相对容易地实现:

class Undoable {
    private String value;
    private String previous;

    public void setValue(String newValue) {
        this.previous = this.value;
        this.value = newValue;
    }

    public void restoreState() {
        if (this.previous != null) {
            this.value = this.previous;
            this.previous = null;
        }
    }
}

这提供了撤消对对象所做的最后更改的能力。

这通常通过将整个对象状态包装在单个对象中来实现,称为 Memento。 这允许在单个操作中保存和恢复整个状态,而不必单独保存每个字段。

虚拟机实例

JavaServer Faces 提供了一个名为 StateHolder 的接口,允许实现者保存和恢复其状态。 有几个标准组件可以实现这一点,包括单独的组件——例如,HtmlInputFile、HtmlInputText 或 HtmlSelectManyCheckbox——以及复合组件,例如 HtmlForm。

观察者模式

观察者模式允许一个对象向其他人表明发生了变化。 通常我们会有一个 Subject - 发出事件的对象,和一系列 Observers - 接收这些事件的对象。 观察者将向他们希望被告知更改的主题进行注册。 一旦发生这种情况,主题中发生的任何变化都会导致观察者被告知:

class Observable {
    private String state;
    private Set<Consumer<String>> listeners = new HashSet<>;

    public void addListener(Consumer<String> listener) {
        this.listeners.add(listener);
    }

    public void setState(String newState) {
        this.state = state;
        for (Consumer<String> listener : listeners) {
            listener.accept(newState);
        }
    }
}

这需要一组事件侦听器,并在每次状态随新状态值更改时调用每个侦听器。

虚拟机实例

java 有一对标准的类允许我们这样做——java.beans.PropertyChangeSupport 和 java.beans.PropertyChangeListener。

PropertyChangeSupport 充当一个可以添加和删除观察者的类,并且可以通知他们所有的状态变化。 PropertyChangeListener 是一个接口,我们的代码可以实现该接口以接收已发生的任何更改:

PropertyChangeSupport observable = new PropertyChangeSupport();

// Add some observers to be notified when the value changes
observable.addPropertyChangeListener(evt -> System.out.println("Value changed: " + evt));

// Indicate that the value has changed and notify observers of the new value
observable.firePropertyChange("field", "old value", "new value");

请注意,还有一对似乎更合适的类——java.util.Observer 和 java.util.Observable。 但是,由于不灵活且不可靠,这些在 Java 9 中已被弃用。

策略模式

策略模式允许我们编写通用代码,然后将特定策略插入其中,为我们的具体情况提供所需的特定行为。

这通常通过一个代表策略的接口来实现。 然后,客户端代码能够根据具体情况的需要编写实现此接口的具体类。 例如,我们可能有一个系统,我们需要通知最终用户并将通知机制实现为可插拔策略:

interface NotificationStrategy {
    void notify(User user, Message message);
}
class EmailNotificationStrategy implements NotificationStrategy {
    ....
}
class DingTalkNotificationStrategy implements NotificationStrategy {
    ....
}

然后,我们可以在运行时准确地决定实际使用这些策略中的哪一个来将此消息发送给该用户。 我们还可以编写新的策略来使用,而对系统的其余部分影响最小。

虚拟机实例

标准 Java 库广泛使用这种模式,通常以起初可能并不明显的方式使用。 例如,Java 8 中引入的 Streams API 广泛使用了这种模式。 提供给 map()、filter() 和其他方法的 lambdas 都是提供给泛型方法的可插入策略。

再早之前,Java 1.2 中引入的 Comparator 接口是一种策略,可以根据需要对集合中的元素进行排序。 我们可以提供 Comparator 的不同实例,以根据需要以不同的方式对同一列表进行排序:

// Sort by name
Collections.sort(users, new UsersNameComparator());

// Sort by ID
Collections.sort(users, new UsersIdComparator());

模板方法模式

当我们想要协调几种不同的方法一起工作时,使用模板方法模式。 我们将使用模板方法和一组一个或多个抽象方法定义一个基类——要么未实现,要么以某些默认行为实现。 然后模板方法以固定模式调用这些抽象方法。 然后我们的代码实现这个类的一个子类,并根据需要实现这些抽象方法:

class Component {
    public void render() {
        doRender();
        addEventListeners();
        syncData();
    }

    protected abstract void doRender();

    protected void addEventListeners() {}

    protected void syncData() {}
}

在这里,我们有一些任意的 UI 组件。 我们的子类将实现 doRender() 方法来实际渲染组件。 我们还可以选择实现 addEventListeners() 和 syncData() 方法。 当我们的 UI 框架呈现这个组件时,它将保证所有三个都以正确的顺序被调用。

虚拟机实例

Java 集合使用的 AbstractList、AbstractSet 和 AbstractMap 有很多这种模式的例子。 例如, indexOf() 和 lastIndexOf() 方法都根据 listIterator() 方法工作,该方法具有默认实现,但在某些子类中会被覆盖。 同样, add(T) 和 addAll(int, T) 方法都根据 add(int, T) 方法工作,该方法没有默认实现,需要由子类实现。

Java IO 也在 InputStream、OutputStream、Reader 和 Writer 中使用了这种模式。 例如,InputStream 类有几个以 read(byte[], int, int) 方式工作的方法,需要子类来实现。

访问者模式

访问者模式允许我们的代码以类型安全的方式处理各种子类,而无需求助于 instanceof 检查。 对于我们需要支持的每个具体子类,我们将有一个访问者接口和一个方法。 然后我们的基类将有一个 accept(Visitor) 方法。 子类将各自调用此访问者的适当方法,将自身传入。这允许我们在这些方法中的每一个中实现具体行为,每个方法都知道它将使用具体类型:

interface UserVisitor<T> {
    T visitStandardUser(StandardUser user);
    T visitAdminUser(AdminUser user);
    T visitSuperuser(Superuser user);
}
class StandardUser {
    public <T> T accept(UserVisitor<T> visitor) {
        return visitor.visitStandardUser(this);
    }
}

这里我们有我们的 UserVisitor 接口,上面有三种不同的访问者方法。 我们的示例 StandardUser 调用适当的方法,在 AdminUser 和 Superuser 中也会这样做。 然后,我们可以根据需要编写访问者以使用这些:

class AuthenticatingVisitor {
    public Boolean visitStandardUser(StandardUser user) {
        return false;
    }
    public Boolean visitAdminUser(AdminUser user) {
        return user.hasPermission("write");
    }
    public Boolean visitSuperuser(Superuser user) {
        return true;
    }
}

我们的 StandardUser 从来没有权限,我们的 Superuser 总是有权限,我们的 AdminUser 可能有权限,但这需要在用户本身中查找。

虚拟机实例

Java NIO2 框架将此模式与 Files.walkFileTree() 一起使用。 这需要一个 FileVisitor 的实现,它具有处理遍历文件树的各个不同方面的方法。 然后,我们的代码可以使用它来搜索文件、打印匹配的文件、处理目录中的许多文件或许多其他需要在目录中工作的事情:

Files.walkFileTree(startingDir, new SimpleFileVisitor() {
    public FileVisitResult visitFile(Path file, BasicFileAttributes attr) {
        System.out.println("Found file: " + file);
    }

    public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) {
        System.out.println("Found directory: " + dir);
    }
});

结论

在本文中,我们了解了用于对象行为的各种设计模式。 我们还查看了在核心 JVM 中使用的这些模式的示例,因此我们可以看到它们以许多应用程序已经受益的方式使用。

参考文献:
https://www.baeldung.com/java-behavioral-patterns-jdk
https://gameprogrammingpatterns.com/behavioral-patterns.html
https://en.wikipedia.org/wiki/Behavioral_pattern
相关文章
|
1月前
|
设计模式 PHP
PHP中的设计模式:单一职责原则在软件开发中的应用
【10月更文挑战第8天】 在软件开发中,设计模式是解决常见问题的经验总结,而单一职责原则作为面向对象设计的基本原则之一,强调一个类应该只有一个引起变化的原因。本文将探讨单一职责原则在PHP中的应用,通过实际代码示例展示如何运用该原则来提高代码的可维护性和可扩展性。
33 1
|
1月前
|
设计模式 算法 搜索推荐
后端开发中的设计模式应用与实践
在软件开发的广袤天地中,后端技术如同构筑高楼大厦的钢筋水泥,支撑起整个应用程序的骨架。本文旨在通过深入浅出的方式,探讨后端开发领域内不可或缺的设计模式,这些模式犹如精雕细琢的工具箱,能够助力开发者打造出既健壮又灵活的系统架构。从单例模式到工厂模式,从观察者模式到策略模式,每一种设计模式都蕴含着深刻的哲理与实践价值,它们不仅仅是代码的组织方式,更是解决复杂问题的智慧结晶。
|
2月前
|
设计模式 数据库连接 PHP
PHP中的设计模式:提升代码的可维护性与扩展性在软件开发过程中,设计模式是开发者们经常用到的工具之一。它们提供了经过验证的解决方案,可以帮助我们解决常见的软件设计问题。本文将介绍PHP中常用的设计模式,以及如何利用这些模式来提高代码的可维护性和扩展性。我们将从基础的设计模式入手,逐步深入到更复杂的应用场景。通过实际案例分析,读者可以更好地理解如何在PHP开发中应用这些设计模式,从而写出更加高效、灵活和易于维护的代码。
本文探讨了PHP中常用的设计模式及其在实际项目中的应用。内容涵盖设计模式的基本概念、分类和具体使用场景,重点介绍了单例模式、工厂模式和观察者模式等常见模式。通过具体的代码示例,展示了如何在PHP项目中有效利用设计模式来提升代码的可维护性和扩展性。文章还讨论了设计模式的选择原则和注意事项,帮助开发者在不同情境下做出最佳决策。
|
2月前
|
设计模式 算法 测试技术
PHP中的设计模式:策略模式的应用与实践
在软件开发的浩瀚海洋中,设计模式如同灯塔,指引着开发者们避开重复造轮子的暗礁,驶向高效、可维护的代码彼岸。今天,我们将聚焦于PHP领域中的一种重要设计模式——策略模式,探讨其原理、应用及最佳实践,揭示如何通过策略模式赋予PHP应用灵活多变的业务逻辑处理能力,让代码之美在策略的变换中熠熠生辉。
|
2月前
|
设计模式 算法 数据库连接
PHP中的设计模式:提高代码的可维护性与扩展性本文旨在探讨PHP中常见的设计模式及其应用,帮助开发者编写出更加灵活、可维护和易于扩展的代码。通过深入浅出的解释和实例演示,我们将了解如何使用设计模式解决实际开发中的问题,并提升代码质量。
在软件开发过程中,设计模式是一套经过验证的解决方案模板,用于处理常见的软件设计问题。PHP作为流行的服务器端脚本语言,也有其特定的设计模式应用。本文将重点介绍几种PHP中常用的设计模式,包括单例模式、工厂模式和策略模式,并通过实际代码示例展示它们的具体用法。同时,我们还将讨论如何在实际项目中合理选择和应用这些设计模式,以提升代码的可维护性和扩展性。
61 4
|
23天前
|
设计模式 存储 数据库连接
PHP中的设计模式:单例模式的深入理解与应用
【10月更文挑战第22天】 在软件开发中,设计模式是解决特定问题的通用解决方案。本文将通过通俗易懂的语言和实例,深入探讨PHP中单例模式的概念、实现方法及其在实际开发中的应用,帮助读者更好地理解和运用这一重要的设计模式。
16 1
|
1月前
|
设计模式 PHP 开发者
PHP中的设计模式:桥接模式的解析与应用
在软件开发的浩瀚海洋中,设计模式如同灯塔一般,为开发者们指引方向。本文将深入探讨PHP中的一种重要设计模式——桥接模式。桥接模式巧妙地将抽象与实现分离,通过封装一个抽象的接口,使得实现和抽象可以独立变化。本文将阐述桥接模式的定义、结构、优缺点及其应用场景,并通过具体的PHP示例代码展示如何在实际项目中灵活运用这一设计模式。让我们一起走进桥接模式的世界,感受它的魅力所在。
|
1月前
|
设计模式 测试技术 持续交付
架构视角下的NHibernate:设计模式与企业级应用考量
【10月更文挑战第13天】随着软件开发向更复杂、更大规模的应用转变,数据访问层的设计变得尤为重要。NHibernate作为一个成熟的对象关系映射(ORM)框架,为企业级.NET应用程序提供了强大的支持。本文旨在为有一定经验的开发者提供一个全面的指南,介绍如何在架构层面有效地使用NHibernate,并结合领域驱动设计(DDD)原则来构建既强大又易于维护的数据层。
38 2
|
1月前
|
设计模式 算法 PHP
PHP中的设计模式:策略模式的深入解析与应用
【10月更文挑战第8天】 在软件开发的浩瀚宇宙中,设计模式如同星辰指引,照亮了代码设计与架构的航道。本文旨在深入探索PHP语境下策略模式(Strategy Pattern)的精髓,不仅剖析其内核原理,还将其融入实战演练,让理论在实践中生根发芽。策略模式,作为解决“如何优雅地封装算法族”的答案,以其独特的灵活性与扩展性,赋予PHP应用以动态变换行为的能力,而无需牵动既有的类结构。
27 2
|
1月前
|
设计模式 缓存 数据库连接
探索PHP中的设计模式:单例模式的实现与应用
在PHP开发中,设计模式是提高代码可复用性、可维护性和扩展性的重要工具。本文将深入探讨单例模式(Singleton Pattern)的基本概念、在PHP中的实现方式以及实际应用场景。单例模式确保一个类仅有一个实例,并提供全局访问点。通过具体代码示例和详细解释,我们将展示如何在PHP项目中有效利用单例模式来解决实际问题,提升开发效率和应用性能。
下一篇
无影云桌面