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

基于SOLID DIP原则的游戏实体接口复用方案咨询:通用实体基类设计是否合理?

Is Inheriting from a Common StdEntity Class a Valid Approach?

Great question—let’s break this down from both a SOLID compliance and practical maintenance perspective.

Your Initial Approach is Absolutely Valid

First off: yes, creating a StdEntity base class that implements the shared getATK(), setATK(), getDEF(), setDEF() logic, then having StdPlayer inherit from it and implement IPlayer, and StdMonster do the same with IMonster, is a totally reasonable and sound design. Here’s why it works well with your existing setup:

  • Follows DRY Principle: You eliminate duplicate code for the core stats logic, so if you ever need to adjust how ATK/DEF are handled (e.g., add validation, tie them to a formula), you only have to modify one place (StdEntity) instead of two.
  • Preserves DIP Compliance: Your commands like atkPotion(IEntity), expPotion(IPlayer), etc., still depend on abstractions (the interfaces), not concrete classes. StdPlayer and StdMonster are just implementations of those interfaces, so you’re not violating the dependency inversion principle at all.
  • Single Responsibility: StdEntity handles the core entity stats, while StdPlayer focuses solely on player-specific EXP logic, and StdMonster handles monster-specific element logic. This keeps each class focused on one job, aligning with the single responsibility principle.

Here’s what this implementation might look like in code:

// Common base class for shared stats logic
public class StdEntity implements IEntity {
    protected int atk;
    protected int def;

    @Override
    public int getATK() {
        return atk;
    }

    @Override
    public void setATK(int atk) {
        this.atk = atk;
        // You could add validation here later without touching Player/Monster
    }

    @Override
    public int getDEF() {
        return def;
    }

    @Override
    public void setDEF(int def) {
        this.def = def;
    }
}

// Player implementation
public class StdPlayer extends StdEntity implements IPlayer {
    private int exp;

    @Override
    public void setEXP(int exp) {
        this.exp = exp;
    }

    @Override
    public int getEXP() {
        return exp;
    }
}

// Monster implementation
public class StdMonster extends StdEntity implements IMonster {
    private String element;

    @Override
    public void setElement(String element) {
        this.element = element;
    }

    @Override
    public String getElement() {
        return element;
    }
}

A More Flexible Alternative: Composition Over Inheritance

While your inheritance approach works great for your current needs, if you anticipate future changes to your entity system (e.g., adding entities with different stat logic, or allowing dynamic stat changes), a composition-based approach might be more robust.

Instead of inheriting the stats logic, you’d encapsulate it in a separate component class, then have StdPlayer and StdMonster hold an instance of this component and delegate the IEntity method calls to it. This follows the "composition over inheritance" principle, which avoids potential issues with rigid inheritance hierarchies down the line.

Example of this approach:

// Reusable stats component
public class BaseStats implements IEntity {
    private int atk;
    private int def;

    @Override
    public int getATK() { return atk; }
    @Override
    public void setATK(int atk) { this.atk = atk; }
    @Override
    public int getDEF() { return def; }
    @Override
    public void setDEF(int def) { this.def = def; }
}

// Player using composition
public class StdPlayer implements IPlayer {
    private IEntity statsComponent;
    private int exp;

    public StdPlayer() {
        // Use the base stats by default
        this.statsComponent = new BaseStats();
    }

    // Delegate IEntity methods to the component
    @Override
    public int getATK() { return statsComponent.getATK(); }
    @Override
    public void setATK(int atk) { statsComponent.setATK(atk); }
    @Override
    public int getDEF() { return statsComponent.getDEF(); }
    @Override
    public void setDEF(int def) { statsComponent.setDEF(def); }

    // Player-specific logic
    @Override
    public void setEXP(int exp) { this.exp = exp; }
    @Override
    public int getEXP() { return exp; }
}

// Monster using composition
public class StdMonster implements IMonster {
    private IEntity statsComponent;
    private String element;

    public StdMonster() {
        this.statsComponent = new BaseStats();
    }

    @Override
    public int getATK() { return statsComponent.getATK(); }
    @Override
    public void setATK(int atk) { statsComponent.setATK(atk); }
    @Override
    public int getDEF() { return statsComponent.getDEF(); }
    @Override
    public void setDEF(int def) { statsComponent.setDEF(def); }

    @Override
    public void setElement(String element) { this.element = element; }
    @Override
    public String getElement() { return element; }
}

The biggest advantage here is flexibility: if you later need a BossMonster with a custom stat calculation (e.g., ATK scales with health), you can create a BossStats class that implements IEntity and swap it into BossMonster without modifying the base StdMonster or StdEntity code. This aligns with the open/closed principle (open for extension, closed for modification).

Final Recommendation

  • Stick with inheritance if your stat logic is stable, and you don’t foresee needing multiple variations of ATK/DEF behavior anytime soon. It’s simple, clean, and fits your current requirements perfectly.
  • Switch to composition if you expect your entity system to grow in complexity (e.g., adding unique stat types, dynamic stat modifiers). It’s a bit more code upfront but pays off in maintainability later.

Either way, both approaches keep your code compliant with the dependency inversion principle, since all your commands still depend on the abstract interfaces rather than concrete classes.

内容的提问来源于stack exchange,提问作者Juan Andres Flores Gutierrez

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 13:17:46