It is totally normal to have an instance of a class as a property of another class.
What if I need to get one of player property in stats or equipment? Should I pass player to constructor?
Passing an instance of another class into a class constructor is also totally normal. However I would advise against bi-directional relationships. A Player
has Equipment
OR Equipment
has a Player
, but I would not do both. If you need to get some piece of information which depends on both class instances, you would get that from the one which has
the other. Based on your code above, that would be the Player
.
For example, if you want to get a specific stat defense
where we take the player’s base defense add some boost from the equipment, that might look like this:
getDefense(): number {
return this.statistics.defense + this.equipment.stats.defense;
}
Maybe we don’t want to have to create a method for each individual stat, so we could create a general method that gets a stat by name. We use keyof Stats
as a way to ensure that all name strings are valid.
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];
}
}
Another thing we can do is expose the stats as a public readonly
property. That’s what I’ve done in Equipment
. We don’t want the stats to be edited from outside of the class, but we can’t just make the property readonly
because we want to be able to edit the stats when calling levelUp()
.
You can have both public and private versions of a variable, using _
to prefix the private version, so that data can be publicly readable but is only editable within the class. The only way to edit it from outside is through calling class methods.
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;
})
}
}
Based on the way you’ve described Stats
, I think it should just be an interface
rather than a class
since it’s just a bunch of numbers. Use classes when you want to have methods, or when you have properties which depend on each other.
interface Stats {
strength: number;
defense: number;
hp: number;
}
But if you wanted to have a class that implements that interface, so could pass in instance of that class to the constructor of either Player
or Equipment
and there would be no issue. That flexibility makes good design.
CLICK HERE to find out more related problems solutions.