Шаблон-бесплатный шаблон подмешать текста библиотека диаграмм


Запись справочника содержит примесь шаблон , но это требует кучу шаблонного, не работает для геттеров/сеттеров и не поддерживает определение значений по умолчанию (или конструктор) на территории подмешать (только на составной объект).

Я написал альтернативный шаблон миксин для построения графиков библиотека я работаю. Моя библиотека ожидает большое входное и большое выходное в зависимости от того, какие куски в конечном итоге в финальной графике. У меня есть "контекст диаграммы" и каждый виджет диаграмма может способствовать контексте диаграммы с миксина.

В настоящее время я вижу несколько минусов на мой подход.

  • Он не поддерживает миксины, которые нужно множественное наследование (полагаю, я мог бы создать такой миксин через applyMixins но это кажется немного сложным).
  • Он не поддерживает добавление методов и функций к окончательному составной объект (это действительно просто еще один случай предыдущего пункта)
  • Если несколько классов расширяет базовый класс, то, что конструктор базового класса будет вызываться несколько раз.

Эти вопросы не являются проблемой для моего приложения, но я не эксперт в JavaScript и этот код, но он работает, сделал некоторые довольно глубокие манипуляции прототипа. Поэтому моими главными задачами являются:

  1. Это манипуляция прототип недействительным и, следовательно, вызвать проблемы для меня в будущем?
  2. В машинописи например, они заявляют:

derivedCtor.prototype[name] = baseCtor.prototype[name];

но, похоже, лучше было бы сказать:

Object.defineProperty(derivedCtor.prototype, name, Object.getOwnPropertyDescriptor(baseCtor, name));

почему они не используют такой подход? Есть некоторые проблемы с ним?

Код:

import { Subject } from 'rxjs/Subject';

class HasYAxis {
    yAxisType: 'linear' | 'log' = 'linear';
}

class BaseChart {
    size = { width: 0, height: 0 };
    get hasSize() { return !!this.size && this.size.width > 0 && this.size.height > 0; }
}

class BarChart extends BaseChart {
    bins: number[];
    yAxisType: 'linear' | 'log' = 'linear';
    binSelected = new Subject<number>();
    binHidden = new Subject<number>();
    get binWidth() { return this.size.width / this.bins.length; }
    getTotalBinCount() { return this.bins.reduce((a, b) => a + b, 0); }
}

interface Constructor<T> { new(): T; }

function getParents(ctor: Constructor<any>) {
    const parents = [];
    let parent = Object.getPrototypeOf(ctor.prototype);
    while (parent !== Object.prototype) {
        parents.push(parent);
        parent = Object.getPrototypeOf(parent);
    }
    return parents;
}

function getAllPrototypes(ctors: Constructor<any>[]) {
    const allCtors = new Set<Constructor<any>>();
    for (let ctor of ctors) {
        allCtors.add(ctor.prototype);
        for (let parent of getParents(ctor)) {
            allCtors.add(parent);
        }
    }
    return allCtors;
}

// I'm not sure the name for this.  I call it Christmas tree overloading.
// I've seen it used in a few places where a method could take in a 
// hetergeneous array and the result type depends on the type of the
// inputs.  I'm aware of the obvious cons (lots of typing and gives back
// any if passed in more than 5 arguments) and not too concerned about it.
function applyMixins<T1, T2>(t1: Constructor<T1>, t2: Constructor<T2>): Constructor<T1 & T2>;
function applyMixins<T1, T2, T3>(t1: Constructor<T1>, t2: Constructor<T2>, t3: Constructor<T3>): Constructor<T1 & T2 & T3>;
function applyMixins<T1, T2, T3, T4>(t1: Constructor<T1>, t2: Constructor<T2>, t3: Constructor<T3>, t4: Constructor<T4>): Constructor<T1 & T2 & T3 & T4>;
function applyMixins<T1, T2, T3, T4, T5>(t1: Constructor<T1>, t2: Constructor<T2>, t3: Constructor<T3>, t4: Constructor<T4>, t5: Constructor<T5>): Constructor<T1 & T2 & T3 & T4 & T5>;
function applyMixins(...ctors: Constructor<any>[]) {
    // The constructor for the composite object will simply call the constructors for each of the mixins.  This adds
    // support for defining default values as well as construction initialization in the mixins.
    const result = class {
        constructor() {
            // Don't include parents here, assume base ctors will call super()
            for (let ctor of ctors) {
                ctor.prototype.constructor.apply(this);
            }
        }
    } as any;
    // allProtos contains the prototypes of each of the ctors as well as prototypes for any base types they may have
    const allProtos = getAllPrototypes(ctors);
    allProtos.forEach(proto => {
        Object.getOwnPropertyNames(proto).filter(name => name !== 'constructor').forEach(name => {
            // Here I am using the property descriptor instead of the value.  If I don't (e.g. if I use
            // proto[name] then getter/setter based properties will not work correctly).
            let propertyDescriptor = Object.getOwnPropertyDescriptor(proto, name);
            Object.defineProperty(result.prototype, name, propertyDescriptor);
        });
    });
    return result;
}

let chartContextClass = applyMixins(HasYAxis, BarChart);
let chartContext = new chartContextClass();

chartContext.bins = [1, 2];
chartContext.size = { width: 10, height: 10 };
console.log(chartContext.hasSize);            // true
console.log(chartContext.binWidth);           // 5
console.log(chartContext.getTotalBinCount()); // 3
console.log(chartContext.yAxisType);          // linear


Комментарии