Модераторы: Sardar, Aliance

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Наследование и состояние над-класса 
V
    Опции темы
Се ля ви
Дата 5.6.2008, 15:32 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Java/SOAрхитектор
****


Профиль
Группа: Модератор
Сообщений: 2007
Регистрация: 5.6.2004
Где: place without tim e and space

Репутация: 5
Всего: 126



Продолжаем эпопею, начатую в теме "Наследование и сво-во "constructor"".

Теперь вспомним про состояния объектов.
Использование инкапсуляции открывает для нас возможность закрывать внутреннее состояние объекта от использования его извне. Состоянием называется совокупность значений полей объекта.

А теперь вспомним, что при реализации наследования, основанного на прототипах, мы присваиваем свойству "prototype" конструктора объект, сгенерированный по конструктору предка - т.е. объект предка. Соответственно, у всех объектов-наследников будет ссылка на один и тот же объект предка и если мы будем менять его состояние, то оно будет меняться для всех объектов-наследников сразу. Такое поведение недопустимо при серёзном использовании наследования для расширения одним конструктором другого:
Код
/** @constructor */
var A = function() {
    
    this.constructor = A;
    
    /** @type {number} */
    this.publicField = 5;
    
    
    /** @type {number} */
    var _privateField = 3;
    
    /** @return {number} */
    this.getPrivateField = function(){
        return _privateField;
    }
    
    /** 
     * @param {number} privateField
     * @return {A} this object (ParamBean pattern - @see http://se-la-vy.blogspot.com/2008/04/paramsbean.html)
     */
    this.setPrivateField = function(privateField) {
        
        if (typeof _privateField == 'number')
            _privateField = privateField;
        else
            if (typeof privateField == 'object' && privateField instanceof Number)
                _privateField = privateField.valueOf();
            else
                throw new IllegelArgumentException('privateField param is not a number');
        
        return this;
    };
}

var B;
(B = function() {
    
    this.constructor = B
    
}).prototype = new A;

var IllegelArgumentException = function(message) {
    this.constructor = IllegelArgumentException;

    alert('IllegelArgumentException: ' + (
        this.message = message
    ));
}

var b1 = new B,
    b2 = new B;

b1.setPrivateField(1);
b2.setPrivateField(2);

alert(b1.getPrivateField());//2


Как вы думаете, что с этим можно сделать?


--------------------
  )
 (
[_])
проф. блог

Кролики думали, что занимаются любовью, а на самом деле их просто разводили...
PM MAIL WWW Skype GTalk   Вверх
Се ля ви
Дата 5.6.2008, 17:16 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Java/SOAрхитектор
****


Профиль
Группа: Модератор
Сообщений: 2007
Регистрация: 5.6.2004
Где: place without tim e and space

Репутация: 5
Всего: 126



Как вариант напрашивается объявить класс-наследник примерно так:

Код
var B;
(B = function() {
    (
        this.constructor = B
    ).prototype = new A;
    
}).prototype = new A;


Чуть-чуть модифицируем код с учётом того, что ссылку на конструктор предка мы можем получить и из внутренних источников:
Код
var B;
(B = function() {
    (
        this.constructor = B
    ).prototype = new this.constructor.prototype.constructor;
    
}).prototype = new A;


Т.е. фактически, мы обновляем свойство "prototype" каждый раз при создании объекта, что бы каждому новому экземпляру доставался новый экземпляр-объект, полученный из родительского конструктора.

Этот метод будет работать но, к сожалению, будет иметь один существенный изъян - это вариант лишь для конструкторов, не использующих параметры инициализации (т.н. формальные параметры). Если же конструктору предка нужно будет передать параметр - он при такой схеме попадёт только к следующему объекту-наследнику, но не к данному:
Код
/** @constructor */
var A = function(privateField) {
    
    this.constructor = A;
    
    /** @type {number} */
    this.publicField = 5;
    
    
    /** @type {number} */
    var _privateField = privateField;
    
    /** @return {number} */
    this.getPrivateField = function(){
        return _privateField;
    }
    
    /** 
     * @param {number} privateField
     * @return {A} this object (ParamBean pattern - @see http://se-la-vy.blogspot.com/2008/04/paramsbean.html)
     */
    this.setPrivateField = function(privateField) {
        
        if (typeof _privateField == 'number')
            _privateField = privateField;
        else
            if (typeof privateField == 'object' && privateField instanceof Number)
                _privateField = privateField.valueOf();
            else
                throw new IllegelArgumentException('privateField param is not a number');
        
        return this;
    };
}

var B;
(B = function(privateField) {
    (
        this.constructor = B
    ).prototype = new this.constructor.prototype.constructor(privateField);
    
    this.privilegedMethod = function(){
        alert('privileged method!')
    }
    
}).prototype = new A(3);

var IllegelArgumentException = function(message){
    alert('IllegelArgumentException: ' + (
        this.message = message
    ));
}

var b1 = new B(5),
    b2 = new B(6);

alert('b1.getPrivateField() = ' + (
    b1.getPrivateField()
));//'b1.getPrivateField() = 3'
alert('b2.getPrivateField() = ' + (
    b2.getPrivateField()
));//'b2.getPrivateField() = 5'

Так что такое решение в общем случае неприемлемо...


--------------------
  )
 (
[_])
проф. блог

Кролики думали, что занимаются любовью, а на самом деле их просто разводили...
PM MAIL WWW Skype GTalk   Вверх
AKS
Дата 5.6.2008, 18:57 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Участник форума
**


Профиль
Группа: Участник
Сообщений: 725
Регистрация: 20.9.2006

Репутация: 27
Всего: 52



Цитата(Се ля ви @  5.6.2008,  15:32 Найти цитируемый пост)
Как вы думаете, что с этим можно сделать?

Какой интригующий вопрос! smile А я Вам отвечу вопросом - а что нужно сделать?
Цитата(Се ля ви @  5.6.2008,  15:32 Найти цитируемый пост)
... у всех объектов-наследников будет ссылка на один и тот же объект предка и если мы будем менять его состояние, то оно будет меняться для всех объектов-наследников сразу.

Это как Вы собираетесь менять состояние объекта?
Цитата(Се ля ви @  5.6.2008,  17:16 Найти цитируемый пост)
var _privateField = privateField;

Зачем?
PM MAIL   Вверх
Се ля ви
Дата 5.6.2008, 23:50 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Java/SOAрхитектор
****


Профиль
Группа: Модератор
Сообщений: 2007
Регистрация: 5.6.2004
Где: place without tim e and space

Репутация: 5
Всего: 126



Цитата(AKS @  5.6.2008,  18:57 Найти цитируемый пост)
Цитата(Се ля ви @  5.6.2008,  15:32 Найти цитируемый пост)
Как вы думаете, что с этим можно сделать?

Какой интригующий вопрос! smile А я Вам отвечу вопросом - а что нужно сделать?

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

В данном случае нужно сделать так, что бы каждый экземпляр, созданный конструктором "B", имел неявную ссылку на свой объект, созданный конструктором "A" и что бы это по возможности не сказалось на пользователе - т.е. на той части программы, в которой объявляются и используются объекты-экземпляры, созданные на основе конструкторов.

Цитата(AKS @  5.6.2008,  18:57 Найти цитируемый пост)
Цитата(Се ля ви @  5.6.2008,  15:32 Найти цитируемый пост)
... у всех объектов-наследников будет ссылка на один и тот же объект предка и если мы будем менять его состояние, то оно будет меняться для всех объектов-наследников сразу.

Это как Вы собираетесь менять состояние объекта?

