문제

Some code may say more than a thousand words:

/**
 * 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;
};

Resource objects are ValueObjects considered immutable. They return a new object on an addition operation. Now instead of just calling "new Resource(args)" to create the new object to return, I created a new one based on the old object. This also permits inheritance.

As I'm starting to use this on all my ValueObjects (just in case I want to inherit from them at some point in the future), I started to think about this a bit more.

JavaScript doesn't allow immutable objects. Yet, Objects are vulnerable by direct method overwriting or calling their constructor. All I can do is to decide that these are bad habits. Unfortunately ECMAScript5 "freeze" is not yet here, though my pattern is compatible with it.

Now that I have this "bad style" of calling the constructor on an immutable object along with this code duplication, I'm thinking of creating a new function to encapsulate this procedure:

Object.recreate = function(proto, constructor, args) 
{
    var obj = Object.create(proto);
    constructor.apply(obj, args);
    return obj;
};

And therefor:

Resource.prototype.plus = function(resource) 
{
    // if ... {throw ...}
    return Object.recreate(this, Resource, 
            [this.getAmount() + resource.getAmount(), this.getType()]);
};

Maybe somebody has a better idea for the name of this function. 'Recreate' is what my first thought was. What do you think about this pattern? Is it an over-abstraction? Should I save this for classes I'm sure I'll inherit from? Did I miss anything important?

Edit: I see I forgot to mention something important which doesn't reflect currently in this article. ValueObject's are easily cloned with Object.create. Their private members are unchangeable. But what about changeable private members? If a set() is called on the clone, it sets the original prototype object within the closure! As my Object.recreate recreates the closure, this problem is solved.

So is there a better way for inheritance with private variables? Why is everyone using sugar for class creation? I've read so much about prototypalism, I still don't have the hang of it.

도움이 되었습니까?

해결책

Coming back to this after years, I can say that the code I wrote then was flawed in mixing concepts. JavaScript can actually provide true private variables by closure. But since ValueObjects are immutable anyways, that private state cannot be changed, and it overcomplicates things to hide it.

Also, the ingenious Object.recreate is a kind of over-abstracted "factory method", that one should declare seperatedly for each class if one needs it.

Use composition over inheritance! Crockford gives great JavaScript insights: https://crockford.com/javascript/prototypal.html

다른 팁

I think copy or clone would be a better name. This article explains mechanisms for creating such generic functions.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top