Mr.Raindrop

Mr.Raindrop

一切都在无可挽回的走向庸俗。
twitter
github

设计模式初探--行为型模式

行为型模式涉及到算法和对象间职责的分配,行为模式描述了对象和类的模式,以及它们之间的通信模式,行为模式刻划了在程序运行时难以跟踪的复杂的控制流可分为行为类模式和行为对象模式。

  1. 行为类模式使用继承机制在类间分派行为。
  2. 行为对象模式使用对象聚合来分配行为。一些行为对象模式描述了一组对等的对象怎样相互协作以完成其中任何一个对象都无法单独完成的任务。

职责链模式(Chain of Responsibility)#

为了避免请求发送者与多个请求处理者耦合在一起,将所有请求的处理者通过前一对象记住其下一个对象的引用而连成一条链;当有请求发生时,可将请求沿着这条链传递,直到有对象处理它为止。

结构#

  • 抽象处理者(Handler):定义处理请求的接口(如handleRequest()),并持有下一个处理者的引用。
  • 具体处理者(Concrete Handler): 实现处理逻辑,决定是否处理请求或传递。
  • 客户端(Client): 创建处理链并触发请求。

实现

// 抽象处理者
public abstract class Approver {
    protected Approver nextApprover;
    public void setNext(Approver next) { this.nextApprover = next; }
    public abstract void handleRequest(LeaveRequest request);
}

// 具体处理者:组长
public class GroupLeader extends Approver {
    @Override
    public void handleRequest(LeaveRequest request) {
        if (request.getDays() <= 2) {
            System.out.println("组长批准请假");
        } else if (nextApprover != null) {
            nextApprover.handleRequest(request); // 传递请求
        }
    }
}

// 客户端构建链
Approver groupLeader = new GroupLeader();
Approver manager = new Manager();
groupLeader.setNext(manager);
groupLeader.handleRequest(new LeaveRequest("张三", 3)); // 转经理处理

迭代器模式#

迭代器模式是一种行为型设计模式,它提供了一种统一的方式来访问集合对象中的元素,而不是暴露集合内部的表示方式。简单地说,就是将遍历集合的责任封装到一个单独的对象中,我们可以按照特定的方式访问集合中的元素。(听不懂,斯密达)

将类组成数组迭代与迭代器的区别:迭代器模式通过抽象遍历逻辑解耦数据结构,提供了更高的灵活性(不同的遍历策略:分页、并行)和扩展性,尤其适合需要隐藏内部实现、支持多样化遍历策略的场景。通过泛型(如Iterator<T>)保证类型安全,同时为不同集合(ListSet)提供统一接口。例如,Java 的Iterable接口允许for-each循环兼容所有集合。而数组直接迭代在简单、固定数据结构的场景中更具性能优势。

CS61B 11. Inheritance IV: Iterators, Object Methods

应用

遍历音乐播放列表:当我们在手机或电脑上播放音乐时,通常会创建一个播放列表。播放列表可以被视为一个集合,每首歌曲可以被视为集合中的一个元素。使用迭代器模式,我们可以通过迭代器对象逐个访问播放列表中的歌曲,进行播放、暂停或切歌等操作。

迭代器模式的组成#

  • 抽象迭代器(Iterator):定义了遍历聚合对象所需的方法,包括 hashNext () 和 next () 方法等,用于遍历聚合对象中的元素。
  • 具体迭代器(Concrete Iterator):它是实现迭代器接口的具体实现类,负责具体的遍历逻辑。它保存了当前遍历的位置信息,并可以根据需要向前或向后遍历集合元素。
  • 抽象聚合器(Aggregate): 一般是一个接口,提供一个 iterator () 方法,例如 java 中的 Collection 接口,List 接口,Set 接口等。
  • 具体聚合器(ConcreteAggregate):就是抽象容器的具体实现类,比如 List 接口的有序列表实现 ArrayList,List 接口的链表实现 LinkList,Set 接口的哈希列表的实现 HashSet 等。

image

实现#

设计模式第 16 讲 —— 迭代器模式(Iterator)-CSDN 博客

状态模式#

允许对象在内部状态改变时,改变它的行为 。 将不同的状 隔离;每个状态都是一个单独的类 。

核心角色#

  • 上下文(Context):维护当前状态引用。
  • 抽象状态(State):声明状态行为的接口。
  • 具体状态(Concrete State):实现特定状态下的行为,并可能触发状态转换。

实现#

