Shniper
Shniper

Reputation: 854

Using object inheritance when both objects pass arguments

I have a Equipment parent class which takes in args and two children Weapon and Armor which also take args. I'm not sure if there is a special way to target prototypes or if my code actually isn't working but here is a shortened DEMO

I need to create the variables used for the arguments in each object based on the value of other variables as well as an algorithm that uses a random number. Each item is unique so I need to make the hp for equipment at the same time as the damage for weapons and I'm not sure how to do that.

function Equipment(hp) {
  var self = this;
  this.hp = hp;
}
//create subclass for weapons
function Weapon(baseDam) {
  var self = this;
  this.baseDam = baseDam;
}

function generateEquipment() {
  hp = Math.round(Math.random() * 10);
  baseDam = Math.round(Math.random() * 50);
  Weapon.prototype = new Equipment(hp);
  weapon = new Weapon(baseDam);
  stringed = JSON.stringify(weapon);
  alert(stringed);
}

generateEquipment();

Upvotes: 0

Views: 50

Answers (2)

user3297291
user3297291

Reputation: 23372

The 'pattern' you're describing is called 'Composition' and can be very powerful. There're many different ways of combining/composing new classes or objects.

Reading your question and comments, it seems to me that you're mainly interested in defining many different types of equipment without too much (repeated) code.

Have you thought about passing an array of class names to your generateEquipment method and returning a new, custom constructor? Here's an example:

function Equipment(hp) {
  this.hp = Math.round(hp);
}

Equipment.prototype.describe = function() {
  return "This piece of equipment has " + this.hp + " hitpoints";
}

function Weapon(baseDam) {
  this.baseDam = Math.round(baseDam);
}

Weapon.prototype.describe = function() {
  return "The weapon does " + this.baseDam + " damage";
}

function generateCustomEquipment(types) {
  var CustomEquipment = function() {
    var self = this;

    // Create the properties for all types
    types.forEach(function(type) {
      type.call(self, Math.random() * 100);
    });
  };

  CustomEquipment.prototype.describe = function() {
    var self = this;

    // Combine the 'describe' methods of all composed types
    return types
      .map(function(type) {
        return type.prototype.describe.call(self);
      })
      .join(". ");
  }

  return CustomEquipment;
}

var Sword = generateCustomEquipment([Equipment, Weapon]);
var Armor = generateCustomEquipment([Equipment]);
var Arrow = generateCustomEquipment([Weapon]);

var sword1 = new Sword();
document.writeln("A sword: " + sword1.describe() + "<br/>");

var armor1 = new Armor();
document.writeln("A piece of armor: " + armor1.describe() + "<br/>");

var arrow1 = new Arrow();
document.writeln("An arrow: " + arrow1.describe() + "<br/>");

Upvotes: 0

Soham Kamani
Soham Kamani

Reputation: 552

First, the answer to your question : Your code is not really wrong, and your weapon still has its hp, except that its contained in the objects prototype, so won't show when stringified. There are ways to get around this, like I've shown here, but this according to me is not the correct way to do it.

Normally, prototypes should only store methods and not instance variables, because if you later decide to modify the prototype, the instance variable will get modified as well ,in case it is passed by reference.

A better pattern would be to use Object.assign - it is the easiest to understand and feels most natural. Further, if you expect Weapon to be a subclass of equipment, that logic should be encapsulated in Weapon itself.

Here is the proposed new way of declaring your Weapon Class :

function Weapon(baseDam) {
  var self = this;
  var hp = Math.round(Math.random() * 10);
  Object.assign(self, Equipment.prototype, new Equipment(hp));
  this.baseDam = baseDam;
}

Since hp is also generated randomly, that logic is now encapsulated in Weapon. This is also scalable, as this pattern will work for long inheritence chains as well.

Some people may recommend ES6 classes, which is also an approach that would work, but in my opinion it is syntactical sugar, which hides most of the inner workings of your code.

Here is a working demo with my approach.

Upvotes: 1

Related Questions