Разделение большого класса на модули


Я пытаюсь построить свою первую библиотеку (браузер) и решил использовать модули commonjs в стиле узла + browserify для организации моего кода. Я структурировал его таким образом, что каждый модуль содержит 1 класс, который работал довольно хорошо, пока некоторые из классов не начали становиться невероятно огромными.

Итак, у меня есть класс, подобный

module.exports = MyClass;

function MyClass() {
  //initializing stuff

  this.publiceMethod= function publicMethod() {
    //do stuff
  };

  var privateMethod = function privateMethod() {
    //do stuff
  };
}

MyClass.prototype.notSureMethod = function notSureMethod() {
  //err... static method?
}

Проблема в том, что у меня есть большое количество методов, использующих различные способы их объявления ( this.method, var method, this.prototype.method). Что я такое интересно, есть ли относительно простой способ создать другой модуль (Ы) и потребовать их в MyClass как часть определения класса для повышения читаемости.

Идеальное поведение, которое я ищу, - это что-то вроде модуля, который является самоисполняющейся функцией, которая разделяет ту же область, из которой она вызывается (включая частные переменные / методы, если это возможно).

Я изучаю JS всего несколько недель, так что будьте со мной помягче, если я веду себя как идиот. Спасибо заранее :)

-- edit--

Слепо играя с ним, я понял, как сделать то, что я ищу, с помощью прототипов и общедоступных методов.
//Underneath MyClass definition
require('./prototype-methods')(MyClass);
require('./public-methods')(MyClass);

Тогда суть его в других файлах такова:

module.exports = function(MyClass) {
  MyClass.prototype.method = . . . .

  MyClass.method = . . . .
}
Таким образом, это заставляет меня задуматься, есть ли способ сделать что-то подобное с частными методами. Есть идеи?

- - - edit2 - - -

Что именно вы используете? Не могли бы вы привести пример того, что ваш класс неужели?

Я создаю библиотеку для Web audio api, которая, по сути, позволяет вам сочинять музыку. У меня есть пространство имен для библиотеки, и пространство имен (в данный момент) содержит несколько различных классов. Существует класс score, который выступает посредником между part, effect и player классы. Класс part - это просто оболочка для объекта instrument, который является объектом с функциями, воспроизводящими музыку.

Пространство имен имеет фабричную функцию, которая возвращает новый экземпляр score, которая в свою очередь имеет заводские функции, возвращающие новые part(s), effect(s) и в конечном итоге player. Кроме того, в любой момент времени может быть больше 1 балла, поэтому я потенциально могу сделать плейлист.

Первоначально я пытался использовать чисто функциональный паттерн, но мой код приобрел новое значение спагетти. Я больше знаком с пространством имен / классами, чем с функциональными шаблонами. В целом, я строил библиотеку так же, как и обычный файл javascript, но из-за добавлена сложность, были использованы commonjs модули + browserify, чтобы легко разделить код на различные файлы / построить части. Конкретный класс, о котором идет речь, - это класс part. Я хочу, чтобы библиотека была очень гибкой в том, какие обозначения она принимает, поэтому мне нужно было добавить довольно много методов для учета всех этих обозначений (= big file).
3 2

3 ответа:

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

Кажется, что я мог бы технически добавить / получить доступ к закрытым членам, используя eval, но мне сказали, что это зло. Просто вынимание прототипа и открытых методов из файла значительно урезало его, так что оставление закрытых членов в нем совершенно нормально.

Isbn 978-0-596-80675-0 (Глава 5 Шаблоны Создания Объектов / Шаблон Песочницы), isbn 0-596-10199-6 (Глава 10 модули и пространства имен / утилиты модулей)

На данный момент я также нахожусь в поиске удобного метода создания приватных данных. В настоящее время я использую функциональный шаблон наследования и defineClass следующим образом:

