You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在不违反接口隔离原则(ISP)下实现自动售货机的状态设计模式?

解决自动售货机状态模式违反接口隔离原则的方案

你的问题核心是单一大State接口强迫所有状态类实现不需要的方法,违反了接口隔离原则(ISP)——客户端不应该依赖它不需要的接口。下面是重构方案,通过拆分细粒度接口、按需实现来解决这个问题。

一、重构思路:拆分细粒度接口

将原有庞大的State接口拆分为多个职责单一的小接口,每个接口对应一组相关行为,具体状态类只实现自己需要的接口,避免冗余的异常实现。

拆分后的接口定义

// 顶层标记接口,统一状态类型
public interface State {}

// 处理按钮触发行为的接口
public interface Triggerable extends State {
    void clickOnInsertCoinButton(VendingMachine machine) throws Exception;
    void clickOnStartProductSelectionButton(VendingMachine machine) throws Exception;
}

// 处理交易相关行为的接口
public interface Transactional extends State {
    void insertCoin(VendingMachine machine, Coin coin) throws Exception;
    void chooseProduct(VendingMachine machine, int codeNumber) throws Exception;
    int getChange(int returnChangeMoney) throws Exception;
    Item dispenseProduct(VendingMachine machine, int codeNumber) throws Exception;
    List<Coin> refundFullMoney(VendingMachine machine) throws Exception;
}

// 处理库存管理的接口
public interface InventoryManageable extends State {
    void updateInventory(VendingMachine machine, Item item, int codeNumber) throws Exception;
}

二、具体状态类的实现

每个状态类仅实现自身职责对应的接口,无需处理无关方法:

1. IdleState(空闲状态)

空闲状态支持触发投币按钮、更新库存,无需处理交易行为:

public class IdleState implements Triggerable, InventoryManageable {

    public IdleState(){
        System.out.println("当前自动售货机处于空闲状态");
    }

    public IdleState(VendingMachine machine){
        System.out.println("当前自动售货机处于空闲状态");
        machine.setCoinList(new ArrayList<>());
    }

    @Override
    public void clickOnInsertCoinButton(VendingMachine machine) throws Exception{
        machine.setVendingMachineState(new HasMoneyState());
    }

    @Override
    public void clickOnStartProductSelectionButton(VendingMachine machine) throws Exception {
        throw new Exception("请先点击投币按钮");
    }

    @Override
    public void updateInventory(VendingMachine machine, Item item, int codeNumber) throws Exception {
        machine.getInventory().addItem(item, codeNumber);
    }
}

2. HasMoneyState(已投币状态)

已投币状态支持触发选品按钮、交易相关行为:

public class HasMoneyState implements Triggerable, Transactional {

    public HasMoneyState() {
        System.out.println("当前自动售货机处于已投币状态");
    }

    @Override
    public void clickOnInsertCoinButton(VendingMachine machine) throws Exception {
        // 已投币状态下可以继续投币,无需切换状态
        System.out.println("可继续投币");
    }

    @Override
    public void clickOnStartProductSelectionButton(VendingMachine machine) throws Exception {
        machine.setVendingMachineState(new SelectionState());
    }

    @Override
    public void insertCoin(VendingMachine machine, Coin coin) throws Exception {
        machine.getCoinList().add(coin);
        System.out.println("已投入硬币:" + coin.getValue());
    }

    @Override
    public void chooseProduct(VendingMachine machine, int codeNumber) throws Exception {
        throw new Exception("请先点击选品开始按钮");
    }

    @Override
    public int getChange(int returnChangeMoney) throws Exception {
        throw new Exception("未完成商品选择,无法找零");
    }

    @Override
    public Item dispenseProduct(VendingMachine machine, int codeNumber) throws Exception {
        throw new Exception("未完成商品选择,无法出货");
    }

    @Override
    public List<Coin> refundFullMoney(VendingMachine machine) throws Exception {
        List<Coin> refund = machine.getCoinList();
        machine.setCoinList(new ArrayList<>());
        machine.setVendingMachineState(new IdleState(machine));
        return refund;
    }
}

3. SelectionState(选品状态)

选品状态仅需处理交易行为:

public class SelectionState implements Transactional {

    public SelectionState() {
        System.out.println("当前自动售货机处于选品状态");
    }

    @Override
    public void insertCoin(VendingMachine machine, Coin coin) throws Exception {
        throw new Exception("选品状态下无法投币");
    }

    @Override
    public void chooseProduct(VendingMachine machine, int codeNumber) throws Exception {
        Item item = machine.getInventory().getItem(codeNumber);
        // 校验金额、库存逻辑...
        machine.setVendingMachineState(new DispensedState(machine, codeNumber));
    }

    @Override
    public int getChange(int returnChangeMoney) throws Exception {
        // 计算找零逻辑
        return returnChangeMoney;
    }

    @Override
    public Item dispenseProduct(VendingMachine machine, int codeNumber) throws Exception {
        throw new Exception("请先选择商品");
    }

    @Override
    public List<Coin> refundFullMoney(VendingMachine machine) throws Exception {
        List<Coin> refund = machine.getCoinList();
        machine.setCoinList(new ArrayList<>());
        machine.setVendingMachineState(new IdleState(machine));
        return refund;
    }
}

三、调整后的上下文类(VendingMachine)

上下文类保持状态的统一管理,对外暴露操作方法,内部根据当前状态的接口类型委托调用:

public class VendingMachine {

    private State vendingMachineState;
    private Inventory inventory;
    private List<Coin> coinList;

    public VendingMachine(){
        vendingMachineState = new IdleState();
        inventory = new Inventory(10);
        coinList = new ArrayList<>();
    }

    // 对外暴露的投币按钮点击方法
    public void clickInsertCoinButton() throws Exception {
        if (vendingMachineState instanceof Triggerable) {
            ((Triggerable) vendingMachineState).clickOnInsertCoinButton(this);
        } else {
            throw new Exception("当前状态不支持此操作");
        }
    }

    // 对外暴露的投币方法
    public void insertCoin(Coin coin) throws Exception {
        if (vendingMachineState instanceof Transactional) {
            ((Transactional) vendingMachineState).insertCoin(this, coin);
        } else {
            throw new Exception("当前状态不支持投币");
        }
    }

    // 其他对外方法同理:clickProductSelectionButton、chooseProduct等

    // getter和setter方法
    public State getVendingMachineState() {
        return vendingMachineState;
    }

    public void setVendingMachineState(State vendingMachineState) {
        this.vendingMachineState = vendingMachineState;
    }

    public Inventory getInventory() {
        return inventory;
    }

    public void setInventory(Inventory inventory) {
        this.inventory = inventory;
    }

    public List<Coin> getCoinList() {
        return coinList;
    }

    public void setCoinList(List<Coin> coinList) {
        this.coinList = coinList;
    }
}

四、项目结构示例

按职责划分包结构,保持代码清晰:

src/
├── com/
│   └── vendingmachine/
│       ├── model/           # 模型类
│       │   ├── VendingMachine.java
│       │   ├── Inventory.java
│       │   ├── Coin.java
│       │   └── Item.java
│       └── state/           # 状态相关接口和实现类
│           ├── State.java
│           ├── Triggerable.java
│           ├── Transactional.java
│           ├── InventoryManageable.java
│           ├── IdleState.java
│           ├── HasMoneyState.java
│           ├── SelectionState.java
│           └── DispensedState.java

内容的提问来源于stack exchange,提问作者Samarth Mehrotra

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 12:18:27