Посредством изменения значений его полей. Для public полей - напрямую, для private - опосредованно setter`ами.

Цитата(AKS @  5.6.2008,  18:57 Найти цитируемый пост)
Цитата(Се ля ви @  5.6.2008,  17:16 Найти цитируемый пост)
var _privateField = privateField;

Зачем?

Это простейший пример инкапсуляции, приём Information hiding (если чего-то непонятно, с переводом могу помочь smile ).
Объект содержит private-поле _privateField, которое видно только внутри объекта и не видно снаружи, соответственно я, как составитель библиотеки, получаю некоторую гарантию, что программист, который будет эту мою библиотеку использовать, не сможет получить прямого доступа к этому полю.

Например, я мог бы наложить некоторые дополнительные ограничения - заставить передавать числовое значение в каком-то ограниченном диапазоне и/или кратное какому-то числу и сделать проверку на соответствие этим ограничениям перед присвоением значения полю. Ну или как в случае с методом "setPrivateField" сделать проверку на соответствие нужному типу (number) и ввести преобразование, если значение number было передано в оболочке - объекте Number:
Код
        if (typeof _privateField == 'number')
            _privateField = privateField;
        else
            if (typeof privateField == 'object' && privateField instanceof Number)
                _privateField = privateField.valueOf();
            else
                throw new IllegelArgumentException('privateField param is not a number');


Таким образом, после прохождения такой проверки, я полностью уверен, что какой бы дуралей ни использовал этот класс, поле _privateField всегда будет типа "number", так что я могу без опаски производить с ним в других методах объекта арифметические операции. Т.е. объект получается надёжным.

Такого рода ограничения не нужны, когда над проектом работает один человек или даже несколько, которые сидят рядом и всегда могут друг друга обо всём спросить или легко разберутся в коде друг друга, но для больших проектов и написания библиотек для сторонних пользователей это утверждение не справедливо, по-этому приходится писать надёжный код.


--------------------
  )
 (
[_])
проф. блог

Кролики думали, что занимаются любовью, а на самом деле их просто разводили...
PM MAIL WWW Skype GTalk   Вверх
Се ля ви
Дата 6.6.2008, 02:14 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Java/SOAрхитектор
****


Профиль
Группа: Модератор
Сообщений: 2007
Регистрация: 5.6.2004
Где: place without tim e and space

Репутация: 5
Всего: 126



Итак, похоже единственный приемлемый ответ на вопрос о том, как добиться желанной цели состоит в том, что бы архитектурно не воспринимать конструктор как метод, устанавливающий какое-то состояние для нашего объекта в зависимости от параметров его инициализации, а создавать для установки состояния специальный метод.

Предлагаю, назвать этот метод "init" - на мой взгляд, это наиболее приемлемое название.

Тогда на реализацию этого метода необходимо наложить следующие ограничения:
1. Для этого метода нужно обеспечить, что бы его вызывали только один раз или не вызывали вовсе если его нет (если нужное состояние объекта выставляется автоматически или отсутствует вовсе). Для этого проще всего уничтожать ссылку на этот метод после его выполнения - тогда проинициализирован объект или нет можно будет понять просто проверив, есть ли у него такой метод.
2. В этом методе нужно вызывать такой же метод "init" предка конструктора, если они существуют, передавая им нужные параметры инициализации. При чём желательно делать это в самом начале их выполнения, что бы иметь гарантию того, что когда данный объект начнёт выполнять какие-то методы, объект предка уже будет в правильном проинициализированном состоянии.
3. Если у класса-предка есть метод "init", то у класса-потомка он непременно должен быть

Т.к. этот метод будет перекрываться, нам нужна чёткая ссылка на объект-прототип для его вызова (кстати, то же будет касаться любых других методов - перекрыв метод, мы сможем выполнить дополнительные действия, а потом вызвать переопределяемый метод). По аналогии с Java назвать эту ссылку "super" не получится, поскольку это слово закрыто для использования составителями языка, по-этому, учитывая приватность этого поля, предлагаю, как и для других private-полей, добавить к началу названия символ "_".

Вообще, почему я приставил к имени закрытого поля символ "_"? Дело в том, что в JS нельзя разделить private-поле и параметр, переданный в функцию, так что поля обязаны называться по-другому, что бы быть доступными в методах с такими же именами параметров - например, в setter`е мы не можем написать, в отличие от Java, "this.a = a" потому, что "this.a" никуда не ссылается, а private-поле "a" оказывается перекрыто одноимённым параметром - отсюда и предложение писать символ "_" перед именами переменных, соответствующих закрытым полям.

Что бы не нарушать синтаксис создания экземпляра объекта, полезно вызывать метод "init" в том случае, если параметры инициализации ему всё-таки были переданы в конструктор - это означает, что объект используется непосредственно, а не как прототип.

Предыдущий приём отметать по-этому рано. Нужно просто его дополнить некоторыми деталями:
1. Присваивать свойству "prototype" конструктора объект A без параметров инициализации.
2. Присваивать значение private-полю "_super" нужно между присвоением будущему объекту прямой ссылки на его конструктор и присвоением свойству "prototype" нового объекта предка, потому что после этого присвоения мы до нужного нам объекта уже не доберёмся (доберёмся по ссылке __proto__, но только в FF, а в общем виде у нас будет только неявная ссылка).
3. Всё, что было описано выше про метод "init".

Код
/**
 * Sample constructor for demonstrate Inheritance - parent
 * @constructor
 * @type {function}
 * @param {number} privateField
 */
var A = function(privateField) {
    
    /** @type {function} */
    this.constructor = A;
    
    /** @type {object} */
    var _super = this.constructor.prototype;
    
    /** @type {number} */
    var _privateField;
    
    /**
     * Getter method of "privateField" property
     * @return {number}
     */
    this.getPrivateField = function(){
        return _privateField;
    }
    
    /**
     * Setter method of "privateField" property
     * @param {number} privateField+
     * @return {A} this object (ParamBean pattern - @see http://se-la-vy.blogspot.com/2008/04/paramsbean.html)
     * @throws NullPointerException when argument is not presented
     * @throws IllegelArgumentException when argument is not number literal or Number object
     */
    this.setPrivateField = function(privateField) {
        
        if (privateField == null) throw new NullPointerException('argument "privateField" is not presented!');
        
        if (typeof privateField == 'number')
            _privateField = privateField;
        else
            if (typeof privateField == 'object' && privateField instanceof Number)
                _privateField = privateField.valueOf();
            else
                throw new IllegelArgumentException('privateField param is not a number');
        
        return this;
    }
    
    /**
     * Method for initialization of object state
     * @param {number} privateField+
     * @throws NullPointerException when argument is not presented
     * @throws IllegelArgumentException when argument is not number literal or Number object
     */
    this.init = function(privateField) {
        if (_super.init) _super.init();
        
        this.setPrivateField(privateField); 
        
        delete this.init;
    }
    
    if (privateField != null)
        this.init(privateField);
}

/**
 * Sample constructor for demonstrate Inheritance - child
 * @constructor
 * @extends {A}
 * @type {function}
 * @param {number} privateField?
 */
var B = function(privateField) {
    
    /** @type {function} */
    this.constructor = B;
    
    /** @type {A} */
    var _super = this.constructor.prototype;
    
    /** @type {A} */
    this.constructor.prototype = new this.constructor.prototype.constructor;
    
    /**
     * Method for initialization of object state
     * @param {number} privateField+
     * @throws NullPointerException when argument is not presented
     * @throws IllegelArgumentException when argument is not number literal or Number object
     */
    this.init = function(privateField) { 
        
        if (_super.init) _super.init(privateField);
        
        delete this.init;
    }
    
    if (privateField != null)
        this.init(privateField);
}
B.prototype = new A;

/**
 * @constructor
 * @param {string} message
 */
var IllegelArgumentException = function(message) {
    /** @type {function} */
    this.constructor = IllegelArgumentException;
    
    alert('IllegelArgumentException: ' + (
        this.message = message
    ));
},
    NullPointerException = function(message) {
    /** @type {function} */
    this.constructor = NullPointerException;
    
    alert('NullPointerException: ' + (
        this.message = message
    ));
}

/** @type {B} */
var b1 = new B(5),
    b2 = new B(6);