var defineClass = function () {
    var inheritance = function inheritance() { };
    return function defineClass(data) {
        var classname = data.name,
            superclass = data.extend || Object,
            constructor = data.construct || function () { },
            methods = data.methods || {},
            statics = data.statics || {},
            borrows,
            provides;
        if (!data.borrows) {
            borrows = [];
        }
        else {
            if (data.borrows instanceof Array) {
                borrows = data.borrows;
            }
            else {
                borrows = [data.borrows];
            };
        };
        if (!data.provides) {
            provides = [];
        }
        else {
            if (data.provides instanceof Array) {
                provides = data.provides;
            }
            else {
                provides = [data.provides];
            };
        };
        inheritance.prototype = superclass.prototype;
        var proto = new inheritance();
        for (var i = 0; i < borrows.length; i++) {
            var c = borrows[i];
            for (var p in c.prototype) {
                if (typeof c.prototype[p] != "function") continue;
                proto[p] = c.prototype[p];
            }
        }
        for (var p in methods) {
            proto[p] = methods[p];
        };
        proto.constructor = constructor;
        constructor.superclass = superclass.prototype;
        if (classname) {
            proto.classname = classname;
        };
        for (var i = 0; i < provides.length; i++) {
            var c = provides[i];
            for (var p in c.prototype) {
                if (typeof c.prototype[p] != "function") {
                    continue;
                };
                if (p == "constructor" || p == "superclass") {
                    continue;
                };
                if (p in proto && typeof proto[p] == "function" && proto[p].length == c.prototype[p].length) {
                    continue;
                };
                throw new Error("Class " + classname + " are not provided method " + c.classname + "." + p);
            };
        };
        constructor.prototype = proto;
        for (var p in statics) {
            constructor[p] = statics[p];
        };
        return constructor;
    }
}();

//SAMPLE CODE
var tempObj = function () { };
// example of a variable
tempObj.prototype.distance = 0;
// example of a method
tempObj.prototype.walk = function (time) {
    this.distance = this.distance + time * this.walkSpeed
};
tempObj.prototype.toString = function () {
    return this.name + " distance " + this.distance
};

var Animal = defineClass({
    name: "Animal",
    construct: function (name, walkSpeed) {
        this.name = name;
        this.walkSpeed = walkSpeed;
    },
    borrows: tempObj,
    methods: {
        distance: tempObj.prototype.distance
    }
});

var tempObj2 = defineClass({
    methods: {
        fly: function (time) {
            this.distance = this.distance + time * this.flySpeed
        }
    }
});

var Bird = defineClass({
    name: "Bird",
    construct: function (name, walkSpeed, flySpeed) {
        // call the parent constructor
        Bird.superclass.constructor.call(this, name, walkSpeed)
        this.flySpeed = flySpeed
    },
    extend: Animal,
    borrows: tempObj2
});

var Cuckoo = defineClass({
    name: "Cuckoo",
    extend: Bird,
    construct: function (name, walkSpeed, flySpeed) {
        // call the parent constructor
        Cuckoo.superclass.constructor.call(this, name, walkSpeed, flySpeed)
        this.word = "cucoo";
    },
    methods: {
        say: function () {
            return this.name + " says " + this.word;
        }
    }
});

var animal = new Animal("Dog", 2);
animal.walk(3);
var dd = animal.toString();             // => Dog distance 6

bird = new Bird("Bird", 1, 10);
bird.walk(3);
var ww = bird.toString();               // => Bird distance 3
bird.fly(2);
var ff = bird.toString();               // => Bird distance 23

cuckoo = new Cuckoo("Cuckoo", 1, 10);
cuckoo.walk(3);
var ww = cuckoo.toString();             // => Cuckoo distance 3
cuckoo.fly(2);
var ff = cuckoo.toString();             // => Cuckoo distance 23
var cSay = cuckoo.say();                // => Cuckoo says cucoo

Что именно вы используете? Не могли бы вы привести пример того, что делает ваш класс?

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

module.exports = function(x, y) {
  return x * y
}

И во втором модуле вы можете использовать такую функцию, как это:

var add = require('./add.js')
var result = add(15, 23);

Если вы хотите узнать больше об этом, проверьте Этот СЕМИНАР nodeschool.