patternjavascriptMinor
JavaScript (multiple) inheritance / mixins
Viewed 0 times
javascriptinheritancemixinsmultiple
Problem
I'm quite new to JavaScript, and I'm trying to understand the idea behind inheritance in JavaScript. I've read a bit about it, and I don't want to mess with prototypes (which I don't yet fully understand) so, I wrote this code using the
```
var xtend = require("xtend");
// Human constructor.
// Human is a base class, it can introduce itself, and knows it's age.
function createHuman(name, age) {
function getName() {
return name;
}
function getAge() {
return age;
}
return {
getName : getName,
getAge : getAge,
};
}
// Boy constructor
// Boy is a Human
// Boy can play football (but not yet)
function createBoy(name, age) {
function playFootball() {
console.warn("Football skills not implemented yet, but trying", "-", this.getName());
// TODO: implement this
}
return xtend(createHuman(name, age), {
playFootball: playFootball,
})
}
// Girl constructor
// Girl is a Human
// Girl can sing (well, almost)
function createGirl(name, age) {
function singASong() {
console.warn("Singing not implemented yet, but trying", "-", this.getName());
// TODO: implement this
}
return xtend(createHuman(name, age), {
singASong: singASong,
})
}
// Hermaphrodite constructor
// Hermaphrodite is a Girl and Hermaphrodite is a Boy, so we have multiple inheritance?
// Hermaphrodite can do what boys and girls can
function createHermaphrodite(name, age) {
return xtend(createGirl(name, age), createBoy(name, age));
}
// Tests:
var boy = createBoy("John", 12);
var boy2 = createBoy("Frank", 10);
var girl = createGirl("Daisy", 7);
var herm = createHermaphrodite("Mel", 24);
console.info("Boy: ", boy.getName(), boy.getAge());
console.info("Boy: ", boy2.getName(), boy2.getAge());
console.info("Girl: ", girl.getName(), girl.getAge());
console.info("Hermaphrodite: ", herm.getName(), herm.getAge());
boy.playFootball(
xtend module for node.js.```
var xtend = require("xtend");
// Human constructor.
// Human is a base class, it can introduce itself, and knows it's age.
function createHuman(name, age) {
function getName() {
return name;
}
function getAge() {
return age;
}
return {
getName : getName,
getAge : getAge,
};
}
// Boy constructor
// Boy is a Human
// Boy can play football (but not yet)
function createBoy(name, age) {
function playFootball() {
console.warn("Football skills not implemented yet, but trying", "-", this.getName());
// TODO: implement this
}
return xtend(createHuman(name, age), {
playFootball: playFootball,
})
}
// Girl constructor
// Girl is a Human
// Girl can sing (well, almost)
function createGirl(name, age) {
function singASong() {
console.warn("Singing not implemented yet, but trying", "-", this.getName());
// TODO: implement this
}
return xtend(createHuman(name, age), {
singASong: singASong,
})
}
// Hermaphrodite constructor
// Hermaphrodite is a Girl and Hermaphrodite is a Boy, so we have multiple inheritance?
// Hermaphrodite can do what boys and girls can
function createHermaphrodite(name, age) {
return xtend(createGirl(name, age), createBoy(name, age));
}
// Tests:
var boy = createBoy("John", 12);
var boy2 = createBoy("Frank", 10);
var girl = createGirl("Daisy", 7);
var herm = createHermaphrodite("Mel", 24);
console.info("Boy: ", boy.getName(), boy.getAge());
console.info("Boy: ", boy2.getName(), boy2.getAge());
console.info("Girl: ", girl.getName(), girl.getAge());
console.info("Hermaphrodite: ", herm.getName(), herm.getAge());
boy.playFootball(
Solution
Q1: I wouldn't say it was good or particularly bad practice for some of the reasons I will go into below.
Q2: It's not memory efficient if you compare the code you have to a class / prototype based solution.
Q3: Take a look below, i've tried to keep it to as skeletal structure as possible to demonstrate how prototypes can be very useful.
xtend
xtend is great for smaller, intricate pieces of code and it has helped me loads recently. The caveat: In larger code bases you want to avoid the level of blind inheritance that xtend provides. Potentially in the future you may create a class that uses an object that has many layers of inheritance as an array of options for example. When that object squares up to the media and asked it's name and occupation, how does it know where all its components came from and what they do? In your code, how does the boy know he's a boy as well as a human for example.
Public / Private Methods
OOP in JS : Public / Private Variables will explain alot, if you are used to OOP in other languages this will probably click straight away.
Your first function is a class constructor with two private methods declared. I prefer to only set information in a constructor and then use public methods (prototypes) to deal with the workload.
Prototypes
Many objects can use prototypes and they're never duplicated within each object that is created.
Take this piece of code for example, first the class structure:
We could then use the class very easily to create a number of humans with different names, ages and genders.
I've included the
Start again:
I hope that gives you an insight into prototypes, they will be far more useful to you than object inheritance (though I admit it does have its uses).
Q2: It's not memory efficient if you compare the code you have to a class / prototype based solution.
Q3: Take a look below, i've tried to keep it to as skeletal structure as possible to demonstrate how prototypes can be very useful.
xtend
xtend is great for smaller, intricate pieces of code and it has helped me loads recently. The caveat: In larger code bases you want to avoid the level of blind inheritance that xtend provides. Potentially in the future you may create a class that uses an object that has many layers of inheritance as an array of options for example. When that object squares up to the media and asked it's name and occupation, how does it know where all its components came from and what they do? In your code, how does the boy know he's a boy as well as a human for example.
Public / Private Methods
OOP in JS : Public / Private Variables will explain alot, if you are used to OOP in other languages this will probably click straight away.
Your first function is a class constructor with two private methods declared. I prefer to only set information in a constructor and then use public methods (prototypes) to deal with the workload.
Prototypes
Many objects can use prototypes and they're never duplicated within each object that is created.
Take this piece of code for example, first the class structure:
function CreateHuman(params) {
// We create a new human object using params as initial data
this.human = {
name : params.name,
age : params.age,
gender : 'unknown'
}
}
// first prototype, updates the gender with the one specified in the call
CreateHuman.prototype.setGender = function (gender) {
this.human.gender = gender;
}
CreateHuman.prototype.setName = function (name) {
this.human.name = name;
}
CreateHuman.prototype.setAge = function (age) {
this.human.age = age;
}
// this method does more work than any of the others
CreateHuman.prototype.setHobby = function() {
// If the gender has been set, the hobby will be applied
// You could use functions here as well
switch(this.gender) {
case('male') :
this.person.hobby = 'football';
this.person.hobbyActive = (this.age >= 6) ? true : false;
break;
case('female') :
this.person.hobby = 'singing';
this.person.hobbyActive = (this.age >= 4) ? true : false;
break;
deafult :
this.person.hobby = 'no hobby';
this.person.hobbyActive = true;
}
}
// And a return method to get the object from the class
CreateHuman.prototype.getPerson = function() {
return this.person; // We return the object with all collected data
}We could then use the class very easily to create a number of humans with different names, ages and genders.
var params =
{
name : "Dave",
age : 30
};I've included the
params in this example as you may just need one object created and feeding it params is an easy way to make sure your code is useable by anyone else that might use your class.var human = new CreateHuman(params);
var people = {}; // Make people an object to hold all the humans created
// Call our first prototype function
human.setGender('male');
// 2nd prototype
human.setHobby();
people.human1 = human.getPerson();people.human1 if logged in the console would be:Object {name : 'dave',
age : 30,
gender : 'male',
hobby : 'football',
hobbyActive : true
}Start again:
human.setName('Alexa');
human.setAge(28);
human.setGender('female');
human.setHobby();
people.human2 = human.getPerson()
// And so onI hope that gives you an insight into prototypes, they will be far more useful to you than object inheritance (though I admit it does have its uses).
Code Snippets
function CreateHuman(params) {
// We create a new human object using params as initial data
this.human = {
name : params.name,
age : params.age,
gender : 'unknown'
}
}
// first prototype, updates the gender with the one specified in the call
CreateHuman.prototype.setGender = function (gender) {
this.human.gender = gender;
}
CreateHuman.prototype.setName = function (name) {
this.human.name = name;
}
CreateHuman.prototype.setAge = function (age) {
this.human.age = age;
}
// this method does more work than any of the others
CreateHuman.prototype.setHobby = function() {
// If the gender has been set, the hobby will be applied
// You could use functions here as well
switch(this.gender) {
case('male') :
this.person.hobby = 'football';
this.person.hobbyActive = (this.age >= 6) ? true : false;
break;
case('female') :
this.person.hobby = 'singing';
this.person.hobbyActive = (this.age >= 4) ? true : false;
break;
deafult :
this.person.hobby = 'no hobby';
this.person.hobbyActive = true;
}
}
// And a return method to get the object from the class
CreateHuman.prototype.getPerson = function() {
return this.person; // We return the object with all collected data
}var params =
{
name : "Dave",
age : 30
};var human = new CreateHuman(params);
var people = {}; // Make people an object to hold all the humans created
// Call our first prototype function
human.setGender('male');
// 2nd prototype
human.setHobby();
people.human1 = human.getPerson();Object {name : 'dave',
age : 30,
gender : 'male',
hobby : 'football',
hobbyActive : true
}human.setName('Alexa');
human.setAge(28);
human.setGender('female');
human.setHobby();
people.human2 = human.getPerson()
// And so onContext
StackExchange Code Review Q#13098, answer score: 4
Revisions (0)
No revisions yet.