// 抽象状态接口:订单状态行为契约 (参考网页2[2](@ref)和网页8[8](@ref))
public interface OrderState {
    void pay(OrderContext context);      // 支付操作
    void cancel(OrderContext context);   // 取消订单
    void ship(OrderContext context);      // 发货操作
    void refund(OrderContext context);    // 申请退款
    void confirmReceipt(OrderContext context); // 确认收货
}

// 待支付
public class PendingPaymentState implements OrderState {
    @Override
    public void pay(OrderContext context) {
        System.out.println("✅ 支付成功,订单进入已支付状态");
        context.setState(new PaidState());
    }

    @Override
    public void cancel(OrderContext context) {
        System.out.println("❌ 订单已取消");
        context.setState(new CanceledState());
    }

    // 其他操作禁止(参考网页8的状态约束[8](@ref))
    @Override
    public void ship(OrderContext context) {
        throw new IllegalStateException("待支付订单不能发货");
    }
    // ... 类似实现其他方法的禁止逻辑
}

// 已支付
public class PaidState implements OrderState {
    @Override
    public void ship(OrderContext context) {
        System.out.println("🚚 商品已发货");
        context.setState(new ShippedState());
    }

    @Override
    public void refund(OrderContext context) {
        System.out.println("🔄 退款申请受理中");
        context.setState(new RefundingState());
    }
    
    // 禁止重复支付(参考网页3的状态转换[3](@ref))
    @Override
    public void pay(OrderContext context) {
        throw new IllegalStateException("已支付订单不能重复付款");
    }
}

// 已发货
public class ShippedState implements OrderState {
    @Override
    public void confirmReceipt(OrderContext context) {
        System.out.println("🎁 确认收货,交易完成");
        context.setState(new CompletedState());
    }

    @Override
    public void refund(OrderContext context) {
        System.out.println("📦 发起退货流程");
        context.setState(new RefundingState());
    }
}

// 上下文类 (订单主体)
public class OrderContext {
    private OrderState currentState;
    private String orderId;

    public OrderContext() {
        this.currentState = new PendingPaymentState(); // 初始状态 待支付
        this.orderId = UUID.randomUUID().toString();
    }

    // 状态转换入口(参考网页2的TransitionTo设计[2](@ref))
    public void setState(OrderState state) {
        System.out.printf("【状态变更】%s → %s%n", 
            currentState.getClass().getSimpleName(),
            state.getClass().getSimpleName());
        this.currentState = state;
    }

    // 委托操作到当前状态(参考网页3的上下文设计[3](@ref))
    public void pay() { currentState.pay(this); }
    public void cancel() { currentState.cancel(this); }
    // ... 其他操作委托方法
}

// 客户端调用示例
public class Client {
    public static void main(String[] args) {
        OrderContext order = new OrderContext();
        
        order.pay();       // ✅ 正常支付
        order.ship();      // ✅ 发货操作
        order.pay();       // ❌ 抛出 IllegalStateException
        
        try {
            order.cancel(); // ❌ 已发货订单不可取消
        } catch (Exception e) {
            System.out.println("操作失败: " + e.getMessage());
        }
    }
}

模板方法模式#

Java 设计模式 —— 模板方法模式【Template Method Pattern】_java 模板模式 - CSDN 博客

在一个抽象类公开定义了执行它的方法的模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。简单说,模板方法模式,定义一个操作中的算法的骨架,而将一些步骤延迟到子类中,使得子类可以不改变一个算法的结构,就可以重定义该算法的某些特定步骤,这种类型的设计模式属于行为型模式。核心思想是 “封装不变,扩展可变

image

结构

  • 抽象类

    • 模板方法:定义算法骨架(通常用final修饰防止子类覆盖),包含基本方法和抽象方法。
    • 基本方法:通用步骤的实现(如boilWater()烧水),可直接继承或提供默认实现。
    • 抽象方法:必须由子类实现的步骤(如brew()冲泡咖啡)
    • 钩子方法(Hook Method):可选步骤(如customerWantsCondiments()是否加配料),子类可选择性重写。
  • 具体子类

    • 继承抽象类,实现抽象方法或重写钩子方法,提供具体逻辑(如Americano实现咖啡冲泡细节)。

实现

// 抽象类
public abstract class CoffeeMaker {
    // 模板方法(定义算法骨架)
    public final void makeCoffee() {
        boilWater();
        brew();
        pourInCup();
        if (needCondiments()) {
            addCondiments();
        }
    }
    // 基本方法(通用步骤)
    private void boilWater() { System.out.println("烧水"); }
    // 抽象方法(必须由子类实现)
    protected abstract void brew();
    // 钩子方法(可选步骤)
    protected boolean needCondiments() { return true; }
    protected void addCondiments() {} // 默认空实现
}