alert('b1.getPrivateField() = ' + (
    b1.getPrivateField()
));//'b1.getPrivateField() = 5'

alert('b2.getPrivateField() = ' + (
    b2.getPrivateField()
));//'b2.getPrivateField() = 6'


Вот теперь нам действительно не важно - передали нам ссылку на объект A или на объект B - если нас интересует только функциональность объекта A, то мы работаем с объектом как с объектом, созданным конструктором A, хотя реально это может быть и объект, созданный конструктором B. И всё это может работать на сколько угодно шагов в дереве иерархии наследования.


--------------------
  )
 (
[_])
проф. блог

Кролики думали, что занимаются любовью, а на самом деле их просто разводили...
PM MAIL WWW Skype GTalk   Вверх
AKS
Дата 6.6.2008, 09:34 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Участник форума
**


Профиль
Группа: Участник
Сообщений: 725
Регистрация: 20.9.2006

Репутация: 27
Всего: 52



Цитата(Се ля ви @  5.6.2008,  23:50 Найти цитируемый пост)
Нужно по прежнему реализовать наследование так, что бы получить возможность писать крупные приложения на JavaScript.

А-а-а, вот оно что! Тогда я тут не смогу помочь ничем - "крупные приложения на JavaScript" не писал и вряд ли придется. ;)
Цитата(Се ля ви @  5.6.2008,  23:50 Найти цитируемый пост)
Посредством изменения значений его полей.

А что это за объект предка? Прототип что-ли?
Цитата(Се ля ви @  5.6.2008,  23:50 Найти цитируемый пост)
Это простейший пример инкапсуляции...

Спасибо, удружили! Теперь наконец-то буду знать, что такое инкапсуляция! smile
Если серьезно, то вопрос задавал для того, чтобы узнать, зачем она нужна там, где все от природы public (дальнейшие ваши объяснения все прояснили).
Цитата(Се ля ви @  5.6.2008,  23:50 Найти цитируемый пост)
...получаю некоторую гарантию, что программист, который будет эту мою библиотеку использовать, не сможет получить прямого доступа к этому полю.

А Вас не волнует та цена, которую придется платить за такие возможности в J(ava)Script?
Если Вы делаете примерно то же самое в Java-программе, то ваши методы ведь будут общими для всех экземпляров, так ведь? А что происходит в данном случае? Всякий раз, наряду с приватной переменной, Вам необходимо создать пару методов (геттер и сеттер), которые будут уникальны для каждого экземпляра. Т.е. с каждым объектом-экземпляром создаются еще объекты-функции, размещаемые в динамической памяти. Нужны ли такие программные единицы, отрицательно влияющие на потребление памяти и снижающие производительность, да еще и в "приложениях", где критически важны время отклика и интерактивность?
Цитата(Се ля ви @  5.6.2008,  23:50 Найти цитируемый пост)
Таким образом, после прохождения такой проверки, я полностью уверен, что какой бы дуралей ни использовал этот класс, поле _privateField всегда будет типа "number", так что я могу без опаски производить с ним в других методах объекта арифметические операции. Т.е. объект получается надёжным.

Типичное заблуждение. Я сам ничего придумывать не буду -  возьму готовое. Вот обсуждение на тему "jQuery – это сила". Там Zeroglif показал народу, слабо представляющему что такое J(ava)Script, насколько примитивны подобные ухищрения. Было (функция из либы):
Код

    // This may seem like some crazy code, but trust me when I say that this
    // is the only cross-browser way to do this. --John
    isFunction: function( fn ) {
        return !!fn && typeof fn != "string" && !fn.nodeName && 
            fn.constructor != Array && /function/i.test( fn + "" );
    }

Вдохновенные комменты к этой функции были, вроде: "У нее ровно одна задача — узнать, является ли передаваемый параметр функцией. Со своей задачей она справляется". Справляется, как бы не так!
Zeroglif написал:
Цитата

Ну передай ей для начала значения этих переменных:
var a, b, c;

a = /^\dfunction\d/;
b = new String('Wow, J(ava)Script is soooo functional...');
c = ['method', 'function', 'object'];
c.constructor = 'Lego';

Люди даже въехать-то не смогли, что и как без дополнительных объяснений. Вот так.
Что я хотел этим сказать? А то, что если Вы собираетесь писать для "дуралеев" (ваше выражение), то это бесполезная трата времени. Просто-напросто Вам не позволит этого сделать сам язык программирования, за который Вы взялись. И не стоит забывать о золотом правиле: не пытайтесь предугадать все варианты использования вашей программы
Цитата(Се ля ви @  5.6.2008,  23:50 Найти цитируемый пост)
... для больших проектов и написания библиотек для сторонних пользователей это утверждение не справедливо, по-этому приходится писать надёжный код.

Надежный код на J(ava)Script – это утопия. Всем будет удобней, если код будет "прозрачным" и ясным. Именно этого и нужно добиваться, вместо того, чтобы писать "защиту от дурака".
Цитата(Се ля ви @  5.6.2008,  23:50 Найти цитируемый пост)
throw new IllegelArgumentException('privateField param is not a number');

Ну а это-то на кой черт? Что и говорить - Java-программист не может обойтись без своего класса обработки исключений. Но а кому Вы собираетесь показывать свои alert’ы? Пользователям эти "дурно пахнущие" сообщения даром не нужны, а программист-неудачник пусть уж увидит встроенные сообщения об ошибке. Да и что может быть лучше "родной" обработки исключений? К тому же, есть ведь, в конце концов, try catch плюс conlsole.log/opera.postError/Debug.write

В заключении - следующее. Я вижу, что у Вас уже сформировалась определенная концепция, и Вас уже с этого пути не свернуть. Явление уже привычное – кто родом с Java, кто с C(++) и т.д., выбирают привычные методы достижения своих целей и не желают (или не могут) действовать иначе. Если Вы уверены, что такой простецкий язычишка, как J(ava)Script, даст Вам то, что нужно, то я желаю только удачи в разработке ваших "хитрых супер-пупер приложений" (больше не буду влезать в концептуальные споры). 

PM MAIL   Вверх
Се ля ви
Дата 6.6.2008, 11:41 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Java/SOAрхитектор
****


Профиль
Группа: Модератор
Сообщений: 2007
Регистрация: 5.6.2004
Где: place without tim e and space

Репутация: 5
Всего: 126



Цитата(AKS @  6.6.2008,  09:34 Найти цитируемый пост)
Цитата(Се ля ви @  5.6.2008,  23:50 Найти цитируемый пост)
Нужно по прежнему реализовать наследование так, что бы получить возможность писать крупные приложения на JavaScript.

А-а-а, вот оно что! Тогда я тут не смогу помочь ничем - "крупные приложения на JavaScript" не писал и вряд ли придется. ;)

Ну хорошо, приложение уровня GMail, на ваш взгляд, можно написать как "некрупное приложение"? Если бы вам пришлось - вы бы как действовали?

Цитата(AKS @  6.6.2008,  09:34 Найти цитируемый пост)
А Вас не волнует та цена, которую придется платить за такие возможности в J(ava)Script?
Если Вы делаете примерно то же самое в Java-программе, то ваши методы ведь будут общими для всех экземпляров, так ведь? А что происходит в данном случае? Всякий раз, наряду с приватной переменной, Вам необходимо создать пару методов (геттер и сеттер), которые будут уникальны для каждого экземпляра. Т.е. с каждым объектом-экземпляром создаются еще объекты-функции, размещаемые в динамической памяти. Нужны ли такие программные единицы, отрицательно влияющие на потребление памяти и снижающие производительность, да еще и в "приложениях", где критически важны время отклика и интерактивность?

Точно! Об этом я как-то подзабыл - очень хорошо, что вы мне это заметили. Действительно - в Java все методы содержатся в классах, в ед. экземпляре, а тут получается, что я расточительно создаю каждый раз новый объект-функцию. Спасибо, что обратили на это моё внимание! smile Сейчас буду думать над этим...

