ES6 — прикрепить прототип класса к синглтону

Мне интересно, как лучше всего прикрепить «новый» класс к существующему экземпляру.

Например: у меня есть «рабочий» класс, который необходимо «расширить» до существующего экземпляра класса IPC, чтобы он мог общаться по каналу ipc.

class Worker {
    constructor(ipc) {
        this.__proto__ = ipc;
        this.__proto__.constructor = Worker;
    }
}

И тогда я могу использовать его так:

const worker = new Worker(myCurrentIpc);
worker.send('blabla') /* Calls super instance ipc method */

Это плохая практика, так как она убивает производительность. /developer.mozilla.org/nl/docs/Web/JavaScript/Reference/Global_Objects/Object/setPrototypeOf

И в этом случае метод send будет вызываться миллионы раз.

Итак, в таком случае использования, что бы вы порекомендовали?

Я также могу снова создать каждую функцию в экземпляре Worker, а затем вызвать ipc.func() в этом методе, но это также похоже на антишаблон.

Спасибо

Пояснение:

У меня есть один рабочий «хост» (основной). У него есть уже инициализированный экземпляр IPC (node-ipc).

Когда я создаю дочерний рабочий процесс, я хочу использовать (уже существующий) родительский экземпляр IPC для подключения к дочернему рабочему.

Так что было бы очень хорошо, когда бы я ни создавал нового Worker({ipc:ipc}), я мог бы прикрепить worker.prototype к экземпляру IPC. Таким образом, я могу просто сделать worker.send() и экземпляр IPC знает, что он идет с основного-> дочернего канала..


person DutchKevv    schedule 01.10.2016    source источник
comment
Является ли ipc другим классом или просто экземпляром класса?   -  person Kroltan    schedule 01.10.2016
comment
ipc — это уже инициализированный экземпляр класса   -  person DutchKevv    schedule 01.10.2016
comment
Ваши отношения родитель-работник не похожи на отношения подкласса. Здесь вы должны предпочесть композицию наследованию.   -  person Bergi    schedule 01.10.2016


Ответы (3)


Как уже упоминалось, речь идет о принципе композиция над наследованием. Рабочий объект должен хранить экземпляр ipc как частное свойство и обертывать его методы, если он должен раскрывать некоторые из них:

class Worker {
  constructor(ipc) {
    this.ipc = ipc;
  }

  send(...args) {
    return this.ipc.send(...args);
  }
}
person Estus Flask    schedule 01.10.2016

Если вы действительно хотите получить доступ к методам ipc с помощью worker.theIpcMethod, вы можете использовать "фабрику классов", функцию, которая создает класс, расширяющий определенное значение:

function WorkerClassFactory(ipc) {
    return class Worker extends ipc {
        //contents of Worker class here
    }
}

Затем вы создаете конкретный класс Worker или данный IPC:

let Worker = WorkerClassFactory(myIpcInstance);

//then somewhere
let worker_instance = new Worker();
worker_instance.doStuff();

Конечно, вы можете создать его один раз и использовать во всей программе, чтобы получить последовательное instanceof поведение.


НО! Это очень странный поступок, я бы рекомендовал просто открыть доступное только для чтения свойство и использовать его оттуда, или даже просто обернуть интерфейс IPC во что-то более простое в использовании (если вы класс просто предоставляет методы IPC, почему бы не использовать IPC напрямую?).

class Worker {
    constructor(ipc) {
        this._ipc = ipc;
    }

    get ipc() {
        return this._ipc;
    }
}

new Worker(ipc).ipc.send("asdasdasd");
person Kroltan    schedule 01.10.2016
comment
Спасибо за примеры кода @Kroltan ... Я в некотором смысле рад, что и вы, и synthet1c продвигаетесь в направлении простого превращения ipc в переменную внутри класса.. Начал чувствовать, что слишком далеко зашел с наследованием - person DutchKevv; 01.10.2016
comment
@DutchKev Да, это своего рода злоупотребление прототипным наследованием. Это также непоследовательно, потому что если вы дважды вызовете WorkerClassFactory с одним и тем же значением ipc, экземпляры этого класса не обязательно будут instanceof этим рабочим. Конечно, Factory может кэшировать свои выходные данные, но это сомнительный обходной путь. - person Kroltan; 01.10.2016
comment
@DutchKev Это то, что касается принципа композиция вместо наследования. Это не эмпирическое правило, но здесь оно полностью применимо. - person Estus Flask; 01.10.2016
comment
@estus, приятно читать, спасибо! Я искал черно-белую документацию о принципе «наилучшей практики», и в основном, как вы указываете, где «линия» находится между наследованием и композицией, которую следует иметь в виду. - person DutchKevv; 02.10.2016

Плохо, я не понял вопроса.

Это может быть немного больше, что вы ищете.

Здесь мы отделяем объект IPC и передаем его в конструктор Worker, а затем из экземпляра вы просто делегируете его одноэлементному IPC.

Как говорится в комментариях, использование класса не требуется.

// here is a singleton with a single static method. using class in not really
// neccasary here, you could just use an object with a single propery
class IPC {
  static send(val) {
    console.log('IPC::send', val)
  }
}

class Worker {
  // constructor takes the ipc as a variable and saves it to itself
  constructor(ipc) {
    this.ipc = ipc
  }
  // the send method simply calls the ipc send function
  send(val) {
    this.ipc.send(val)
  }
}

// create a new worker
const worker = new Worker(IPC);

worker.send('blabla')

person synthet1c    schedule 01.10.2016
comment
Спасибо за пример кода @synthet1c, но тогда IPC - это не синглтон, а новый класс. Я хочу прикрепить «новый» экземпляр к синглетону/существующему экземпляру. - person DutchKevv; 01.10.2016
comment
Вы не должны использовать class для синглтонов. Если вы сделаете его классом, создайте экземпляры с new, если вам нужен синглтон, используйте литерал объекта. - person Bergi; 01.10.2016