// 具体子类
public class Americano extends CoffeeMaker {
    @Override
    protected void brew() { System.out.println("冲泡美式咖啡"); }
    @Override
    protected boolean needCondiments() { return false; } // 不添加配料
}

策略模式#

策略模式详解 - 知乎

策略模式(Strategy Pattern)定义了一组同类型的算法,在不同的类中封装起来,每种算法可以根据当前场景相互替换,从而使算法的变化独立于使用它们的客户端(即算法的调用者)。

  • 算法封装以互相替换
  • 使算法独立于使用算法的用户

以用户支付时不同的支付方式为例,正常情况下需要判断用户使用哪种支付方式跳转至对应的支付页面。这样的缺点也很明显:

  • 扩展性差
  • 之后修改任何逻辑,当前方法都会被修改

超过 3 层的 if-else 的逻辑判断代码可以使用卫语句、策略模式、状态模式等来实现。

策略模式主要通过创建接口 **(定义策略接口),并对其加以不同的实现(实现策略接口)** 来提高代码的扩展性。

接口类只负责业务策略的定义,每个策略的具体实现单独放在实现类中,工厂类 Factory 只负责获取具体实现类,而具体调用代码则负责业务逻辑的编排。这些实现用到了面向接口而非实现编程,满足了职责单一、开闭原则,从而达到了功能上的高内聚低耦合、提高了可维护性、扩展性以及代码的可读性。

工厂模式创建

public class PaymentFactory {
 private static final Map<PayTypeEnum, Payment> payStrategies = new HashMap<>();

 static {
     payStrategies.put(PayTypeEnum.WX, new WxPayment());
     payStrategies.put(PayTypeEnum.ALIPAY, new AlipayPayment());
     payStrategies.put(PayTypeEnum.BANK_CARD, new BankCardPayment());
 }

 public static Payment getPayment(PayTypeEnum payType) {
     if (payType == null) {
         throw new IllegalArgumentException("pay type is empty.");
     }
     if (!payStrategies.containsKey(payType)) {
         throw new IllegalArgumentException("pay type not supported.");
     }
     return payStrategies.get(payType);
 }
}

使用

Order order = 订单信息
PayResult payResult = PaymentFactory.getPayment(payType).pay(order);
if (payResult == PayResult.SUCCESS) {
    System.out.println("支付成功");
} else if (payType == 支付宝) {
    System.out.println("支付失败");
}

观察者模式#

图说设计模式 — Graphic Design Patterns

观察者模式是一种软件设计模式,属于行为型模式之一。这种模式用于建立一种对象与对象之间的依赖关系,其中 ** 一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并自动更新。** 在此,发生改变的对象称为观察目标,而被通知的对象称为观察者,一个观察目标可以对应多个观察者,而且这些观察者之间没有相互联系,可以根据需要增加和删除观察者,使得系统更易于扩展,这就是观察者模式的模式动机。

在观察者模式中,有两个主要的角色:

  1. Subject: 目标(主题)
  2. ConcreteSubject: 具体目标,提供添加、删除、通知观察者的方法。当主题的状态发生变化时,它会遍历观察者列表,调用每个观察者的方法来通知它们。
  3. Observer: 观察者
  4. ConcreteObserver: 具体观察者,定义了一个更新方法,当主题状态改变时,所有依赖于该主题的观察者都会收到通知,并执行相应的操作。

../_images/Obeserver.jpg

实现

观察者模式(Observer) Java 实现_java implements observer-CSDN 博客

Subject 抽象主题角色类

public abstract class Subject {

    /**
     * 用来保存注册的观察者对象
     */
    private List<Observer> list = new ArrayList<>();

    /**
     * 注册观察者对象
     * @param observer 观察者对象
     */
    public void attach(Observer observer) {
        list.add(observer);
        System.out.println("Attached an observer");
    }

    /**
     * 移除观察者对象
     * @param observer 观察者对象
     */
    public void detach(Observer observer) {
        list.remove(observer);
    }

    /**
     * 通知所有注册的观察者对象
     */
    public void notifyObservers(String newState) {
        for (Observer observer : list) {
            observer.update(newState);
        }
    }
}

ConcreteSubject 具体主题

public class ConcreteSubject extends Subject {

    private String state;

    public String getState() {
        return state;
    }

    public void change(String newState) {
        state = newState;
        System.out.println("主题状态为:" + state);
        // 状态发生改变,通知各个观察者
        this.notifyObservers(state);
    }

}

Observer 抽象观察者角色类

public interface Observer {