Цитата(AKS @  6.6.2008,  09:34 Найти цитируемый пост)
Цитата(Се ля ви @  5.6.2008,  23:50 Найти цитируемый пост)
Посредством изменения значений его полей.

А что это за объект предка? Прототип что-ли?

Помнится, слово "прототип" как раз Zeroglif и критиковал - что из-за повсеместного использования в разных контекстах оно всех только путает. Да, в данном случае имелся в виду прототип. smile Но мне больше нравится "экземпляр конструктора предка", потому что это чётче отражает суть.

Цитата(AKS @  6.6.2008,  09:34 Найти цитируемый пост)
если Вы собираетесь писать для "дуралеев" (ваше выражение), то это бесполезная трата времени. Просто-напросто Вам не позволит этого сделать сам язык программирования, за который Вы взялись. И не стоит забывать о золотом правиле: не пытайтесь предугадать все варианты использования вашей программы

AKS, поверьте, эти правила не мной придуманы. Когда проект пишут 20 программистов, про которых нельзя сказать, что они - "не разлей вода", ибо у всех разные подходы и видение проекта, в результате чего периодически возникают мелкие конфликты и плетутся интриги, очень часто возникает ситуация, когда один разработчик обвиняется в том, что его код неправильный - ему угрожает лишение премии, понижение зарплаты или даже увольнение. А настоящая причина оказывается в том, что его код какой-то другой программист неправильно использует. И начинаются бодания типа: "Откуда я знал, что твоей функции можно передавать только 'number'? Я думал, она и строку примет нормально и распарсит..." или "Сам и проверяй свой диапазон, почему я-то это должен делать?.." И это ещё будет очень хорошо, если вам вообще это предъявят, а в худшем случае сделают о вас выводы как о разработчике без предъявления претензий и это и другие такие вещи постепенно сформируют соответствующее отношение к Вам у начальства и коллег. А вы даже не будете понимать, в чём дело до самого последнего момента, когда станет уже поздно разбираться и вспоминать, в чём причина такого отношения. В итоге в таких проектах всё настолько усложняется, что единственно-верным ответом на то, как писать такие приложения и не плодить конфликтов становится использование надёжного кода, что бы никакой дуралей не смог вас обвинить в том, что вы написали дурной код, который не работает в реальной ситуации и Вам не пришлось бы постоянно доказывать, что вы - не индюк или терять авторитет. Что бы можно было всегда чётко разграничить зоны ответственности - это мой косяк, а это - твой.

Цитата(AKS @  6.6.2008,  09:34 Найти цитируемый пост)
Надежный код на J(ava)Script – это утопия. Всем будет удобней, если код будет "прозрачным" и ясным. Именно этого и нужно добиваться, вместо того, чтобы писать "защиту от дурака".

Вспомнил одну шутку - "можно сделать защиту от дурака, но не от изобретательного!" smile
Прозрачность и ясность не позволит пользоваться основным для крупных приложений приёмом абстрагирования. Когда вы получаете конструктор и не копаетесь в его внутренностях, что бы понять, как с ним работать - смотрите только на сигнатуры методов и вам сразу же всё становится понятно, как в вашем данном конкретном случае его использовать.
Поверьте, в крупном проекте это - недопустимая роскошь - постоянно тратить время на копание в исходниках, что бы досконально знать всю цепочку операторов, которая выполняется при использовании различных сторонних библиотек. Просто каждый разработчик на своём уровне контролирует какой-то "слой" программного кода проекта и отвечает за его правильную работу, всё остальное - не его дело, только так можно масштабировать большие проекты.

Цитата(AKS @  6.6.2008,  09:34 Найти цитируемый пост)
Цитата(Се ля ви @  5.6.2008,  23:50 Найти цитируемый пост)
throw new IllegelArgumentException('privateField param is not a number');

Ну а это-то на кой черт? Что и говорить - Java-программист не может обойтись без своего класса обработки исключений. Но а кому Вы собираетесь показывать свои alert’ы? Пользователям эти "дурно пахнущие" сообщения даром не нужны, а программист-неудачник пусть уж увидит встроенные сообщения об ошибке. Да и что может быть лучше "родной" обработки исключений? К тому же, есть ведь, в конце концов, try catch плюс conlsole.log/opera.postError/Debug.write. 

