跳转至

20 常用设计模式

本章目标

  • 理解什么是设计模式
  • 掌握模组开发最常用的 6 个模式
  • 能在模组代码里识别出这些模式

20.1 什么是设计模式?

设计模式是前辈总结的常用代码组织套路,解决特定场景的经典问题。不是规定,是经验。

模组开发(Forge/NeoForge)里大量使用设计模式,看懂它们,读框架源码不迷路。

20.2 单例模式(Singleton)

问题:某个类只需要一个实例(比如模组管理器、配置加载器)。 解决:构造器私有,提供一个静态方法返回唯一实例。

public class ModManager {
    private static final ModManager INSTANCE = new ModManager();

    private ModManager() { }    // 私有构造器,外部不能 new

    public static ModManager getInstance() {
        return INSTANCE;
    }

    public void doSomething() { ... }
}

// 使用
ModManager.getInstance().doSomething();

模组里的单例

Forge 的 Minecraft.getInstance()、MinecraftForge.EVENT_BUS 都是单例思想。懒加载单例(第一次用时才创建)注意线程安全,一般用静态内部类方式。

20.3 工厂模式(Factory)

问题:创建对象逻辑复杂,或想隐藏具体实现类。 解决:用一个"工厂"方法负责创建。

public class ItemFactory {
    public static Item createItem(String type) {
        return switch (type) {
            case "sword" -> new SwordItem(...);
            case "pickaxe" -> new PickaxeItem(...);
            case "food" -> new FoodItem(...);
            default -> new Item(new Item.Properties());
        };
    }
}

// 使用:调用方不关心具体类
Item item = ItemFactory.createItem("sword");

模组里的注册表就是工厂思想的体现:Registry.register(Registries.ITEM, id, () -> new Item(...))。

20.4 建造者模式(Builder)

问题:对象构造参数太多,直接写构造器又长又容易错。 解决:链式调用,逐步设置参数。

public class ItemConfig {
    private final int damage;
    private final int durability;
    private final boolean unbreakable;
    private final String rarity;

    private ItemConfig(Builder b) {
        this.damage = b.damage;
        this.durability = b.durability;
        this.unbreakable = b.unbreakable;
        this.rarity = b.rarity;
    }

    public static class Builder {
        private int damage = 1;
        private int durability = 100;
        private boolean unbreakable = false;
        private String rarity = "COMMON";

        public Builder damage(int v) { this.damage = v; return this; }
        public Builder durability(int v) { this.durability = v; return this; }
        public Builder unbreakable(boolean v) { this.unbreakable = v; return this; }
        public Builder rarity(String v) { this.rarity = v; return this; }

        public ItemConfig build() { return new ItemConfig(this); }
    }
}

// 使用(链式)
ItemConfig config = new ItemConfig.Builder()
        .damage(8)
        .durability(500)
        .rarity("EPIC")
        .build();

模组里的 Builder

Forge 的 Item.Properties()、BlockBehaviour.Properties()、Component.literal() 链式写法都是 Builder 模式。你写 new Item(new Item.Properties().stacksTo(16)) 就是在用 Builder。

20.5 观察者模式(Observer)/ 事件驱动

问题:一个事情发生时,多个地方想响应(如"玩家加入"→ 更新排行榜 + 发欢迎语 + 记日志)。 解决:事件发布者不直接调用各方,而是"广播事件",订阅者各自处理。

// 事件类
public class PlayerJoinEvent {
    private final String playerName;
    public PlayerJoinEvent(String name) { this.playerName = name; }
    public String getPlayerName() { return playerName; }
}

// 事件总线(简化版)
public class EventBus {
    private final List<Consumer<PlayerJoinEvent>> listeners = new ArrayList<>();

    public void register(Consumer<PlayerJoinEvent> listener) {
        listeners.add(listener);
    }

    public void post(PlayerJoinEvent event) {
        listeners.forEach(l -> l.accept(event));
    }
}

// 使用:各处订阅
EventBus bus = new EventBus();
bus.register(e -> System.out.println("排行榜更新: " + e.getPlayerName()));
bus.register(e -> System.out.println("欢迎 " + e.getPlayerName() + " 加入!"));

// 触发事件
bus.post(new PlayerJoinEvent("小明"));

这就是 Forge 事件系统!

MinecraftForge.EVENT_BUS.register(...) + @SubscribeEvent 就是标准的观察者模式。模组开发天天用。

20.6 策略模式(Strategy)

问题:同一件事有多种做法,运行时切换。 解决:把算法封装成接口,运行时传入具体实现。

public interface DamageCalculator {
    int calculate(int base, int level);
}

// 策略一:普通伤害
class NormalDamage implements DamageCalculator {
    public int calculate(int base, int level) { return base + level / 2; }
}

// 策略二:暴击伤害
class CritDamage implements DamageCalculator {
    public int calculate(int base, int level) { return (base + level) * 2; }
}

// 使用
public class Weapon {
    private DamageCalculator strategy;

    public Weapon(DamageCalculator strategy) { this.strategy = strategy; }

    public void setStrategy(DamageCalculator strategy) { this.strategy = strategy; }

    public int getDamage(int base, int level) {
        return strategy.calculate(base, level);
    }
}

Weapon w = new Weapon(new NormalDamage());
w.getDamage(10, 20);        // 20
w.setStrategy(new CritDamage());
w.getDamage(10, 20);        // 60

20.7 适配器模式(Adapter)

问题:接口不兼容,让一个类"适配"成另一个接口。 解决:写个中间类包装。

// 旧接口
interface OldSystem { void oldMethod(); }
// 新接口
interface NewSystem { void newMethod(); }

// 适配器
class Adapter implements NewSystem {
    private final OldSystem old;
    Adapter(OldSystem old) { this.old = old; }
    public void newMethod() { old.oldMethod(); }   // 内部转调旧实现
}

模组开发里为了兼容老版本 API、兼容其他模组,适配器很常见。

20.8 实战:模组场景

// 综合:物品注册 + 单例 + Builder(贴近真实 Forge 写法)
public class ModItems {
    public static final Item STEEL_SWORD = new SwordItem(
            Tiers.DIAMOND, 3, -2.4F,
            new Item.Properties().stacksTo(1).rarity(Rarity.EPIC)   // Builder 模式
    );
}

// 事件订阅(观察者模式)
@Mod.EventBusSubscriber(modid = FfsMod.MODID)
public class ModEvents {
    @SubscribeEvent
    public static void onJoin(PlayerEvent.PlayerLoggedInEvent event) {
        // 响应玩家加入事件
    }
}

20.9 小练习

  1. 实现单例 Logger,带 info/warn 两个方法
  2. 用 Builder 模式写一个 ServerSettings(名称/人数上限/难度)
  3. 模拟一个简单事件总线,订阅 3 个监听器,触发 1 次事件
  4. 用策略模式实现"打折":普通会员 9 折、VIP 7 折

本章小结

  • 单例:全局唯一实例(模组管理器)
  • 工厂:集中创建对象(注册表)
  • Builder:链式配置参数(Item.Properties)
  • 观察者/事件:发布-订阅(Forge 事件系统)
  • 策略:运行时切换算法(伤害计算)
  • 适配器:兼容不同接口

下一章,进入第三部分,开始衔接 Forge 模组开发!21 构建工具 Gradle 入门

🏠 返回总站