    /**
     * 更新接口
     * @param state 更新的状态
     */
    void update(String state);

}

ConcreteObserver 具体观察者角色类

public class ConcreteObserver implements Observer {

    // 观察者状态
    private String observerState;

    @Override
    public void update(String state) {
        /**
         * 更新观察者的状态,使其与目标的状态保持一致
         */
        observerState = state;
        System.out.println("状态为:" + observerState);
    }
}

客户端类

image

public class Client {

    public static void main(String[] args) {
        // 创建主题对象
        ConcreteSubject subject = new ConcreteSubject();
        // 创建观察者对象
        Observer observer = new ConcreteObserver();
        // 将观察者对象注册到主题对象上
        subject.attach(observer);
        // 改变主题对象的状态
        subject.change("new state");
    }

}

优点

  • 实现表示层和数据逻辑层的分离,并定义了稳定的消息更新传递机制,抽象了更新接口,使得可以有各种各样不同的表示层作为具体观察者角色。
  • 在观察目标和观察者之间建立一个抽象的耦合。
  • 支持广播通信。
  • 符合 “开闭原则” 的要求。

缺点

  • 观察者众多时,将所有的观察者都通知到会花费很多时间。
  • 如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。
  • 没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。

应用

凡是涉及到一对一或者一对多的对象交互场景都可以使用观察者模式。

扩展#

MVC (Model-View-Controller)

MVC 模式是一种架构模式,它包含三个角色:模型 (Model),视图 (View) 和控制器 (Controller)。观察者模式可以用来实现 MVC 模式,观察者模式中的观察目标就是 MVC 模式中的模型 (Model),而观察者就是 MVC 中的视图 (View),控制器 (Controller) 充当两者之间的中介者 (Mediator)。当模型层的数据发生改变时,视图层将自动改变其显示内容。

  • Model:表示应用程序的数据层或业务逻辑层。它负责管理应用程序的状态,并直接与数据库或其他持久化存储交互。

  • View用户界面,它显示数据给用户并接收用户的输入。它通常只与 Controller 通信。

  • Controller:充当 Model 和 View 之间的中介。它接收用户的输入(例如通过 HTTP 请求),处理这些输入(可能涉及调用 Model 的方法),然后决定要显示哪个 View。

    移动开发中的 iOS Swift (UIKit) 和 Android (Activity/Fragment)

MVC、MVP、MVVM 简介

MVC,MVP 和 MVVM 的图示 - 阮一峰的网络日志

MVC (Model-View-Controller)

  • Model:表示应用程序的数据层或业务逻辑层。它负责管理应用程序的状态,并直接与数据库或其他持久化存储交互。

  • View用户界面,它显示数据给用户并接收用户的输入。它通常只与 Controller 通信。

  • Controller:充当 Model 和 View 之间的中介。它接收用户的输入(例如通过 HTTP 请求),处理这些输入(可能涉及调用 Model 的方法),然后决定要显示哪个 View。

    移动开发中的 iOS Swift (UIKit) 和 Android (Activity/Fragment)

image

MVVM (Model-View-ViewModel)

  • Model:如同前两种模式,负责数据和业务逻辑。

  • View:用户界面,它通过绑定机制与 ViewModel 进行交互。它不知道 ViewModel 的存在,只关心如何呈现数据

  • ViewModel:一个连接 Model 和 View 的桥梁。它暴露公共属性和命令给 View,并且通过数据绑定和命令绑定的方式与 View 互动。ViewModel 不知道具体的 View,因此可以更容易地实现单元测试。

    JavaScript 框架如 Vue.js, Angular, React(虽然 React 更倾向于组件化设计,但也可以配合状态管理库如 Redux 实现类似 MVVM 的模式)

image

两种模式的主要区别为
1、数据绑定:MVVM 的一个显著特点就是双向数据绑定,这使得数据的变化可以自动同步到 UI 上,同时也允许用户输入的数据直接反馈到数据模型中。而在 MVC 模式下,这种同步通常需要通过控制器来手动实现。
2、职责分离:虽然 MVC 和 MVVM 都强调了职责分离的原则,但是 MVVM 通过引 I 入 ViewModel 进一步减少了视图和模型之间的直接依赖,提高了代码的可测试性和可维护性。
3、视图与控制器的关系:在 MVC 中,视图和控制器之间有直接的交互,而 MVVM 中,视图主要与 ViewModel 交互,这减少了视图对业务逻辑的直接依赖,使得 UI 设计更加灵活。

加载中...
此文章数据所有权由区块链加密技术和智能合约保障仅归创作者所有。