首页 > 其他分享 >设计模式串讲

设计模式串讲

时间:2023-08-19 16:56:42浏览次数:43  
标签:请求 串讲 对象 light void private 设计模式 public

Behavioral Design Patterns 行为模式

行为模式负责对象之间的高效沟通和职责委派,这些模式刻画了运行时难以跟踪的复杂控制流,从而把开发者的注意力从控制流转移到对象间的联系方式上。行为模式不仅描述对象或类的模式,还描述它们之间的通信模式。

行为类模式使用继承机制在类间分派行为,有模板方法(Template Method)和解释器(Interpreter)。

行为对象模式使用对象复合而不是继承。一些行为对象模式描述了一组对等的对象怎样相互协作以完成其中任一个对象都无法单独完成的任务。这里一个重要的问题是对等的对象如何互相了解对方。对等对象可以保持显式的对对方的引用,但那会增加它们的耦合度。在极端情况下,每一个对象都要了解所有其他的对象。 中介者在对等对象间引入一个中介对象以避免这种情况的出现。 中介者提供了松耦合所需的间接性。

责任链模式提供更松的耦合。它让你通过一条候选对象链隐式的向一个对象发送请求。根据运行时刻情况任一候选者都可以响应相应的请求。候选者的数目是任意的,可以在运行时刻决定哪些候选者参与到链中。
观察者模式定义并保持对象间的依赖关系。
其他的行为对象模式常将行为封装在一个对象中并将请求指派给它。策略模式将算法封装在对象中,这样可以方便地指定和改变一个对象所使用的算法。 命令模式将请求封装在对象中,这样它就可作为参数来传递,也可以被存储在历史列表里,或者以其他方式使用。状态模式封装一个对象的状态,使得当这个对象的状态对象变化时,该对象可改变它的行为。访问者模式封装分布于多个类之间的行为,而迭代者模式则抽象了访问和遍历一个集合中的对象的方式。

Chain of Responsibility 责任链

问题

在线订购系统的访问限制,认证用户可创建订单,有管理权限的用户拥有所有订单的完全访问权限,这些检查依次进行,如何实现?

请求发送者和接收者的强耦合关系将使得后续调整检查步骤十分困难,并且复用部分检查步骤也很困难。

解决方案

使多个对象都有机会处理请求,从而避免发送者和接收者之间的耦合关系。

示意图

Chain of Responsibility

两种方式:

  • 请求中包含正确的数据,所有处理者都执行自己的主要行为
  • 如果某一个处理者能够处理,将不再传递请求

Demo代码

public class ChainOfResponsibilityPattern {
    public static void main(String[] args) {
        //组装责任链
        Handler userAuth = new UserAuthenticator();
        Handler adminAuth = new AdminAuthenticator();
        Handler endHandler = new EndHandler();
        userAuth.setNext(adminAuth);
        adminAuth.setNext(endHandler);
        
        //提交请求
        string userName = args[1];
        userAuth.handle(userName);
    }
}

abstract class Handler {
    private Handler next;

    public void setNext(Handler next) {
        this.next = next;
    }

    public abstract void handle(String request);
}

class UserAuthenticator extends Handler {
    public void handle(String userName) {
        if (!userExists(userName)) {
            System.out.println("用户不存在。");
        } else {
            // 赋予用户权限
            ...

			next.handle(userName);
        }
    }
}

class AdminAuthenticator extends Handler {
    public void handle(String userName) {
        if (adminExists(userName)) {
            // 赋予管理者权限
            ...

			next.handle(userName);
        }
    }
}

class EndHandler extends Handler {
    public void handle(String userName) {
        return;
    }
}

适用场景

  • 需要用不同方式处理不同种类需求,且请求类型和顺序预先未知
  • 必须按顺序执行多个处理者
  • 处理者和顺序必须在运行时改变

优缺点

优点

  • SRP:解耦触发和执行操作的类

  • OCP:在不修改客户端代码的情况下增加新的处理者

  • 可以控制请求处理的顺序

缺点

  • 部分请求可能未被处理

和其他模式的区别

  • 和装饰模式的区别:结构很相似,都依赖于递归组合把要执行的操作传递给一系列对象;但责任链的管理者可以相互独立地执行一切操作和随时停止传递请求,装饰无法中断请求的传递但能在遵循接口的情况下扩展对象的行为。

