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 小练习¶
- 实现单例 Logger,带 info/warn 两个方法
- 用 Builder 模式写一个
ServerSettings(名称/人数上限/难度) - 模拟一个简单事件总线,订阅 3 个监听器,触发 1 次事件
- 用策略模式实现"打折":普通会员 9 折、VIP 7 折
本章小结¶
- 单例:全局唯一实例(模组管理器)
- 工厂:集中创建对象(注册表)
- Builder:链式配置参数(Item.Properties)
- 观察者/事件:发布-订阅(Forge 事件系统)
- 策略:运行时切换算法(伤害计算)
- 适配器:兼容不同接口
下一章,进入第三部分,开始衔接 Forge 模组开发!21 构建工具 Gradle 入门