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

Java实现Logo语言时如何优雅完成多指令的分支切换

基于函数式组件的指令分发实现方案

抛弃if/else和switch硬编码分支,用指令注册表+流式匹配链实现即可,完全覆盖所有Logo指令场景,且全程用Stream、Predicate、Function等JDK原生函数式组件,无额外复杂依赖,扩展成本极低。

核心抽象定义

先定义运行时上下文和函数式约定,所有组件遵循单一职责:

import java.awt.*;
import java.util.List;
import lombok.Data;

// 运行时上下文,存储画笔、画布、坐标等全量状态
@Data
public class LogoContext {
    private int x, y;
    private int angle;
    private boolean penDown = true;
    private Color penColor = Color.BLACK;
    private Color fillColor = Color.WHITE;
    private Color screenColor = Color.WHITE;
    private int penSize = 1;
    private final List<Shape> renderedShapes = new ArrayList<>();
}

// 指令执行器:接收解析后的参数、上下文,执行对应操作
@FunctionalInterface
public interface LogoCommandHandler {
    void execute(List<Object> parsedArgs, LogoContext context);
}

// 指令匹配规则:判断当前字符串片段是否匹配目标指令
@FunctionalInterface
public interface CommandMatcher extends Predicate<String> {}

// 参数解析器:从输入串当前位置解析参数,返回解析后的游标位置和参数列表
@FunctionalInterface
public interface ArgsParser extends Function<String, ParseResult> {}

// 解析结果封装
record ParseResult(int nextCursor, List<Object> args){}

// 可执行指令单元
@FunctionalInterface
public interface LogoExecutable {
    void run(LogoContext context);
}

指令注册表实现

所有指令的匹配规则、参数解析逻辑、执行逻辑都提前注册到统一注册表,初始化时一次性加载,运行时不需要做分支判断:

import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.IntStream;

public class LogoCommandRegistry {
    private final List<CommandEntry> commandEntries = new ArrayList<>();

    public LogoCommandRegistry() {
        // 注册无参数指令
        registerNoArg("CLEARSCREEN", (args, ctx) -> {
            ctx.getRenderedShapes().clear();
            ctx.setX(0);
            ctx.setY(0);
            ctx.setAngle(0);
        });
        registerNoArg("HOME", (args, ctx) -> {
            ctx.setX(0);
            ctx.setY(0);
            ctx.setAngle(0);
        });
        registerNoArg("PENUP", (args, ctx) -> ctx.setPenDown(false));
        registerNoArg("PENDOWN", (args, ctx) -> ctx.setPenDown(true));

        // 注册单int参数指令
        registerSingleInt("FORWARD", (args, ctx) -> {
            int step = (int) args.get(0);
            // 按当前角度计算新坐标,penDown为真时追加线段到渲染列表
        });
        registerSingleInt("BACKWARD", (args, ctx) -> {
            int step = (int) args.get(0);
            // 反向计算坐标、绘制逻辑
        });
        registerSingleInt("LEFT", (args, ctx) -> {
            int delta = (int) args.get(0);
            ctx.setAngle((ctx.getAngle() - delta + 360) % 360);
        });
        registerSingleInt("RIGHT", (args, ctx) -> {
            int delta = (int) args.get(0);
            ctx.setAngle((ctx.getAngle() + delta) % 360);
        });
        registerSingleInt("SETPENSIZE", (args, ctx) -> ctx.setPenSize((int) args.get(0)));

        // 注册三int(RGB)参数指令
        registerTripleInt("SETPENCOLOR", (args, ctx) ->
                ctx.setPenColor(new Color((int)args.get(0), (int)args.get(1), (int)args.get(2)))
        );
        registerTripleInt("SETFILLCOLOR", (args, ctx) ->
                ctx.setFillColor(new Color((int)args.get(0), (int)args.get(1), (int)args.get(2)))
        );
        registerTripleInt("SETSCREENCOLOR", (args, ctx) ->
                ctx.setScreenColor(new Color((int)args.get(0), (int)args.get(1), (int)args.get(2)))
        );

        // 注册RIPETI重复指令,天然支持嵌套
        commandEntries.add(new CommandEntry(
                s -> s.trim().startsWith("RIPETI"),
                s -> {
                    Matcher matcher = Pattern.compile("RIPETI\\s+(\\d+)\\s*\\[([\\s\\S]*)]").matcher(s.trim());
                    matcher.find();
                    int repeatTimes = Integer.parseInt(matcher.group(1));
                    String innerCmdStr = matcher.group(2);
                    // 递归解析内部指令块
                    List<LogoExecutable> innerCmds = new LogoExecutor(this).parse(innerCmdStr);
                    return new ParseResult(s.length(), List.of(repeatTimes, innerCmds));
                },
                (args, ctx) -> {
                    int times = (int) args.get(0);
                    List<LogoExecutable> innerCmds = (List<LogoExecutable>) args.get(1);
                    IntStream.range(0, times).forEach(i -> innerCmds.forEach(cmd -> cmd.run(ctx)));
                }
        ));
    }

