43

TL;DR:

Do we need factories/constructors in prototypical OO? Can we make a paradigm switch and drop them completely?

The BackStory:

I've been toying with doing prototypical OO in JavaScript lately and find that 99% of OO done in JavaScript is forcing classical OO patterns into it.

My take on prototypical OO is that it involves two things. A static prototype of methods (and static data) and a data binding. We don't need factories or constructors.

In JavaScript these are Object literals containing functions and Object.create.

This would mean we can model everything as a static blueprint/prototype and a data binding abstraction that's preferably hooked straight into a document-style database. I.e. objects are taken out of the database and created by cloning a prototype with the data. This would mean there is no constructor logic, no factories, no new.

The Example code:

An pseudo example would be :

var Entity = Object.create(EventEmitter, {
    addComponent: {
        value: function _addComponent(component) {
            if (this[component.type] !== undefined) {
                this.removeComponent(this[component.type]);
            }

            _.each(_.functions(component), (function _bind(f) {
                component[f] = component[f].bind(this);
            }).bind(this));

            component.bindEvents();

            Object.defineProperty(this, component.type, {
                value: component,
                configurable: true
            });

            this.emit("component:add", this, component);
        }
    },
    removeComponent: {
        value: function _removeComponent(component) {
            component = component.type || component;

            delete this[component];

            this.emit("component:remove", this, component);
        }
    }
}

var entity = Object.create(Entity, toProperties(jsonStore.get(id)))

The minor explanation:

The particular code is verbose because ES5 is verbose. Entity above is a blueprint/prototype. Any actual object with data would be created by using Object.create(Entity, {...}).

The actual data (in this case the components) is directly loaded from a JSON store and injected directly into the Object.create call. Of course a similar pattern is applied to creating components and only properties that pass Object.hasOwnProperty are stored in the database.

When an entity is created for the first time it's created with an empty {}

The actual Questions:

Now my actual questions are

  • Open source examples of JS prototypical OO?
  • Is this a good idea?
  • Is it in-line with the ideas and concepts behind prototypical OOP?
  • Will not using any constructors/factory functions bite me in the ass somewhere? Can we really get away with not using constructors. Are there any limitations using the above methodology where we would need factories to overcome them.
Community
  • 1
  • 1
Raynos
  • 166,823
  • 56
  • 351
  • 396
  • 2
    Why should there be no constructor logic in general js oop? If in your particular case it isn't needed that's one thing, although I don't see anything inherent in the language that says "there is no need for constructor logic, *ever*." – davin Jun 29 '11 at 20:06
  • @davin I'm questioning whether constructor logic is actually necessary. That's the meat of the entire question, what are the limitations of this methodology that requires constructors. – Raynos Jun 29 '11 at 20:07
  • How about referencing code from big open-source project like for example jquery, yui, socket.io, express? – Alfred Jun 29 '11 at 20:08
  • 1
    @Alfred I havn't seen code like this in big open source projects, that's also part of the question. Are there any that use code like this. – Raynos Jun 29 '11 at 20:11
  • This question is overly broad, but raises a good point. The ability to manufacture objects *at the call site* is a powerful tool, akin to the ability to create unnamed functions. As with any tool, it can be misused. The fact that people are more likely to have encountered traditional OOP makes this tool underused though. – Alexandre C. Jun 29 '11 at 20:13
  • @Alexandre C. Can you recommend how I can improve or split off the question into multiple ones. I feel it's too tightly coupled to the example code / explanation. – Raynos Jun 29 '11 at 20:16
  • @Raynos: imho merely asking for practical uses of `Object.create` will trigger interesting answers from the community. – Alexandre C. Jun 29 '11 at 20:21
  • 1
    @Alexandre IMO, that would be different from what he's trying to ask right now. – jcolebrand Jun 29 '11 at 21:40
  • 2
    @jcolebrand: yes that would. But since most people are trained in "classical" OOP, you will always find someone telling you that in some particular design (good or not), constructors are the best tool. By stating the question the way I suggested, you gain an opportunity to assess where Object.Create shines. Asking "is it good to avoid using XXX in language YYY" amounts to restrict the tools you can use, which is by essence bad. – Alexandre C. Jun 29 '11 at 22:03
  • `var foo = Object.create(proto, ext).init()` when you need it? I do this. – Ryan Florence Jul 01 '11 at 20:59
  • @rpflo that's an option, feels dirty to me though, I prefer to wrap it in a factory rather then call `.init` – Raynos Jul 01 '11 at 21:01

4 Answers4

27

I don't think the constructor/factory logic is necessary at all, as long as you change how you think about Object-Oriented Programming. In my recent exploration of the topic, I've discovered that Prototypical inheritance lends itself more to defining a set of functions that use particular data. This isn't a foreign concept to those trained in classical inheritance, but the hitch is that these "parent" objects don't define the data to be operated on.

var animal = {
    walk: function()
    {
        var i = 0,
            s = '';
        for (; i < this.legs; i++)
        {
            s += 'step ';
        }

        console.log(s);
    },
    speak: function()
    {
        console.log(this.favoriteWord);
    }
}

var myLion = Object.create(animal);
myLion.legs = 4;
myLion.favoriteWord = 'woof';

So, in the above example, we create the functionality that goes along with an animal, and then create an object that has that functionality, along with the data necessary to complete the actions. This feels uncomfortable and odd to anyone who's used to classical inheritance for any length of time. It has none of the warm fuzziness of the public/private/protected hierarchy of member visibility, and I'll be the first to admit that it makes me nervous.

Also, my first instinct, when I see the above initialization of the myLion object is to create a factory for animals, so I can create lions, and tigers, and bears (oh my) with a simple function call. And, I think, that's a natural way of thinking for most programmers - the verbosity of the above code is ugly, and seems to lack elegance. I haven't decided whether that's simply due to classical training, or whether that's an actual fault of the above method.

Now, on to inheritance.

I have always understood inhertance in JavaScript to be difficult. Navigating the ins and outs of the prototype chain is not exactly clear. Until you use it with Object.create, which takes all the function-based, new-keyword redirection out of the equation.

Let's say we wanted to extend the above animal object, and make a human.

var human = Object.create(animal)
human.think = function()
{
    console.log('Hmmmm...');
}

var myHuman = Object.create(human);
myHuman.legs = 2;
myHuman.favoriteWord = 'Hello';

This creates an object which has human as a prototype, which, in turn, has animal as a prototype. Easy enough. No misdirection, no "new object with a prototype equal to the prototype of the function". Just simple prototypal inheritance. It's simple, and straightforward. Polymorphism is easy, too.

human.speak = function()
{
    console.log(this.favoriteWord + ', dudes');
}

Due to the way the prototype chain works, myHuman.speak will be found in human before it's found in animal, and thus our human is a surfer instead of just a boring old animal.

So, in conclusion (TLDR):

The pseudo-classical constructor functionality was kind of tacked on to JavaScript to make those programmers trained in classical OOP more comfortable. It is not, by any means, necessary, but it means giving up classical concepts such as member visibility and (tautologically) constructors.

What you get in return is flexibility, and simplicity. You can create "classes" on the fly - every object is, itself, a template for other objects. Setting values on child objects will not affect the prototype of those objects (i.e. if I used var child = Object.create(myHuman), and then set child.walk = 'not yet', animal.walk would be unaffected - really, test it).

The simplicity of inheritance is honestly mind-boggling. I've read a lot on inheritance in JavaScript, and written many lines of code attempting to understand it. But it really boils down to objects inherit from other objects. It's as simple as that, and all the new keyword does is muddle that up.

This flexibility is difficult to use to its fullest extent, and I'm sure I have yet to do it, but it's there, and it's interesting to navigate. I think most of the reason that it hasn't been used for a large project is that it simply isn't understood as well as it could be, and, IMHO, we're locked into the classical inheritance patterns we all learned when we were taught C++, Java, etc.

Edit

I think I've made a pretty good case against constructors. But my argument against factories is fuzzy.

After further contemplation, during which I've flip-flopped to both sides of the fence several times, I have come to the conclusion that factories are also unnecessary. If animal (above) were given another function initialize, it would be trivial to create and initialize a new object that inherits from animal.

var myDog = Object.create(animal);
myDog.initialize(4, 'Meow');

New object, initialized and ready for use.

@Raynos - You totally nerd sniped me on this one. I should be getting ready for 5 days of doing absolutely nothing productive.

Ryan Kinal
  • 17,414
  • 6
  • 46
  • 63
  • A solid introduction to `Object.create` but it doesn't answer the question "DO we need factories?" Are factories also a remnant of classical OO style programming? – Raynos Jun 29 '11 at 21:33
  • I did address that momentarily, I think the answer is "No, we don't NEED factories - but they might be good practice for avoiding code repetition." Also, as part of the definition of functionality in the "parent" object, there's nothing stopping you from creating an `initialize` function with all the arguments you need (and thus avoiding the need for a factory) – Ryan Kinal Jun 29 '11 at 21:48
  • 3
    `initialize` is the same as a constructor/factory in my book. The real question is can we change our attitude to OO design so that constructors are not neccessary. – Raynos Jun 30 '11 at 11:47
  • We can, in fact, design an object oriented system which does not use constructors and factories. The code in this answer shows a simple example. Explicit declaration (as shown above) requires a lot more knowledge about what your object needs, but proper documentation and error handling should cut down on the difficulty of using these particular methods. – Ryan Kinal Jul 04 '11 at 00:33
11

As per your comment that the question is mainly "is constructor knowledge necessary?" I feel it is.

A toy example would be storing partial data. On a given data set in memory, when persisting I may only choose to store certain elements (either for the sake of efficiency or for data consistency purposes, e.g. the values are inherently useless once persisted). Let's take a session where I store the user name and the number of times they've clicked on the help button (for lack of a better example). When I persist this in my example, I do have no use for the number of clicks, since I keep it in memory now, and next time I load the data (next time the user logs in or connects or whatever) I will initialise the value from scratch (presumably to 0). This particular use case is a good candidate for constructor logic.

Aahh, but you could always just embed that in the static prototype: Object.create({name:'Bob', clicks:0}); Sure, in this case. But what if the value wasn't always 0 at first, but rather it was something that required computation. Uummmm, say, the users age in seconds (assuming we stored the name and the DOB). Again, an item that there is little use persisting, since it will need to be recalculated on retrieval anyway. So how do you store the user's age in the static prototype?

The obvious answer is constructor/initialiser logic.

There are many more scenarios, although I don't feel the idea is much related to js oop or any language in particular. The necessity for entity creation logic is inherent in the way I see computer systems model the world. Sometimes the items we store will be a simple retrieval and injection into a blueprint like prototype shell, and sometimes the values are dynamic, and will need to be initialised.

UPDATE

OK, I'm going to try for a more real-world example, and to avoid confusion assume that I have no database and need not persist any data. Let's say I'm making a solitaire server. Each new game will be (naturally) a new instance of the Game prototype. It is clear to me that their is initialiser logic required here (and lots of it):

I will, for example, need on each game instance not just a static/hard-coded deck of cards, but a randomly shuffled deck. If it were static the user would play the same game every time, which is clearly not good.

I may also have to start a timer to finish the game if the player runs out. Again, not something that can be static, since my game has a few requirements: the number of seconds is inversely related to the number of games the connected player has won so far (again, no saved info, just how many for this connection), and proportional to the difficulty of the shuffle (there is an algorithm that according to the shuffle results can determine the degree of difficulty of the game).

How do you do that with a static Object.create()?

davin
  • 44,863
  • 9
  • 78
  • 78
  • 2
    I defiantly see your logic and arguments there. Could this be instead turned into a specific function that get's called when you load the data from the database? Or is there no semantic difference between a constructor/initializer and a database type conversion function that's hard coupled to a particular prototype. – Raynos Jun 29 '11 at 20:27
  • @Raynos, I see it as the same thing, but I'm all ears if you (or anyone else) feel otherwise. – davin Jun 29 '11 at 20:30
  • 1
    @davin one can argue this function only exists because your optimising the database. I believe it's an extension on the serialization/deserialization logic that reduces the size of objects in the database. It also acts as a behind the scenes thing rather then an active constructor you personally call. – Raynos Jun 29 '11 at 20:36
  • 1
    @Raynos, I disagree on both accounts: (1) take the "seconds alive" of my example, even if we saved it to the database it would require initialisation. I conclude from this that the constructor logic is necessary even when we aren't trying to optimise the database size. (2) I don't see the difference between behind the scenes and upfront in terms of a generic language discussion. But more importantly, if there's an admin entering new users into the system (for example) the constructor becomes necessary not just when initialising from the database... – davin Jun 29 '11 at 20:41
  • @davin as for (2) don't confuse a constructor with adding properties onto an object. You can simply create a clean object and add properties to it. You could even define getter/setters on the prototype. I also don't agree with (1) since that's a weird egde-case. You need to set up a setInterval to keep it upto date anyway, there's no reason to do it in a initializer when you can do it in the interval instead. Think of a better example for (1) that can't be solved with some kind of live-update interval. – Raynos Jun 29 '11 at 20:43
  • @davin I see your point, it's a valid point. I would semantically argue that your starting a game and thus a Game should have a `start` method that does this logic. What the difference is between `start` and a constructor is muddy waters. To be frank, I'm having difficulty imagining this any other way then by using a constructor. I think this will require a paradigm switch for me and a lot of first hand experimentation to properly counter whether a constructor is needed. – Raynos Jun 29 '11 at 21:13
  • The difference between a Game and `start()` is huge. A game has state. `start()` merely initializes that state to a specific point, and then initiates play sequencing. Yes, it's true that one would normally expect that creating a game equates to starting a game, until you abstract save/load points and board setup. In this case, the constructor would ensure that the game can load, that the pieces exist to make the game playable, and would do basic initialization. There would also exist a method on the Game to `load` various bits of the game from savefiles. Then `start()` would be called, again. – jcolebrand Jun 29 '11 at 21:17
  • In terms of invariants I dare say it's imperative that some of those things in the solitaire initialisation example be performed at the point of creation. So no matter how we consider some of that logic (as `start` or constructor) it needs to be tightly coupled with the object creation, proving the necessity of a non-static block of code tightly-coupled to object creation, namely, a constructor. – davin Jun 29 '11 at 21:27
  • @davin @jcolebrand Are you sure we're not under the illusion that we need constructors because we address the problem, think about the solution and design the solution with classical OO patterns in mind including maximising the use of a constructor? Can we think outside the box and realise we don't actually need constructor/factories? Right now, I can't. – Raynos Jun 29 '11 at 21:43
  • So long as you have an object that has members which must exist, then you will have a constructor. The need for a factory is vacant, in almost all circumstances, except as a design pattern. That is a business rule. Otherwise, what you lose by not having a constructor is you lose having specific members defined and with a specific value. Just because a constructor is not explicit, does not mean one does not exist. Let me make an example. – jcolebrand Jun 29 '11 at 21:45
  • Constructor logic is always necessary. An explicit constructor is not. No matter what, you will be constructing an object in some way. In a JavaScript context, use of `new` constitutes a constructor (unnecessary), but object initialization has to happen (necessary). – Ryan Kinal Jun 29 '11 at 23:55
2

Example of a staticly-clonable "Type":

var MyType = {
  size: Sizes.large,
  color: Colors.blue,
  decay: function _decay() { size = Sizes.medium },
  embiggen: function _embiggen() { size = Sizes.xlarge },
  normal: function _normal() { size = Sizes.normal },
  load: function _load( dbObject ) { 
    size = dbObject.size
    color = dbObject.color 
  }
}

Now, we could clone this type elsewhere, yes? Sure, we would need to use var myType = Object.Create(MyType), but then we're done, yes? Now we can just myType.size and that is the size of the thing. Or we could read the color, or change it, etc. We haven't created a constructor or anything, right?

If you said there's no constructor there, you're wrong. Let me show you where the constructor is:

// The following var definition is the constructor
var MyType = {
  size: Sizes.large,
  color: Colors.blue,
  decay: function _decay() { size = Sizes.medium },
  embiggen: function _embiggen() { size = Sizes.xlarge },
  normal: function _normal() { size = Sizes.normal },
  load: function _load( dbObject ) { 
    size = dbObject.size
    color = dbObject.color 
  }
}

Because we've already gone and created all the things we wanted and we've already defined everything. That's all a constructor does. So even if we only clone/use static things (which is what I see the above snippets as doing) we've still had a constructor. Just a static constructor. By defining a type, we have defined a constructor. The alternative is this model of object construction:

var MyType = {}
MyType.size = Sizes.large

But eventually you're going to want to use Object.Create(MyType) and when you do, you will have used a static object to create the target object. And then it becomes the same as the previous example.

jcolebrand
  • 15,889
  • 12
  • 75
  • 121
  • The difference between constructor and prototype/blueprint is subtle but I think it is there. There's also the fact that `MyType` get's created once and not every time we clone it. A constructor get's called for every clone. – Raynos Jun 29 '11 at 22:06
  • Only if it is a dynamic constructor. In C# we have a static constructor that only gets called once over the lifetime of the program for a static class. – jcolebrand Jun 29 '11 at 22:09
2

The short answer to your question "Do we need factories/constructors in prototypical OO?" is no. Factories/Constructors serve 1 purpose only: initialize the newly created object (an instance) to a specific state.

That being said, it is often uses because some objects need initialization code of some sort.

Let's use the component-based entity code you provided. A typical entity is simply a collection of components and few properties:

var BaseEntity = Object.create({},
{
    /* Collection of all the Entity's components */
    components:
    {
        value: {}
    }

    /* Unique identifier for the entity instance */
    , id:
    {
        value: new Date().getTime()
        , configurable: false
        , enumerable: true
        , writable: false
    }

    /* Use for debugging */
    , createdTime:
    {
        value: new Date()
        , configurable: false
        , enumerable: true
        , writable: false
    }

    , removeComponent:
    {
        value: function() { /* code left out for brevity */ }
        , enumerable: true
        , writable: false
    }

    , addComponent:
    {
        value: function() { /* code left out for brevity */ }
        , enumerable: true
        , writable: false
    }
});

Now the following code will create new entities based on the 'BaseEntity'

function CreateEntity()
{
    var obj = Object.create(BaseEntity);

    //Output the resulting object's information for debugging
    console.log("[" + obj.id + "] " + obj.createdTime + "\n");

    return obj;
}

Seems straight forward enough, until you go to reference the properties:

setTimeout(CreateEntity, 1000);
setTimeout(CreateEntity, 2000);
setTimeout(CreateEntity, 3000);

outputs:

[1309449384033] Thu Jun 30 2011 11:56:24 GMT-0400 (EDT)
[1309449384033] Thu Jun 30 2011 11:56:24 GMT-0400 (EDT)
[1309449384033] Thu Jun 30 2011 11:56:24 GMT-0400 (EDT)

So why is this? The answer is simple: because of prototype based inheritance. When we created the objects, there wasn't any code to set the properties id and createdTime on the actual instance, as is normally done in constructors/factories. As a result, when the property is accessed, it pulls from the prototype chain, which ends up being a single value for all entities.

The argument to this is that the Object.create() should be passed the second parameter to set this values. My response would simply be: Isn't that basically the same as calling a constructor or using a factory? It's just another way of setting an object's state.

Now with your implementation where you treat (and rightfully so) all prototypes as a collection of static methods and properties, you do initialize the object by assigning the values of the properties to the data from a data source. It may not be using new or some type of factory, but it is initialization code.

To summarize: In JavaScript prototype OOP - new is not needed - Factories are not needed - Initialization code is usually needed, which is normally done through new, factories, or some other implementation that you don't want to admit is initializing an object

Dino Gambone
  • 545
  • 3
  • 10
  • Nice to see you getting involved, yes any properties that belong to the the instance have to be set in `CreateEntity`. Anything that belongs on the prototype can be statically set. I agree that you can't get away with not having initilization somehow. – Raynos Jun 30 '11 at 17:05
  • I wanted to comment yesterday, but wasn't able to get to a PC long enough to actually type the code and answer up. :) – Dino Gambone Jun 30 '11 at 17:12