We know that the most common way of code reuse in OOP is through inheritance. However, inheritance has some disadvantages. The most important one is that inheritance is an isa relationship, the relationship between parent and child classes is too close. For JAVA, only single inheritance is supported, so that code copying is not necessary in many cases.
For example, suppose we want to create an animal. The bottom layer is an Animal object, which contains cats and dogs. Then there are cats and tigers under the CAT. Dogs and wolves are under the inspecies. A cat can be miao, a dog can be called, and a tiger and a wolf can both be used for hunting. The problem arises because the hunting feature is available to both tigers and wolves, but tigers and cats inherit from each other, wolves have inherited from dogs, and they cannot inherit from them to gain the ability to hunt.
Let's take a look at how Trait solves this problem. Trait looks like a class on the surface. It has attributes and methods, but it must be attached to a class to work. Multiple Traits can be combined into one Trait. If the attributes or methods in different Traits conflict, you can choose to rename the attributes to determine the conflict. If the conflict does not have a resolution, the combination of Traits will throw an exception.
As described above, we define hunting as a Trait, and then integrate Trait when building tiger and Wolf classes. In this way, tigers and wolves will be able to hunt.
Due to restrictions of the Java language, there is no gorgeous way to implement Trait. Let's take a look at how the prototype-based Javascript language implements Trait. This actually proves from one side that prototype-based Javascript is more flexible and powerful For OOP than class-based Java. To reduce the code size, I use the JS library light-traits. Because the full implementation of a Traits library is beyond the scope of this article.
- var util = require('util');
- var Trait = require('light-traits').Trait;
- var expect = require('chai').expect;
- var _ = require('lodash');
- function inherits(constructor, parentConstructor, trait, properties) {
- util.inherits(constructor, parentConstructor);
- if (properties !== undefined)
- _.extend(constructor.prototype, properties);
- if (trait !== undefined)
- constructor.prototype = trait.create(constructor.prototype);
- }
-
- function Animal() {}
- Animal.prototype = {
- isAlive: true,
- eat: function (food) {
- console.log("omnomnom, I'm eating: " + food);
- },
- sleep: function () {
- console.log('zzzz');
- },
- die: function () {
- this.isAlive = false;
- console.log("I'm dead");
- }
- };
-
- function CatFamily() {}
- inherits(CatFamily, Animal);
- function DogFamily() {}
- inherits(DogFamily, Animal);
-
- var TMeow = Trait({
- meow: function () {
- console.log('meow meow');
- }
- });
- function Cat() {}
- inherits(Cat, CatFamily, TMeow);
-
- var cat = new Cat();
- cat.meow();
-
- var TBark = Trait({
- bark: function () {
- console.log('woof woof');
- }
- });
- function Dog() {}
- inherits(Dog, DogFamily, TBark);
-
- var dog = new Dog();
- dog.bark();
-
-
- var THunt = Trait({
- huntCount: 0,
- hunt: function () {
- console.log('looking for food', this.huntCount++, 'times');
-
- },
- kill: function (animal) {
- animal.die();
- console.log('I killed animal');
- }
- });
-
- function Tiger() {}
- inherits(Tiger, CatFamily, THunt, {
- roar: function () {
- console.log("roar...roar...");
- }
- });
-
- var tiger = new Tiger();
- expect(tiger).to.be.instanceOf(CatFamily);
- expect(tiger).to.have.property('hunt');
- expect(tiger).to.have.property('kill');
- expect(tiger).to.not.have.property('meow');
- expect(tiger.isAlive).to.be.equal(true);
- tiger.hunt();
- tiger.eat('meat');
- tiger.roar();
-
- function Wolf() {}
- inherits(Wolf, DogFamily, Trait.compose(TBark, THunt));
-
- var wolf = new Wolf();
- expect(wolf).to.be.instanceOf(DogFamily);
- expect(wolf).to.have.property('hunt');
- expect(wolf).to.have.property('kill');
- expect(wolf).to.have.property('bark');
- expect(wolf.isAlive).to.be.equal(true);
- wolf.bark();
- wolf.hunt();
- wolf.hunt();
- wolf.sleep();
- wolf.kill(cat);
- expect(cat.isAlive).to.be.equal(false);
- expect(wolf.huntCount).to.be.equal(2);