Как создать класс, который расширяет не предопределенный другой класс - PullRequest
0 голосов
/ 01 февраля 2019

Я пытаюсь создать класс, который сможет расширить несколько базовых классов.В следующем примере я хотел бы использовать камень или дерево в качестве базовых классов для дома класса.Для этого я попытался создать класс материала, который бы выбрал подходящий базовый класс.Но я не заставляю его работать.

</p>

<code>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');
</code>

Я бы хотел, чтобы экземпляр экземпляра home работал так же, как экземпляр home2.Но в этом случае тестовая функция, которая выполняет console.log («тестовая функция»), не работает.Я пробовал другие решения, но тогда EventEmitter не работал или свойства камня были бы недоступны.

1 Ответ

0 голосов
/ 01 февраля 2019

Как я уже упоминал в своем комментарии, то, что вы пытаетесь сделать, может быть лучше сделано с использованием композиция над наследованием .

Как общее упрощение, если вы можете сказать: «Мой X - это тип Y» или «Мой X - это Y», и это имеет смысл, это наследство, но если вы скажете: «Мой X состоит из Y"или" Мой X содержит Y ", тогда вы должны использовать композицию.Применительно к вашему случаю, Камень и Дерево являются типом Материала.Дом - это тип материала?Я бы так не сказал, но дом сделан из камня или дерева, точнее, дом сделан из материала, а это значит, что мы должны использовать для этого композицию.

Если выЕсли вы хотите оставить возможность передавать строку конструктору House, который устанавливает материал, тогда вы все равно можете это сделать.См. House#setMaterial в примере кода внизу, хотя фабричный шаблон может работать лучше для вас в будущем.

Другая проблема с вашей структурой состоит в том, что она убивает полиморфизм .Если вам нужны методы, которые делают то же самое в Stone и Wood, скажем, «взлом», вам придется скопировать и вставить один и тот же код, но если они оба унаследованы от общего типа Material, товам нужно создать метод только один раз в базовом классе.

Я хочу иметь возможность использовать EventEmitter из камня и для моего дома.т.е. house.on (...) вместо house.stone.on (...)

Когда дело доходит до использования Eventemitter, я бы порекомендовал вам создать его на максимально возможном уровнеи затем передайте это компонентам, которые нуждаются в этом.В этом случае Хаус может передать источник событий в материал или любые другие компоненты (например, комнату).Из-за сумасшествия Javascript, Хаус может быть и даже передавать себя материалу.См. Функцию 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()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...