Command Pattern 命令

问题

想象一下,你要把控制房间的灯的开关、变亮和变暗的请求设置到新买的遥控器上,你要怎么做呢?硬编码吗?如果你还想继续添加遥控器呢?如果你想把多个操作设置到一个按键呢?如果你以后也想用遥控器去操控其他电器呢?
又或者,让你去为一款编辑器开发不同按键的功能,你会怎么做呢?对不同的按键编写不同的类,在按键的类中集成该按键的操作吗?快捷键怎么办呢?从图形界面实现的相同操作怎么办呢?要怎么撤回操作呢?

解决方案

你说得对,但是命令模式是一款…… 将请求封装为包含所有和该请求相关的信息的独立的类,从而让你能够把请求参数化、延迟请求的执行或放入队列,并能实现可撤销操作的设计模式。

UML图

Command Pattern
Invoker不负责创建命令对象,它通过构造函数从客户端获取预先生成的命令;客户端把包括接收者实体在内的所有请求参数传给命令的构造参数,生成命令实体。

Demo代码

public interface Command {
    void execute() {}
    void unExecute() {}
}

public LightOn implements Command {
    private Light light;
    
    public LightOn(Light light) {
        this.light = light;
    }
    
    void execute() {
        this.light.on();
    }
    void unExecute() {
        this.light.off();
    }
}

public LightDown implements Command {
    private Light light;
    
    public LightOn(Light light) {
        this.light = light;
    }
    
    void execute() {
        this.light.down();
    }
    void unExecute() {
        this.light.up();
    }
}

public class Invoker {
    private Command cmd;
    
    void setCommand(Command cmd) {
        this.cmd = cmd;
    }
    
    void execCommand() {
        cmd.execute();
    }
    
    void unExecuteCommand() {
        cmd.unExecute();
    }
}

public class Light {
    void on() {}
    void off() {}
    void down() {}
    void up() {}
}

适用场景

  • 需要把命令作为方法的参数进行传递、保存命令在其他对象,或运行时切换已连接的命令时
  • 需要把操作放入队列、通过远程执行操作
  • 需要实现操作回滚

优缺点

优点

  • SRP:解耦触发和执行操作的类
  • OCP:在不修改客户端代码的情况下创建新的命令
  • 实现撤销和恢复功能
  • 实现操作的延迟操作
  • 可以把简单命令组合成复杂命令

缺点

  • 在发送者和接收者之间增加了一个全新的层次,代码可能会变得更加复杂

和其他模式的区别

  • 和策略模式的区别:命令能将任何操作转换为对象,策略则将实现同一目的的不同算法转换为对象
  • 和备忘录模式的区别:都能实现撤销操作,命令通过执行逆操作,备忘录保存命令执行前的状态
  • 命令、责任链、中介者、观察者的区别:都用于处理请求发送者和接收者之间不同的连接方式,命令在发送者和接收者之间建立单向连接,责任链按顺序传给一系列潜在接收者,中介者强制发送者和接收者通过一个中介对象间接沟通,观察者允许接收者动态订阅或取消接收请求。

Mediator 中介者

问题

一个创建和修改客户资料的对话框,由各种控件组成,如文本框、复选框(Checkbox)和按钮,某些表单元素可能会直接进行互动,如提交按钮会在保存数据前校验所有输入内容。

元素之间存在许多关联,直接在表单元素代码中实现业务逻辑将导致很难复用和修改。

解决方案

用一个中介对象来封装一系列的对象交互,使得各对象不需要显式地相互引用,从而实现解耦。

UML图

Mediator Pattern

Demo代码

public class Mediator {
    // 引用UI组件:
    private List<JCheckBox> checkBoxList;
    private JButton selectAll;
    private JButton selectNone;
    private JButton selectInverse;

    public Mediator(List<JCheckBox> checkBoxList, JButton selectAll, JButton selectNone, JButton selectInverse) {
        this.checkBoxList = checkBoxList;
        this.selectAll = selectAll;
        this.selectNone = selectNone;
        this.selectInverse = selectInverse;
        // 绑定事件:
        this.checkBoxList.forEach(checkBox -> {
            checkBox.addChangeListener(this::onCheckBoxChanged);
        });
        this.selectAll.addActionListener(this::onSelectAllClicked);
        this.selectNone.addActionListener(this::onSelectNoneClicked);
        this.selectInverse.addActionListener(this::onSelectInverseClicked);
    }

