如何创建一个扩展未预定的其他类的类

问题描述 投票:0回答:1

我正在尝试创建一个应该能够扩展几个基类的类。在下面的例子中,我想使用石头或木头作为课堂的基础类。为此,我尝试创建一个材料类,它将选择适当的基类。但是我没有让它发挥作用。


const EventEmitter = require('events').EventEmitter;

class Stone extends EventEmitter{
    constructor(weight, color){
        super();
        this.color = color;
        this.weight = weight;
        this.hard = true
        this.start();
    }
    testEmitterFunction(){
        this.emit('test');
    }
    start(){
        setInterval(this.testFunc.bind(this), 500);
    }
}

class Wood{
    constructor(weight, color){
        this.color = color;
        this.weight = weight;
        this.hard = false;
    }
}

class Material{
    constructor(mat, weight, color){
        switch(mat){
            case 'wood': return new Wood(weight, color);
            case 'stone': return new Stone(weight, color);
        }
    }
}


class House extends Material{
    constructor(mat, weight, color, name){
        super(mat, weight, color)
        this.name = name;
        this.on('test', (arg) => {
            console.log('1')
        });
        this.test();
    }
    test(){
        console.log('test house function');
    }
}

class House2 extends Stone{
    constructor(weight, color, name){
        super(weight, color)
        this.name = name;
        this.on('test', (arg) => {
            console.log('2')
        });
        this.test();
    }
    test(){
        console.log('test house2 function');
    }
}


const home = new House('stone', 8, 'green', 'homesweethome');
const home2 = new House2(8, 'green', 'homesweethome');

我希望该实例home具有与实例home2相同的行为。但在这种情况下,执行console.log('test house function')的测试功能不起作用。我尝试了其他解决方案,但是EventEmitter不起作用或者石头属性不可用。

javascript node.js oop ecmascript-6
1个回答
2
投票

正如我在评论中提到的,使用composition over inheritance可能会更好地完成你要做的事情。

作为一般简化,如果你可以说“我的X是Y的类型”或“我的X是Y”并且它是有意义的,那是继承,但如果你说“我的X由Y组成”或“我的X包含Y“然后你应该使用构图。适用于您的情况,Stone和Wood都是一种材料。房子是一种材料吗?我不会这么说,但是房子是由石头或木头制成的,或者更确切地说,房子是由材料制成的,这意味着我们应该使用构图。

如果你想保持将字符串传递给设置材料的House构造函数的能力,那么你仍然可以这样做。请参阅底部代码示例中的House#setMaterial,尽管factory pattern将来可能更适合您。

你的结构的另一个问题是它杀死了polymorphism。如果你想在StoneWood中做同样事情的方法,说“破解”,那么你必须复制粘贴相同的代码,但如果它们都从一般的材料类型继承,那么你只需要在基类中创建一次方法。

我希望能够将石头中的EventEmitter用于我的房子。即house.on(...)而不是house.stone.on(...)

在使用均衡器时,我建议您尽可能在最高级别创建一个,然后将其传递给需要它的组件。在这种情况下,House可以将事件发射器传递给材料或任何其他组件(例如房间)。由于Javascript的疯狂,House可以成为eventemitter并将自己传递给材料。请参阅下面的House#setEmitter类中的House函数。观察它如何在多态函数Material#Break中使用。

/** Unimportant */
class EventEmitter {
  constructor(){ this.map = {} }
  on(e, cb){
    if(!this.map[e]) this.map[e] = []
    this.map[e].push(cb)
  }
  emit(event,...data){
    if(!this.map[event]) return
    this.map[event].forEach(cb=>cb(...data))
  }
}
/**/

class Material {
  constructor(name = 'Something', weight = 5, color = 'black', hard = true){
    this.weight = weight
    this.color = color
    this.hard = hard
    this.name = name
  }
  setEmitter(emitter){
    this.emitter = emitter
  }
  
  break(){
    if(this.emitter){
      this.emitter.emit(`break`, `The ${this.name} has broken` )
    }
  }
  
  describe(){
    return `${this.weight}lb ${this.hard?'hard':'soft'} ${this.color} ${this.name}`
  }
}

class Stone extends Material {
  constructor(weight = 8, color = 'gray'){
    super("Stone", weight, color, true)
  }
}

class Wood extends Material {
  constructor(weight=4, color="brown"){
    super("Wood", weight, color, false)
  }
}

class House extends EventEmitter {
  constructor(material, name){
    super()
    this.material = this.setMaterial(material)
    this.name = name
    this.on('break', (what)=>{
      console.log(`${this.name} Event: `+what)
    })
  }
  
  setMaterial(mat){
    const matMap = {
      stone : Stone,
      wood : Wood
    }
    // Just gets a default material
    if(typeof mat == 'string'){
      mat = new matMap[mat]()
    }
    mat.setEmitter(this)
    return mat
  }
  // Logs information about the material
  describe(){
    console.log(`A house named ${this.name} made out of ${this.material.describe()}`)
  }
}


// Examples

// Method 1: Create a basic stone house and set material color later
const stoneHouse = new House("stone", "MyHouse")
stoneHouse.describe()
stoneHouse.material.color = "blue"
stoneHouse.describe()
stoneHouse.material.break()

// Method 2: Create a material and pass it to the house
const myMaterial = new Wood(6, "green")
const woodHouse = new House(myMaterial, "WoodHouse")
woodHouse.describe()
// Call a function that emits an event to the house
myMaterial.break()
© www.soinside.com 2019 - 2024. All rights reserved.