Некоторые коды могут произносить более тысячи слов:
/**
* Represents an amount of a resource
* @param {number} amount
* @param {string} type
*/
function Resource(amount, type)
{
var nAmount = amount;
var sType = type;
if (amount < 0)
{
throw new IllegalArgumentException("amount has to be positive");
}
/**
* @method Resource
* @return {number} amount of the resource
*/
this.getAmount = function()
{
return nAmount;
};
/**
* @method Resource
* @return {string} resource type
*/
this.getType = function()
{
return sType;
};
}
/**
* Addition of two resources produces a new resource with the sum amount
* the new object uses the old one as prototype
* @param {Resource} resource
* @return {Resource} new Resource object
*/
Resource.prototype.plus = function(resource)
{
if (!(resource instanceof Resource && this.getType() == resource.getType()))
{
throw new IllegalArgumentException("resources don't match.");
}
var newRes = Object.create(this); // create a new object based on the current one
// execute the Resource constructor on it
Resource.call(newRes, this.getAmount() + resource.getAmount(), this.getType());
return newRes;
};
Объектами ресурса являются объекты ValueObject, которые считаются неизменяемыми. Они возвращают новый объект в операции сложения. Теперь вместо того, чтобы просто вызывать «new Resource (args)» для создания возвращаемого нового объекта, я создал новый на основе старого объекта. Это также разрешает наследование.
Поскольку я начинаю использовать это на всех своих объектах ValueObject (на всякий случай, когда я хочу наследовать от них в будущем), я начал думать об этом немного больше.
JavaScript не допускает неизменных объектов. Тем не менее, объекты уязвимы прямым перезаписыванием метода или вызовом их конструктора Все, что я могу сделать, это решить, что это вредные привычки. К сожалению, ECMAScript5 «заморозить» еще не здесь, хотя мой шаблон совместим с ним.
Теперь, когда у меня есть «плохой стиль» вызова конструктора для неизменяемого объекта вместе с этим дублированием кода, я думаю о создании новой функции для инкапсуляции этой процедуры:
Object.recreate = function(proto, constructor, args)
{
var obj = Object.create(proto);
constructor.apply(obj, args);
return obj;
};
И для этого:
Resource.prototype.plus = function(resource)
{
// if ... {throw ...}
return Object.recreate(this, Resource,
[this.getAmount() + resource.getAmount(), this.getType()]);
};
Может быть, у кого-то есть идея для названия этой функции. «Воссоздать» - вот о чем я думал в первую очередь. Что вы думаете об этом шаблоне? Это чрезмерная абстракция? Должен ли я сохранить это для классов, я уверен, что я унаследую от? Я что-то упустил?
Редактировать: я вижу, я забыл упомянуть что-то важное, что не отражено в этой статье. ValueObject легко клонируются с Object.create. Их частные члены неизменны. Но как насчет сменных частных членов? Если set () вызывается для клона, он устанавливает исходный объект-прототип в замыкании! Когда мой Object.recreate воссоздает замыкание, эта проблема решена.
Так есть ли лучший способ наследования с закрытыми переменными? Почему все используют сахар для создания класса? Я так много читал о прототипизме, но до сих пор не знаю, как это сделать.