Javascript中的Trait與代碼重用
我們知道,OOP中最普遍的代碼重用方式是通過繼承,但是,繼承有一些缺點,其中最為主要的是繼承是一種isa關系,父子類之間的關系太過緊密,而對于像JAVA這門語言而言,只能支持單繼承,使得很多時候不能不進行代碼拷貝這樣的事情。
舉個例子,假設我們要建模動物。最底層是一個Animal對象,下面有貓科,犬科。然后貓科下有貓,老虎。犬科下有狗和狼。 貓能夠miao,狗能夠叫,老虎和狼都能夠狩獵,這個時候問題來了,由于狩獵這個特性是老虎和狼都有的,但是老虎以及從貓科繼承,狼已經(jīng)從犬科繼承,它們都已經(jīng)無法通過繼承來獲得狩獵這個能力了。
讓我們來看看Trait如何來解決這個問題。 Trait從表面上看和一個類很類似,有屬性和方法,但是它必須依附于一個類才能起作用。同時多個Traits可以組合成一個Trait。如果不同的 Traits中屬性或者方法有沖突的話,可以選擇重命名屬性的方法來決議沖突。如果沖突沒有決議的話,組合Traits會拋出異常。
這樣,類層次仍然像前面描述的一樣,我們把狩獵定義為一個Trait,然后在構(gòu)建老虎和狼的類的時候把Trait融入進去。這樣老虎和狼就獲得了狩獵的能力了。
由于Java語言的限制,沒有一種華麗的方法來實現(xiàn)Trait。讓我們來看看基于原型的Javascript語言如何實現(xiàn)Trait。這個其實從一 個側(cè)面證明基于原型的Javascript和基于類的Java相比,對于OOP而言更加靈活和強大。為了縮減代碼的大小,這里我使用light- traits這個JS庫。因為完整實現(xiàn)一個Traits庫超出這篇文章的范圍。
- 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);
 















 
 
 










 
 
 
 