원클릭으로
oop-collaborator-pattern
Prefer object collaborators over static utility helpers for domain behavior in McRPG.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
Prefer object collaborators over static utility helpers for domain behavior in McRPG.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
| name | oop-collaborator-pattern |
| description | Prefer object collaborators over static utility helpers for domain behavior in McRPG. |
final class with only static methods in non-DAO code.If behavior needs runtime context (player, manager, offering, scope, config), model it as an object collaborator with instance methods and explicit dependencies.
Slot, Listener, Manager) instead of static helpers.SomeClass.getInstance() or McRPG.getInstance() inside a static method IS a hidden dependency).getInstance() singletons for per-player or per-system state. Per-player state belongs on McRPGPlayer; per-system state should be a Manager in the registry.A common slip is writing a helper inside an already-stateful class and marking it static just because it happens not to read this:
Before (smell):
public class ExperienceRewardType implements QuestRewardType {
private final String skillName;
private final long amount;
// Static despite the class having instance state it logically belongs to.
// Becomes a hidden dependency once it needs a registry or manager.
private static String formatSkillName(@NotNull String raw) { ... }
private static String applyVars(@NotNull String template, @NotNull Map<String, String> vars) { ... }
}
After (correct):
public class ExperienceRewardType implements QuestRewardType {
private final String skillName;
private final long amount;
// Instance method — honest about its relationship to the class.
private String formatSkillName(@NotNull String raw) { ... }
// Cross-class plumbing belongs on the manager, not duplicated per type.
// localization.getLocalizedMessage(displayLabel, vars) replaces applyVars().
}
Key signals that a static helper should be an instance method (or pushed to a manager):
private and lives inside a class that already has instance fields.static compiles without any other change — the helper was never truly stateless in context.Key signal that a helper should move to a manager/service instead:
applyVars in four reward types → McRPGLocalizationManager.getLocalizedMessage(String, Map)).McRPGMethods). The moment the helper is private to a class with instance state, or appears in more than one class, it no longer qualifies.Before (anti-pattern):
// Static utility with hidden singleton dependency
public final class ComboDisplayManager {
private ComboDisplayManager() {}
public static void updateDisplay(Player player, List<ComboInput> sequence) {
Component display = buildDisplay(sequence, totalLength);
ActionBarCenterContent.getInstance().setPersistent(player.getUniqueId(), display);
}
}
// Singleton holding per-player state in a global Map
public final class ActionBarCenterContent {
private static final ActionBarCenterContent INSTANCE = new ActionBarCenterContent();
private final Map<UUID, Entry> entries = new ConcurrentHashMap<>();
public static ActionBarCenterContent getInstance() { return INSTANCE; }
}
After (collaborator pattern):
// Manager registered via McRPGManagerKey.COMBO — discoverable, injectable, testable
public class ComboManager extends Manager<McRPG> {
public void processInput(Player player, ComboInput input) { ... }
private void updateDisplay(McRPGPlayer player, List<ComboInput> sequence) {
ActionBarHudDisplay hud = displayManager.getOrCreateDisplay(
player,
ActionBarHudDisplay.class,
p -> new ActionBarHudDisplay(p, displayManager.getHudRenderer()));
hud.setSlot(CenterContentPriority.COMBO_STATE,
new IndefiniteCenterContent(buildComboDisplay(sequence, totalLength)));
}
}
// Per-player state lives on the player object — no global map, no cleanup needed
public class McRPGPlayer extends CorePlayer {
public <T extends PlayerDisplay> Optional<T> getDisplay(Class<T> type) { ... }
public <T extends PlayerDisplay> void setDisplay(Class<T> type, T display) { ... }
}
Key improvements: dependencies are explicit, per-player state lifecycle is automatic, third-party plugins can access ComboManager via registryAccess(), and unit tests pass collaborators directly instead of setting up global singletons.
getInstance() or McRPG.getInstance()? -> it has a hidden dependency; refactor to collaborator.