    // 无参指令注册通用方法
    private void registerNoArg(String cmdName, LogoCommandHandler handler) {
        commandEntries.add(new CommandEntry(
                s -> s.trim().startsWith(cmdName),
                s -> new ParseResult(s.trim().indexOf(cmdName) + cmdName.length(), Collections.emptyList()),
                handler
        ));
    }

    // 单int参数指令注册通用方法
    private void registerSingleInt(String cmdName, LogoCommandHandler handler) {
        commandEntries.add(new CommandEntry(
                s -> s.trim().startsWith(cmdName),
                s -> {
                    Matcher matcher = Pattern.compile(cmdName + "\\s+(-?\\d+)").matcher(s.trim());
                    matcher.find();
                    int arg = Integer.parseInt(matcher.group(1));
                    return new ParseResult(matcher.end(), List.of(arg));
                },
                handler
        ));
    }

    // 三int参数指令注册通用方法
    private void registerTripleInt(String cmdName, LogoCommandHandler handler) {
        commandEntries.add(new CommandEntry(
                s -> s.trim().startsWith(cmdName),
                s -> {
                    Matcher matcher = Pattern.compile(cmdName + "\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)").matcher(s.trim());
                    matcher.find();
                    int r = Integer.parseInt(matcher.group(1));
                    int g = Integer.parseInt(matcher.group(2));
                    int b = Integer.parseInt(matcher.group(3));
                    return new ParseResult(matcher.end(), List.of(r, g, b));
                },
                handler
        ));
    }

    public List<CommandEntry> getCommandEntries() {
        return commandEntries;
    }

    record CommandEntry(CommandMatcher matcher, ArgsParser parser, LogoCommandHandler handler){}
}

流式解析执行逻辑

解析过程全程用Stream流匹配对应指令,完全替代硬编码分支:

import java.util.ArrayList;
import java.util.List;

public class LogoExecutor {
    private final LogoCommandRegistry registry;

    public LogoExecutor(LogoCommandRegistry registry) {
        this.registry = registry;
    }

    public List<LogoExecutable> parse(String input) {
        List<LogoExecutable> executables = new ArrayList<>();
        String remaining = input.trim();
        while (!remaining.isEmpty()) {
            String currentSegment = remaining;
            // 流式查找匹配的指令,无任何if/switch分支
            LogoCommandRegistry.CommandEntry matched = registry.getCommandEntries().stream()
                    .filter(entry -> entry.matcher().test(currentSegment))
                    .findFirst()
                    .orElseThrow(() -> new IllegalArgumentException("无法识别的指令: " + currentSegment));

            ParseResult parseResult = matched.parser().apply(remaining);
            executables.add(ctx -> matched.handler().execute(parseResult.args(), ctx));
            remaining = remaining.substring(parseResult.nextCursor()).trim();
        }
        return executables;
    }

    public void execute(String input, LogoContext context) {
        parse(input).forEach(cmd -> cmd.run(context));
    }
}

方案特性

  • 完全满足函数式编程要求:核心逻辑全部基于Predicate、Function、Stream、IntStream实现,没有冗余的分支判断
  • 符合开闭原则:新增指令只需要在注册表中添加对应注册项,不需要修改核心解析分发逻辑
  • 嵌套支持天然实现:RIPETI指令通过递归解析即可支持多层嵌套,不需要额外编写复杂的栈式状态机
  • 测试成本低:每个指令的匹配、解析、执行逻辑都是独立的函数单元,可单独做单元测试

内容的提问来源于stack exchange,提问作者anthony eleuteri

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 10:01:08