    // 当checkbox有变化时:
    public void onCheckBoxChanged(ChangeEvent event) {
        boolean allChecked = true;
        boolean allUnchecked = true;
        for (var checkBox : checkBoxList) {
            if (checkBox.isSelected()) {
                allUnchecked = false;
            } else {
                allChecked = false;
            }
        }
        selectAll.setEnabled(!allChecked);
        selectNone.setEnabled(!allUnchecked);
    }

    // 当点击select all:
    public void onSelectAllClicked(ActionEvent event) {
        checkBoxList.forEach(checkBox -> checkBox.setSelected(true));
        selectAll.setEnabled(false);
        selectNone.setEnabled(true);
    }

    // 当点击select none:
    public void onSelectNoneClicked(ActionEvent event) {
        checkBoxList.forEach(checkBox -> checkBox.setSelected(false));
        selectAll.setEnabled(true);
        selectNone.setEnabled(false);
    }

    // 当点击select inverse:
    public void onSelectInverseClicked(ActionEvent event) {
        checkBoxList.forEach(checkBox -> checkBox.setSelected(!checkBox.isSelected()));
        onCheckBoxChanged(null);
    }
}

适用场景

  • 一些对象和其他对象紧密耦合以致于难以对其修改
  • 组件过于依赖其他组件导致无法在不同应用中复用
  • 为了能在不同情景复用一些基本行为导致需要创建大量组件子类时

优缺点

优点

  • SRP:可以把多个组件的交流抽取到同一位置,使其更易理解和维护
  • OCP:无需修改组件即可增加新的中介者
  • 减轻组件间的耦合情况
  • 可方便地复用各个组件

缺点

  • 将交互的复杂性变为中介者的复杂性,可能会变得越来越难以维护

Memento 备忘录

问题

开发编辑器的撤销功能,采用记录快照的方式来恢复原来的状态,但快照所需要的信息很多,要么会暴露类的所有内部细节使其过于脆弱,要么会限制对其状态的访问权限而无法生成快照。

解决方案

把对象状态的副本存储在名为备忘录的特殊对象中,除了创建备忘录的对象外,任何对象不能访问备忘录的内容,其他对象必须使用受限接口和备忘录进行交互,即可以获取快照的元数据(创建时间、操作名称等),不能获取快照中原始对象的状态。

UML图

基于嵌套类的实现

Memento Pattern

备忘录类被嵌套在原发器中,从而原发器可访问备忘录的成员变量和方法;负责人对备忘录的访问权限很有限,只能在栈中保存备忘录,不能修改其状态。

封装更严格的实现

Memento Pattern

恢复方法被定义在备忘录类中,负责人明确禁止修改存储在备忘录中的状态。

Demo代码

public class Editor {
    private Text text;
    private Position pos;
    private Length selectionWidth;
    
    Snapshot createSnapshot() {
        return new Snapshot(this, text, pos, selectionWidth);
    }
    
    class Snapshot {
        private Text snapText;
    	private Position snapPos;
    	private Length snapSelectionWidth;

        Snapshot(Text text, Position pos, Length selectionWidth) {
            this.snapText = text;
            this.snapPos.x = pos.x;
            this.snapPos.y = pos.y;
            this.snapSelectionWidth = selectionWidth;
        }

        void restore() {
            text = snapText;
            pos.x = this.snapPos.x;
            pos.y = this.snapPos.y;
            selectionWidth = this.snapSelectionWidth;
        }
    }
}

public class Command {
    private Editor editor;
    private Editor.Snapshot backup;
    
    public Command(Editor editor) {
        this.editor = editor;
    }
    
    public void makeBackup() {
        backup = editor.createSnapshot();
    }
    
    public void undo() {
        if (backup != null) {
            backup.restore();
        }
    }
}

适用场景

  • 需要创建对象状态快照来恢复之前的状态时
  • 直接访问对象的成员变量、获取器或设置器将导致封装被突破时

优缺点

优点

  • 可以在不破坏封装情况的前提下创建对象状态快照
  • 可以通过让负责人维护原发器状态历史来简化原发器的代码

缺点

  • 客户端过于频繁地创建备忘录,将消耗大量内存
  • 负责人必须完整跟踪原发器的生命周期,以销毁弃用的备忘录
  • 绝大部分动态编程语言无法保证备忘录中的状态不被修改