Совершенно справедливо! smile Однако в данном случае эти исключения - это классы-заглушки. Дело в том, что пока я окончательно не разобрался с наследованием, писать библиотеку реализации основных Exception`ов рановато. Это дело ближайшего будущего smile
Что же касается родной обработки исключений, то объект Error, к сожалению, по-разному работает в разных браузерах и по-этому его использование затруднено.
Ну а насчёт conlsole.log/opera.postError/Debug.write - я думал в сторону Log4JavaScript, думаю, его можно настроить на то, что бы он писал сообщения в них.

Цитата(AKS @  6.6.2008,  09:34 Найти цитируемый пост)
Я вижу, что у Вас уже сформировалась определенная концепция, и Вас уже с этого пути не свернуть. Явление уже привычное – кто родом с Java, кто с C(++) и т.д., выбирают привычные методы достижения своих целей и не желают (или не могут) действовать иначе. Если Вы уверены, что такой простецкий язычишка, как J(ava)Script, даст Вам то, что нужно, то я желаю только удачи в разработке ваших "хитрых супер-пупер приложений"

AKS, пожалуйста, поймите мою позицию - я не зацикленный на Java программист, который любой язык пытается приспособить под паттерны Java. Боже упаси - я рад бы использовать другие паттерны, если они позволят мне создавать действительно масштабные, многофункциональные приложения на клиенте, в браузере.
Просто когда я сталкиваюсь с тем, что феноменальная гибкость JavaScript не позволяет ни разработать действительно хорошей среды разработки для сценариев - (по сути, почти всё что я видел не отличается от блокнота с подсветкой, единственное - мне недавно понравился Spket IDE, который использует информацию, содержащущуюся в JSDoc-описании, но у него всё равно большое количество глюков), ни написать крупных приложений разными людьми, не очень плотно общающимися между собой, я задаю себе вопрос - "А какого чёрта?". В принципе, нет объективных ограничений у этого языка, которых нельзя было бы преодолеть, вопрос лишь в том, что бы лучше его изучить и правильно использовать для данной задачи. Как правильно? По счастью, я знаком с тем, как реализована Java и я знаю, что её реализация позволяет и создавать удобные среды разработки и писать действительно масштабные приложения сотнями разработчиков, часто вообще не знакомых между собой и находящихся друг от друга на больших расстояниях. Т.е. опыт из Java я привлекаю по мере необходимости.
Вы же, насколько я понимаю, пытаетесь обозначить мне потолок - мол, не пиши масштабных приложений, JavaScript разработан для маленьких хитрых скриптиков на страничках - вот для них этот язык используй! Да я использовал - лет 7 уже на JavaScript пишу. Но сейчас мне хочется расширить его возможности по написанию на нём чегото-то серьёзного.
Просто другие мои коллеги используют GWT - слышали о таком? Это компиляция Java-кода в JavaScript - можете себе представить? Я бы хотел всё-таки нащупать реальную альтернативу и выдавить для таких задач все соки из JavaScript, дав людям альтернативные возможности.

P.S. А смысла этого метода "isFunction" я чего-то вообще не понял. Можно же просто спросить typeof fn == 'function' - и всё... Впрочем, многое в jQuery и Prototype.js мне так же кажется нелогичным.


--------------------
  )
 (
[_])
проф. блог

Кролики думали, что занимаются любовью, а на самом деле их просто разводили...
PM MAIL WWW Skype GTalk   Вверх
AKS
Дата 6.6.2008, 12:55 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Участник форума
**


Профиль
Группа: Участник
Сообщений: 725
Регистрация: 20.9.2006

Репутация: 27
Всего: 52



Цитата(Се ля ви @  6.6.2008,  11:41 Найти цитируемый пост)
Ну хорошо, приложение уровня GMail, на ваш взгляд, можно написать как "некрупное приложение"? Если бы вам пришлось - вы бы как действовали?

Я честно скажу – я не ведущий разработчик. Поэтому я не имею ни малейшего понятия, как бы я действовал или как надо действовать. Это здесь, в этой теме, пользуясь возможностью, я взялся рассуждать на темы, которые по большому счету меня не касаются. Хотя мало ли чего – может быть, когда-нибудь...
Цитата(Се ля ви @  6.6.2008,  11:41 Найти цитируемый пост)
...периодически возникают мелкие конфликты и плетутся интриги...

Искренне сочувствую. Действительно, в такой атмосфере хочешь или не хочешь, а придется проявить изобретательность (просто, чтобы выжить).
Цитата(Се ля ви @  6.6.2008,  11:41 Найти цитируемый пост)
...смотрите только на сигнатуры методов и вам сразу же всё становится понятно...

Идеала нет. Следовательно, когда-нибудь кому-то может быть придется анализировать все ваши классы целиком и полностью. Громоздкая иерархия, множество зависимостей здорово осложнит жизнь вашему приемнику (или кому-то еще).
Цитата(Се ля ви @  6.6.2008,  11:41 Найти цитируемый пост)
...пожалуйста, поймите мою позицию...

Да я все прекрасно понял. Я ведь уже пожелал и удачи, и счастливого программирования. Просто, как я уже написал выше, решил потрепаться. Извиняюсь, что испытываю ваше терпение.
Цитата(Се ля ви @  6.6.2008,  11:41 Найти цитируемый пост)
...феноменальная гибкость JavaScript не позволяет ни разработать действительно хорошей среды разработки для сценариев... ни написать крупных приложений разными людьми

Вот об этом и речь. Не для этих целей был создан, не для programming in the large. Хотя дальновидный создатель языка многое задумывал изначально (это видно из списка 7.5.3 Future Reserved Words), но все это пока не реализовано. 
А так вспоминается выражение, которое некоторые употребляют на форумах (мне оно не нравиться, но почему-то вспомнилось): "вырезание гланд перректально".
Цитата(Се ля ви @  6.6.2008,  11:41 Найти цитируемый пост)
опыт из Java я привлекаю по мере необходимости.

Буду внимательно следить за новостями в области ваших разработок. ;)
Цитата(Се ля ви @  6.6.2008,  11:41 Найти цитируемый пост)
Вы же, насколько я понимаю, пытаетесь обозначить мне потолок... 

Да Вы что? Как Вам такое в голову пришло? Я тогда уж лучше умолкну...
Цитата(Се ля ви @  6.6.2008,  11:41 Найти цитируемый пост)
...лет 7 уже на JavaScript пишу.

У-у-у! В два раза дольше, чем я! ;)
Цитата(Се ля ви @  6.6.2008,  11:41 Найти цитируемый пост)
Просто другие мои коллеги используют GWT - слышали о таком?

Не-а, не слышал.
Цитата(Се ля ви @  6.6.2008,  11:41 Найти цитируемый пост)
А смысла этого метода "isFunction" я чего-то вообще не понял. Можно же просто спросить typeof fn == 'function' - и всё...

Это как раз пример попытки написать "железную проверку" на все случаи жизни. Она такая потому, что  предназначается для работы с таким нестабильным, по-разному (местами непредсказуемо) реализованным DOM API.
А по-поводу "просто спросить" – спросите у Safari, к примеру, typeof (document.images), и она Вам ласково ответит, что тип есть function. ;)
PM MAIL   Вверх
Се ля ви
Дата 6.6.2008, 13:11 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Java/SOAрхитектор
****


Профиль
Группа: Модератор
Сообщений: 2007
Регистрация: 5.6.2004
Где: place without tim e and space

Репутация: 5
Всего: 126



Цитата(AKS @  6.6.2008,  09:34 Найти цитируемый пост)
А Вас не волнует та цена, которую придется платить за такие возможности в J(ava)Script?
Если Вы делаете примерно то же самое в Java-программе, то ваши методы ведь будут общими для всех экземпляров, так ведь? А что происходит в данном случае? Всякий раз, наряду с приватной переменной, Вам необходимо создать пару методов (геттер и сеттер), которые будут уникальны для каждого экземпляра. Т.е. с каждым объектом-экземпляром создаются еще объекты-функции, размещаемые в динамической памяти. Нужны ли такие программные единицы, отрицательно влияющие на потребление памяти и снижающие производительность, да еще и в "приложениях", где критически важны время отклика и интерактивность?

В общем, пока неудачно.

Попытался загнать эти объекты в свойства конструктора и, единожды создав, потом присваивать его всем экземплярам:
Код
    if (this.constructor.getPrivateField)
        this.getPrivateField = this.constructor.getPrivateField;
    else
        /**
         * Getter method of "privateField" property
         * @return {number}
         */
        this.constructor.getPrivateField =
            this.getPrivateField = function(){
                return _privateField;
            }


Но после преобразования объявления всех методов по такой методике получилось следующее:
Код
/**
 * Sample constructor for demonstrate Inheritance - parent
 * @constructor
 * @type {function}
 * @param {number} privateField
 */
var A = function(privateField) {
    
    /** @type {function} */
    this.constructor = A;
    
    /** @type {object} */
    var _super = this.constructor.prototype;
    
    /** @type {number} */
    var _privateField;
    
    if (this.constructor.getPrivateField)
        this.getPrivateField = this.constructor.getPrivateField;
    else
        /**
         * Getter method of "privateField" property
         * @return {number}
         */
        this.constructor.getPrivateField =
            this.getPrivateField = function(){
                return _privateField;
            }
    
    if (this.constructor.setPrivateField)
        this.setPrivateField = this.constructor.setPrivateField
    else
        /**
         * Setter method of "privateField" property
         * @param {number} privateField+
         * @return {A} this object (ParamBean pattern - @see http://se-la-vy.blogspot.com/2008/04/paramsbean.html)
         * @throws NullPointerException when argument is not presented
         * @throws IllegelArgumentException when argument is not number literal or Number object
         */
        this.constructor.setPrivateField =
            this.setPrivateField = function(privateField) {
                
                if (privateField == null) throw new NullPointerException('argument "privateField" is not presented!');
                
                if (typeof privateField == 'number')
                    _privateField = privateField;
                else
                    if (typeof privateField == 'object' && privateField instanceof Number)
                        _privateField = privateField.valueOf();
                    else
                        throw new IllegelArgumentException('privateField param is not a number');
                
                return this;
            }
    
    if (this.constructor.init)
        this.init = this.constructor.init;
    else
        /**
         * Method for initialization of object state
         * @param {number} privateField+
         * @throws NullPointerException when argument is not presented
         * @throws IllegelArgumentException when argument is not number literal or Number object
         */
        this.constructor.init =
            this.init = function(privateField) {
                if (_super.init) _super.init();
                
                this.setPrivateField(privateField); 
                
                delete this.init;
            }
    
    if (privateField != null)
        this.init(privateField);
}

/**
 * Sample constructor for demonstrate Inheritance - child
 * @constructor
 * @extends {A}
 * @type {function}
 * @param {number} privateField?
 */
var B = function(privateField) {
    
    /** @type {function} */
    this.constructor = B;
    
    /** @type {A} */
    var _super = this.constructor.prototype;
    
    /** @type {A} */
    this.constructor.prototype = new this.constructor.prototype.constructor;
    
    if (this.constructor.init)
        this.init = this.constructor.init;
    else
        /**
         * Method for initialization of object state
         * @param {number} privateField+
         * @throws NullPointerException when argument is not presented
         * @throws IllegelArgumentException when argument is not number literal or Number object
         */
        this.constructor.init =
            this.init = function(privateField) { 
            
            if (_super.init) _super.init(privateField);
            
            delete this.init;
        }
    
    if (privateField != null)
        this.init(privateField);
}
B.prototype = new A;

/**
 * @constructor
 * @param {string} message
 */
var IllegelArgumentException = function(message) {
    /** @type {function} */
    this.constructor = IllegelArgumentException;
    
    alert('IllegelArgumentException: ' + (
        this.message = message
    ));
},
    NullPointerException = function(message) {
    /** @type {function} */
    this.constructor = NullPointerException;
    
    alert('NullPointerException: ' + (
        this.message = message
    ));
}

/** @type {B} */
var b1 = new B(5),
    b2 = new B(6);

alert('b1.getPrivateField() = ' + (
    b1.getPrivateField()
));//'b1.getPrivateField() = 5'

alert('b2.getPrivateField() = ' + (
    b2.getPrivateField()
));//'b2.getPrivateField() = 5' - а не 6!.. :(

Т.е. функция, будучи связана уже с другим объектом, почему-то продолжает сохранять связь с private-полем того объекта, в котором была создана.
Это, надо полагать, объясняется поведением замыкания (closure), благодаря которому и стало возможным использование private-полей и privileged методов:
Цитата(http://www.jibbering.com/faq/faq_notes/closures.html)
A "closure" is an expression (typically a function) that can have free variables together with an environment that binds those variables (that "closes" the expression).

Ищу решение smile


--------------------
  )
 (
[_])
проф. блог

Кролики думали, что занимаются любовью, а на самом деле их просто разводили...
PM MAIL WWW Skype GTalk   Вверх
Се ля ви
Дата 11.6.2008, 17:25 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Java/SOAрхитектор
****


Профиль
Группа: Модератор
Сообщений: 2007
Регистрация: 5.6.2004
Где: place without tim e and space

Репутация: 5
Всего: 126



Фу-у-у-ух! Дошли! Несколько подзадолбался, честно говоря. Но, вроде, получилось. В итоге вышла довольно-таки громоздкая конструкция, но что делать? ECMAScript такой, какой он есть smile
Цитата(М. Исаковский)
.., но ты и дорог мне такой.


Итак, проблема с предыдущей (будем в дальнейшем называть её простой) реализацией наследования заключалась в том, что, как правильно заметил AKS,
Цитата(AKS @  6.6.2008,  09:34 Найти цитируемый пост)
Всякий раз, наряду с приватной переменной, Вам необходимо создать пару методов (геттер и сеттер), которые будут уникальны для каждого экземпляра. Т.е. с каждым объектом-экземпляром создаются еще объекты-функции, размещаемые в динамической памяти. Нужны ли такие программные единицы, отрицательно влияющие на потребление памяти и снижающие производительность, да еще и в "приложениях", где критически важны время отклика и интерактивность?


Сразу скажу, я перепробовал массу приёмов и вариантов того, как обойти эту проблему. Если я буду расписывать всё, что я пытался и у меня не получалось, это затянется надолго, по-этому отброшу все свои блуждания, которые в итоге упёрлись в тупик и расскажу только правильный вариант.

Итак, проблема в следующем: в Java у нас действительно есть:
  • класс, в котором хранятся все методы и
  • объекты, в которых хранятся значения всех полей.
Каждый объект ссылается на свой класс и, когда у него вызывается какой-либо метод, он выполняется в контексте этого самого объекта - т.е. производит различные действия с его переменными. А в простой модели наследования у меня получилось, что КАЖДЫЙ объект содержит у себя весь комплект методов. Нетрудно догадаться, что такая модель, конечно же, работать будет, но при выстраивании более или менее больших деревьев наследования такой сценарий будет слишком быстро съедать память. Так что нам нужна альтернативная модель реализации наследования, которая не будет допускать такого положения вещей.

Итак, очевидно, что для того, что бы сэкономить на объектах-функциях, нужно хранить их всех в одном месте и ссылаться на них из всех объектов. Лучшим местом для такого хранилища выглядит сам объект функции-конструктора: он всегда доступен из всех объектов по прямой ссылке(чего мы добились в предыдущей теме). Проблема же состоит в том, что тогда, как было показано в предыдущем посте, эти функции уже не смогут использовать часть контекста - а именно privat`ные переменные. При этом public-переменные функция возьмёт правильные, потому что они окажутся доступны из контекста выполнения - ведь они открыты для использования извне.

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

