从 class 访问 class 即 属性

Access to class from class which is property

我正在 canvas 使用 Typescript 玩多人游戏。我有一个玩家 class,属性 是一个 class,拥有玩家统计数据和装备 class。

class Player {
    statistics = new Stats()
    eq = new Equipment()
}

class Stats {
    strength = 3
    ...
}
class Equipment {
    'items which have parameters like +3 strength etc.'
}

所以我有来自 Stats 的基本统计数据和来自 Equipment 的附加统计数据,我需要将它们结合起来,但是 Player class 主要描述有关 2d 世界中玩家位置的信息,他在哪张地图上,图像等。我应该把结合统计数据的方法放在 Player class 中吗?是否允许通过 ES6 导入将 class 实例导出到另一个 class 并在那里使用它?如果我需要在数据或装备上获得一名玩家 属性 怎么办?我应该将播放器传递给构造函数吗?

将 class 的实例作为另一个 class 的 属性 的实例是完全正常的。

What if I need to get one of player property in stats or equipment? Should I pass player to constructor?

将另一个 class 的实例传递给 class 构造函数也是完全正常的。但是我建议不要建立 bi-directional 关系。 PlayerEquipmentEquipmentPlayer,但我不会两者都做。如果您需要获取一些依赖于两个 class 实例的信息,您可以从 has 另一个实例中获取。根据您上面的代码,那将是 Player.

例如,如果您想获得特定的统计数据 defense,我们将玩家的基础防御从装备中添加一些提升,可能如下所示:

getDefense(): number {
    return this.statistics.defense + this.equipment.stats.defense;
}

也许我们不想为每个单独的统计数据创建一个方法,所以我们可以创建一个通用方法来按名称获取统计数据。我们使用 keyof Stats 来确保所有名称字符串都有效。

class Player {
    private statistics: Stats;
    private eq: Equipment;

    constructor( baseStats: Stats, equipment: Equipment ) {
        this.statistics = baseStats;
        this.eq = equipment;
    }

    public changeEquipment( equipment: Equipment ): void {
        this.eq = equipment;
    }

    public getStat( name: keyof Stats ): number {
        return this.statistics[name] + this.eq.stats[name];
    }
}

我们可以做的另一件事是将统计数据公开为 public readonly 属性。这就是我在 Equipment 中所做的。我们不希望从 class 外部编辑统计数据,但我们不能只创建 属性 readonly 因为我们希望能够在调用时编辑统计数据levelUp().

您可以同时拥有变量的 public 和私有版本,使用 _ 作为私有版本的前缀,这样数据就可以 public 可读,但只能在内部编辑class。从外部编辑它的唯一方法是调用 class 方法。

class Equipment {
    
    private _stats: Stats;
    public readonly name: string;

    constructor( name: string, baseStats: Stats ) {
        this.name = name;
        this._stats = baseStats;
    }

    /**
     * readonly public accessor
     */
    get stats(): Stats {
        return this._stats;
    }

    /**
     * increase all stats by 1
     */
    levelUp(): void {
        Object.keys(this._stats).forEach( key => {
            this._stats[key as keyof Stats] += 1;
        })
    }
}

根据您描述的方式 Stats,我认为它应该只是一个 interface 而不是 class,因为它只是一堆数字。当您想要拥有方法或拥有相互依赖的属性时,请使用 classes。

interface Stats {
    strength: number;
    defense: number;
    hp: number;
}

但是如果你想要一个实现该接口的 class,那么可以将那个 class 的实例传递给 PlayerEquipment 的构造函数不会有问题的。这种灵活性造就了良好的设计。

Playground Link