Visitor 访问者

Interpreter 解释器

标签:请求,串讲,对象,light,void,private,设计模式,public
From: https://www.cnblogs.com/samsonlannister/p/17629288.html

相关文章

  • 剖析MongoDB数据库:理解NoSQL设计模式、优化查询性能和数据安全性
    MongoDB是一个流行的NoSQL文档数据库,它使用JSON样式的文档存储数据。本文将对MongoDB进行剖析,包括NoSQL设计模式、优化查询性能和数据安全性。NoSQL设计模式文档数据库MongoDB采用文档数据库的设计模式,即将相关数据保存在单个文档中,而不是将其拆分成多个表。这种设计模式使得数......
  • 一文带你读懂设计模式之责任链模式
    1.前言emm,翻了一下之前刚入职时候的学习笔记,发现之前在熟悉业务代码的时候曾经专门学习并整理过过设计模式中的责任链模式,之前只是对其简单了解过常用的设计模式有哪些,并未结合实例和源码深入对其探究,利用熟悉代码契机进行系统学习并整理文档如下。2.什么是责任链模式?俗话说没......
  • 设计模式-代理模式
    代理模式(文章目录)什么是代理模式  代理模式(ProxyDesignPattern)的原理和代码实现都不难掌握。它在不改变原始类(或叫被代理类)代码的情况下,通过引入代理类来给原始类附加功能。为什么要用代理模式职责清晰,功能增强部分代码可以放到代理类中,减少与业务代码的耦合;更符合......
  • 设计模式(二)工厂方法
    一、前言工厂模式常见的词:简单工厂、工厂方法、抽象工厂。简单工厂不属于23种经典设计模式,但通常将它作为学习其他工厂模式的基础。二、简单工厂1、定义定义一个工厂类,它可以根据参数的不同返回不同类型的实例,被创建的实例通常都具有共同的父类。由于简单的工厂模式中用于创建......
  • 一文带你读懂设计模式之责任链模式 | 京东云技术团队
    1.前言emm,翻了一下之前刚入职时候的学习笔记,发现之前在熟悉业务代码的时候曾经专门学习并整理过过设计模式中的责任链模式,之前只是对其简单了解过常用的设计模式有哪些,并未结合实例和源码深入对其探究,利用熟悉代码契机进行系统学习并整理文档如下。2.什么是责任链模式?俗话说没有规......
  • Prototype 原型模式简介与 C# 示例【创建型4】【设计模式来了_4】
    〇、简介1、什么是原型模式?一句话解释:  针对比较耗时的对象创建过程,通过原型的Clone方法来克隆对象,而非重新创建。原型设计模式(PrototypeDesignPattern)是一种创建型设计模式,其主要目的是通过复制已有对象来创建新的对象,而无需通过实例化类并初始化其属性。这种模式在需......
  • 16 策略模式 -- go语言设计模式
    策略模式是一种行为设计模式,通过策略模式,可以在运行时修改一个对象的行为。很多资料里对它的定义是:定义一类算法族,将每个算法分别封装起来,让他们可以互相替换,此模式让算法的变化独立于使用算法的客户端。策略模式的实现代码packagemainimport"fmt"/* 练习: 商场促销有策......
  • JS常见的设计模式
    单例模式点击查看代码<script>//单例模式=>自始至终都只能创建一个对象让对象唯一存在letWife=(function(){classPerson{constructor(name){this.name=name}}......
  • 设计模式-策略模式
    策略模式(文章目录)什么是策略模式  定义一族算法类,将每个算法分别封装起来,让它们可以互相替换。策略模式可以使算法的变化独立于使用它们的客户端(这里的客户端代指使用算法的代码)为什么要用策略模式  我们知道,工厂模式是解耦对象的创建和使用,观察者模式是解耦观察者和......
  • JetLinks物联网平台常用的设计模式总结
    平台常用设计模式发布/订阅设计模式(观察者模式)JetLinks平台的消息处理中心(DeviceMessageConnector)类,使用发布/订阅设计模式将设备消息发布至消息总线内(EventBus),规则引擎(RuleEngine)、设备数据写入时序数据库(TimeSeriesMessageWriterConnector),则订阅消息总线内的数据异步对这些......