Privat`ные переменные доступны только в том контексте, где объявляются - и больше нигде. Так как же нам сделать такой контейнер, который бы их содержал? Думаю, ответ очевиден - наилучшим кандидатом на роль такого контейнера является сам объект! smile Действительно, ведь у объекта уже есть все поля, которые нам нужны для выполнения его функций?
Но если это будет обычный объект, то мы возвращаемся к изначальной проблеме - у каждого объекта свой набор функций. Так что на этот объект, который будет являться контейнером функций, в силу его специфики, нужно наложить ряд ограничений:
1. Он не должен содержать реальных данных, только никуда не ведущие (null) ссылки private-полей, public-полей не должно быть вовсе.
2. Он не должен инициализироваться (вобщем-то вытекает из первого).
3. У него не должно быть предков (что бы не создавать лишнего экземпляра предка), т.к. методы предков - это те же public-поля, которые будут вызываться по цепочке прототипов от других объектов. Т.е. этот экземпляр в качестве прототипа будет довольствоваться стандартным прототипом по-умолчанию, т.е. объектом с единственной явной ссылкой "constructor" - (кстати по-этому ему не будет нужна ссылка "constructor").
4. Он должен будет обеспечивать механизм подтягивания в свой контекст из объектов и отправления свойств обратно из своего контекста в объекты.

Ссылку на этот контейнер функций, как и говорилось выше, удобнее всего хранить в особом свойстве конструктора. Я предлагаю назвать это свойство "_class", т.к. в некотором смысле, он действительно соответствует классу в понимании ОО-языков программирования, основанных на классах. Так что дальше мы так и будем называть этот объект классом.

С другой стороны, если обычные объекты не содержат функций, тогда зачем им хранить private`ные переменные? Кто с ними будет работать? Никто. Так что они в объектах и не нужны. Нужно только обеспечить хранилище для private`ных переменных.

Почему бы эти хранилища не содержать в самом классе, спросите вы? Например, в массиве и в объекте содержать только ссылку-идентификатор на соответствующее ему хранилище private`ных переменных? Потому что тогда мы не сможем использовать "сборку мусора" для их удаления - ведь как узнает класс о том, что какой-то объект пользователю уже не нужен и, следовательно, его коллекцию private`ных переменных нужно удалить? Никак. Так что эта коллекция должна находиться в объекте и умирать вместе с объектом.

Первое и второе условие реализовать довольно просто - нужно объявлять приватные переменные только в том случае, если объект создаётся первый раз (узнать об этом можно по отсутствию ссылки "_class" у конструктора), в противном же случае создавать public-поля, считывать и присваивать объекту ссылки на функции и затем проводить инициализацию, если это нужно.

Третье условие реализуется просто присваиванием ссылке "_class" конструктора значения нового объекта ДО присвоения прототипа классу.

Четвёртое - самое интересное. smile Как обеспечить передачу полей "своему" и сохранить при этом их приватность? Над этой задачей я бился долго. Решение я нашёл в том, что бы использовать специальную функцию, генерирующую объект-контейнер для private-полей объекта. Значения переменных из этого контейнера нужно присваивать полям класса каждый раз в начале выполнения каждого метода и сохранять из класса в этот контейнер каждый раз после выполнения любого метода.

Этот контейнер я назвал privateState и решил сохранять его в public-поле объекта, к которому он относится. Метод же, который генерирует его, я решил назвать, как нетрудно догадаться, "createPrivateState"...

<Продолжение следует... Сейчас надо бежать, допишу сегодня ближе к ночи.>


--------------------
  )
 (
[_])
проф. блог

Кролики думали, что занимаются любовью, а на самом деле их просто разводили...
PM MAIL WWW Skype GTalk   Вверх
dsCode
Дата 11.6.2008, 20:48 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 565
Регистрация: 8.9.2007
Где: Saint-Petersburg

Репутация: 19
Всего: 26



Се ля ви, не могу не отдать должное за такие подробные блоги о Ваших исследованиях =) Однако, боюсь, вряд ли это столь актуально в ООП языке с динамическими mutable-объектами.

Более того, версия 2.0. сулит нам классы (с private'ами, public'ами и т.д.), но классы там будут так же динамическими (хотя, может и будут разграничения на статические и динамические) - все так же можно будет достучаться до прототипов и поменять все, что нужно. Это я к тому, что концепции, подобной строго-статичным классам из Java, C++, PHP здесь не будет. Есть ли смысл притягивать ее сюда за хвост (и очень сильно притягивать - "В итоге вышла довольно-таки громоздкая конструкция") - не знаю (с учетом того, что идеология, описанная в последнем посте - это явный избыток кода, ресурсов, анти-KISS), - не знаю .. (разве, что желание подогнать это под Java-концепции) - Вам решать =) 
 
А классы в 2.0. будут в концепции, более схожей с Python'ом (нежели с Java) - там так же, как и в JS (кстати, именно из-за любви к JS, мне очень понравился Питон, хотя до этого (и по долгу службы) на серверной стороне я занимался PHP), можно расширять свойство __class__ инстанцированного объекта (аналог свойства prototype) и новые свойства отобразятся на уже созданных инстансах класса. Приватные проперти там обозначаются двумя подчеркиваниями __private_property и не доступны извне (кроме геттеров / сеттеров) =) по прямому имени, но доступны по косвенному - имя преобразуется в ИмяКласса__private_property (т.е. obj.__private_property упадет по exception'у, что "попытка обращения к приватной переменной", но obj.ИмяКласса__private_property вернет нужное свойство). Кстати, может Вам лучше эту концепцию применить? ;) - сэкономите кучу ресурсов и код упростите (obj.getA() будет работать, но obj.a - уже нет).

Просто не понятно - от кого эти защиты? От программистов, которые будут использовать Вашу библиотеку псевдо-классов? Так они и так умеют программировать и понимают, что к чему. От программистов, которые отрицают концепции прототипного программирования? Не знаю... может им есть смысл увидеть разницу этих подходов и взять на вооружение оба. Ну а иначе, кроме как спортивного интереса подогнать идеи JS под статично-классовые концепции, я не особо вижу. А Рич-Эпликейшенс (и подобные GMail, которые Вы приводили) пишутся и без имитаций private'ов.

P.S.: тем не менее, не хочу сказать, что Ваши труды напрасны =) За один только объем статей можно руку пожать )

Это сообщение отредактировал(а) dsCode - 11.6.2008, 23:38


--------------------
the .code inside
:my music
PM MAIL WWW ICQ Jabber   Вверх
Се ля ви
Дата 12.6.2008, 01:58 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Java/SOAрхитектор
****


Профиль
Группа: Модератор
Сообщений: 2007
Регистрация: 5.6.2004
Где: place without tim e and space

Репутация: 5
Всего: 126



Итак, мы остановились на объекте privateState. Что бы он сохранял в себе информацию достаточно надёжно и не позволял получить к ней доступ извне никому кроме класса, данные в нём должны так же содержаться в private`ных переменных. При этом этот объект должен сам отвечать за выгрузку и загрузку этих данных в класс, т.е. должен иметь доступ к private`ным переменным класса. Как же этого добиться? Очень просто - генерировать его в таком месте, где он сохранит ссылки на private`ные переменные. Т.е. нам необходимо создать функцию генерации privateState`а внутри класса (и эта функция единственная не должна потом быть доступна из объектов).

Для выгрузки и загрузки данных этот объект должен обладать 2-мя методами. Я назвал их незамысловато - "_set" и "_get". Всё просто - "_get" присваивает переменным класса значения переменных, соответствующих экземпляру объекта, а "_set" присваивает переменным объекта значения переменных класса и возвращает тим ссылкам их значения null. Таким образом, каждый private-метод имеет смысл начинать с вызова this.privateState._get(); и заканчивать после вызова this.privateState._set(); с единственным условием - this.privateState._set(); должен быть вызван везде перед выражением "return". Для тех же методов, которые возвращают не пустой результат, соответствующий private-полю, придётся делать дополнительные действия, создавая новую переменную (обычно в этом случае она называется "result"), записывая в неё нужное значение, затем вызывая this.privateState._set(); и только после этого записывая return result;. Так же необходимо вносить вызов _set в случае возбуждения исключительных ситуаций (перед ключевым словом throw).

Для того, что бы вызов одними методами других методов внутри класса не вызывал постоянной перезаписи ссылок из privateState, необходимо внести так же переменную-счётчик вложенности методов в объект privateState - "_inner", что бы объект выполнял какие либо действия исключительно последовательно - вызвали метод "_get" первый раз - переменные подгрузились, счётчик принял значение 1, второй раз - просто присвоили счётчику значение 2, и т.д. Когда вызываем метод "_set", происходит проверка и декрементация счётчика - если это единица, ссылки копируются обратно в объект, иначе - кроме декрементации счётчика метод не делает ничего.

Типовая реализация будет выглядеть примерно так:
Код
var E = function() {
        /** @type {function} */ this.constructor = E;
        /** @type {object} */ var _super = this.constructor.prototype;
        //this.constructor.prototype = new this.constructor.prototype.constructor;
    
        /** @param {Object[]} args */
        this.init = function(args) {
            if (_super.init) _super.init();
    
            //...
    
            delete this.init;
            return this;
        }
    
        if (arguments[0] != null) this.init(arguments);
        else this.init();
        
    },

    F = function() {
        
        /** @type {function} */ this.constructor = F;
        
        if (this.constructor._class) {
            //Get privateState object
            this.privateState = this.constructor._class.createPrivateState(this.constructor.prototype);

            this.constructor.prototype = new this.constructor.prototype.constructor;
            
            //Load methods
            for (var m in this.constructor._class)
                if (m != 'createPrivateState')
                    this[m] = this.constructor._class[m];
            
            //Initialization
            if (arguments[0] != null) this.init(arguments);
            else this.init();

        } else {
            
            delete this.constructor;
            
            var _super,
                _privateField1,
                _privateField2;

            this.createPrivateState = function(super) {
                return new function() {
                    var _inner = 0,
                        __super = super,
                        __privateField1,
                        __privateField2;
                        
                    this._get = function() {
                        if (_inner++ == 0) {
                            _super = __super;
                            _privateField1 = __privateField1;
                            _privateField2 = __privateField2;
                        }
                    };
                    
                    this._set = function() {
                        if (_inner-- == 1) {
                            __super = _super;
                            __privateField1 = _privateField1;
                            __privateField2 = _privateField2;
                            
                            _super =
                            _privateField1 =
                            _privateField2 = null;
                        }
                    };
                };
            }

            this.init = function () {
                this.privateState._get();
                
                if (_super.init) _super.init();

                //...

                delete this.init;
                
                this.privateState._set();
                return this;
            }
            
            this.getPrivateField1 = function() {
                this.privateState._get();
                
                var result = _privateField1;
                
                this.privateState._set();
                return result;
            }
            
            this.setPrivateField1 = function(privateField1) {
                this.privateState._get();
                
                _privateField1 = privateField1;
                
                this.privateState._set();
                return this;
            }
        }

    };
F._class = new F;
F.prototype = new E;


//Тестируем:
var f1 = new F,
    f2 = new F;

alert('f1.setPrivateField1(2).getPrivateField1() = ' + f1.setPrivateField1(2).getPrivateField1()); //'f1.setPrivateField1(2).getPrivateField1() = 2'
alert('f2.setPrivateField1(3).getPrivateField1() = ' + f2.setPrivateField1(3).getPrivateField1()); //'f2.setPrivateField1(3).getPrivateField1() = 3'
alert('f1.getPrivateField1() = ' + f1.getPrivateField1()); //'f1.getPrivateField1() = 2'

alert('f1.setPrivateField1 === f2.setPrivateField1 = ' + (
    f1.setPrivateField1 === f2.setPrivateField1
)); //'f1.setPrivateField1 === f2.setPrivateField1 = true'


На последок маленький совет. Думаю, что разрабатывать какой-то новый функционал будет заметно удобнее с использованием простой модели наследования, а уже потом, когда всё в порядке, только не устраивает объём съедаемой памяти и скорость выполнения, можно переделывать её в эту, заметно более сложную модель.


--------------------
  )
 (
[_])
проф. блог

Кролики думали, что занимаются любовью, а на самом деле их просто разводили...
PM MAIL WWW Skype GTalk   Вверх
AKS
Дата 12.6.2008, 07:59 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Участник форума
**


Профиль
Группа: Участник
Сообщений: 725
Регистрация: 20.9.2006

Репутация: 27
Всего: 52



Цитата(dsCode @  11.6.2008,  20:48 Найти цитируемый пост)
Кстати, может Вам лучше эту концепцию применить? ;) - сэкономите кучу ресурсов и код упростите (obj.getA() будет работать, но obj.a - уже нет).

dsCode, это интересно! Но не понял, как реализовать то, что я подчеркнул?
Цитата(Се ля ви @  12.6.2008,  01:58 Найти цитируемый пост)
alert('f1.getPrivateField1() = ' + f1.getPrivateField1()); //'f1.getPrivateField1() = 2'

Се ля ви, а у меня другой результат. Вы уверены в том, что не "напортачили"?
PM MAIL   Вверх
dsCode
Дата 12.6.2008, 13:06 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 565
Регистрация: 8.9.2007
Где: Saint-Petersburg

Репутация: 19
Всего: 26



AKS

Цитата(AKS @  12.6.2008,  07:59 Найти цитируемый пост)
dsCode, это интересно! Но не понял, как реализовать то, что я подчеркнул?


Да не настолько и интересно =) - обычно. Ведь свойство, по сути, не является приватным (нет, лучше не так, а: вообще нет никаких приватных свойств, а вся приватность скрыта за согласованием именования). И я, естественно, говорил о простых (и единственных - public) свойствах. В Питоне, как я уже отмечал, это сделано преобразованием всех свойств с двумя подчеркиваниями к виду _ИмяКласса__имя_приватного_свойства (кстати, по официальному programming style guide'у, в Питоне имена свойств должны обозначаться через подчеркивание (в C-стиле), а не "верблюдом", как в Java и JavaScript - поначалу очень раздражает =)), но не суть =)). Пример (на синтаксис можно не обращать внимания):

Код

>>> class A:
    def __init__(self, a): # __init__ - в роли "конструктора", self (имя может быть любым, но принято - self) - в роли this
        self.setA(a)

    def setA(self, a): # def - объявление функции
        self.__a = a; # __a - private-свойство

    def getA(self):
        return self.__a;

    
>>> a = A(10) # инстанс класса
>>> a.getA()
10
>>> a.setA(20)
>>> a.getA()
20
>>> a.__a # будет ошибка
Traceback (most recent call last):
  File "<pyshell#30>", line 1, in <module>
    a.__a
AttributeError: A instance has no attribute '__a'
>>> a._A__a # "новое" свойство доступно как public, но это - бывшее "__a"
20

# до кучи (показать схожесть с JS)

>>> a.__class__.b = 30 # добавление нового свойства в класс (аналогично расширению прототипа)
>>> a.b
30
>>> A.c = 40 # или напрямую
>>> a.c
40
>>> 



Как это сделать в JS - без разницы. Можно просто назвать __a (и тогда obj.getA() { return this.__a; }, работает, а obj.a - нет (в смысле, не "нет", конечно же, а undefined, мы ж о JS говорим))

P.S.: у меня ощущение, что я написал много лишних ненужных слов (когда коротко: для обеспечения легковесных объектов (особенно для Rich-Applications) нам не нужны замкнутые var'ы для имитации именно private'ов из статично-классовых языков, а можно обойтись именованием), но тем не менее =)

Се ля ви, ну получилось, что второй инстанс ссылается на методы первого (f1.setPrivateField1 === f2.setPrivateField1), но ведь в виду замыкания и состояние будет тем же, поэтому утверждение alert('f1.getPrivateField1() = ' + f1.getPrivateField1()); //'f1.getPrivateField1() = 2' === false (т.е. не 2 там будет, а 3, т.к. Вы сами меняете состояние f1, вызовом f2.setPrivateField1(3) строчкой выше).


Это сообщение отредактировал(а) dsCode - 12.6.2008, 14:10


--------------------
the .code inside
:my music
PM MAIL WWW ICQ Jabber   Вверх
Се ля ви
Дата 12.6.2008, 14:09 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Java/SOAрхитектор
****


Профиль
Группа: Модератор
Сообщений: 2007
Регистрация: 5.6.2004
Где: place without tim e and space

Репутация: 5
Всего: 126



Цитата(AKS @  12.6.2008,  07:59 Найти цитируемый пост)
Цитата(Се ля ви @  12.6.2008,  01:58 Найти цитируемый пост)
alert('f1.getPrivateField1() = ' + f1.getPrivateField1()); //'f1.getPrivateField1() = 2'

Се ля ви, а у меня другой результат. Вы уверены в том, что не "напортачили"? 

Народ, чуть-чуть обшибся smile Бывает.

Надо строчку "var _inner = true," в методе this.createPrivateState заменить на "var _inner = 0,". В предыдущем посте я уже поправил - теперь всё в порядке, нормально работает.

Цитата(dsCode @  12.6.2008,  13:06 Найти цитируемый пост)
Се ля ви, ну получилось, что второй инстанс ссылается на методы первого (f1.setPrivateField1 === f2.setPrivateField1), но ведь в виду замыкания и состояние будет тем же, поэтому утверждение alert('f1.getPrivateField1() = ' + f1.getPrivateField1()); //'f1.getPrivateField1() = 2' === false (т.е. не 2 там будет, а 3, т.к. Вы сами меняете состояние f1, вызовом f2.setPrivateField1(3) строчкой выше).

Да нет же, в том-то и весь фокус, что бы состояние соответствовало каждому объекту, иначе ничего не получится. Для этого я создание объекта-состояния и поместил внутрь класса - что бы замыкание позволило ссылаться и на переменные данного состояния и на переменные класса (переменные состояния в точности повторяют имена переменных класса, только вначале содержат два символа "_" в отличие от обычных переменных). Всё работает чётко, просто я сначала сделал флаг, а потом подумал про вложенные вызовы методов и решил изменить на счётчик, только там где у меня инициализировался флаг я забыл заменить его нулевой счётчик. smile

В общем, всё в порядке. smile


--------------------
  )
 (
[_])
проф. блог

Кролики думали, что занимаются любовью, а на самом деле их просто разводили...
PM MAIL WWW Skype GTalk   Вверх
Google
  Дата 20.2.2019, 23:32 (ссылка)  





  Вверх
Ответ в темуСоздание новой темы Создание опроса
Форум для вопросов, которые имеются в справочниках, но их поиск вызвал затруднения, или для разработчика требуется совет или просьба отыскать ошибку. Напоминаем: 1) чётко формулируйте вопрос, 2) приведите пример того, что уже сделано, 3) укажите явно, нужен работающий пример или подсказка о том, где найти информацию.
 
1 Пользователей читают эту тему (1 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | JavaScript: Общие вопросы | Следующая тема »


 




[ Время генерации скрипта: 0.1715 ]   [ Использовано запросов: 21 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.