11

A disadvantage of this pattern is that if a private function refers to a public function, that public function can't be overridden if a patch is necessary. This is because the private function will continue to refer to the private implementation and the pattern doesn't apply to public members, only to functions.

Does anyone have an example of what he means by this?

Link to the Revealing Module Pattern referenced above

gogogadgetinternet
  • 5,719
  • 4
  • 24
  • 28
  • It means that doing something like `myRevealingModule.increment = newFunction;` doesn't change the inner workings of the module. `myRevealingModule.start` will still call the internal, private increment function. This can be good or bad, depending on the context. – Felix Kling Feb 21 '14 at 06:12
  • so "a patch" in this case, is it called Monkey Patching, or why is a patch like that necessary -- to fix a bug or to do something just to alter the object's behavior? – nonopolarity Mar 28 '14 at 21:11

3 Answers3

15

Compare an object created by using an object literal to one created by the Revealing Module Pattern.

Here is one created as an object literal.

function makeGreeter(name){
  return {
    getName: function(){ return name;},
    sayHello: function(){console.log("Hello, " + this.getName());}
  }
}

var greeter = makeGreeter("Danny");
greeter.sayHello; // "Hello, Danny"
greeter.getName = function(){ return "George";}
greeter.sayHello(); // "Hello, George"

When you override the public method getName on the returned object, the sayHello method which depends on getName picks up the change. This is because in the Object Literal style, references to public functions are made via this, the returned object.

However, when you use the Revealing Module Pattern,

function makeGreeter(name){
  var getName = function(){ return name;},
    sayHello = function(){console.log("Hello, " + getName());};
  return {
    getName: getName,
    sayHello: sayHello
  }
}

var greeter = makeGreeter("Danny");
greeter.sayHello; // "Hello, Danny"
greeter.getName = function(){ return "George";}
greeter.sayHello(); // "Hello, Danny"

The RMP greeter will not pick up the override to the public getName method. This is because when RMP functions reference other functions (both public and private), they refer to the private closure copy rather than to the public function attached to the returned object.

It is for this reason I regard the Revealing Module Pattern as an anti-pattern.

I-Lin Kuo
  • 3,220
  • 2
  • 18
  • 25
  • 2
    but actually, is such a "patch" not considered very clean to begin with? – nonopolarity Mar 28 '14 at 21:15
  • There's nothing wrong with patching or overriding. It's the indiscriminate use that's a problem. – I-Lin Kuo Mar 29 '14 at 19:33
  • Just curious, how do you fix the problem in 2nd example if you have to use RMP? what is your most flexible pattern in real project. Please bare with me, newbie here. – user2734550 Feb 06 '15 at 21:50
  • @user2734550, you should use the original variant of the Module Pattern, not RMP. – I-Lin Kuo Feb 08 '15 at 05:58
  • How does Revealing Module Pattern affects unit testing the `makeGreeter` object? – Șerban Ghiță Feb 16 '15 at 16:06
  • @serbanghita, there's no relationship between unit testing and RMP. It's just that the RMP is so anti-intuitive behavior from an OOP point of view that it's an anti-pattern. – I-Lin Kuo Feb 16 '15 at 23:43
  • 2
    This example feels a little contrived as in and of itself doesn't seem to reflect how I ever consider using a "private" method. – bigmadwolf Feb 17 '15 at 11:12
  • @pushplaybang. I think you've missed the point of the example. These examples override the exposed public members of your object. The user of your object shouldn't care whether you've implemented your public functionality using any private methods at all. The original module pattern acts exactly like an object literal with respect to override behavior on public members, while the RMP does not. – I-Lin Kuo Feb 17 '15 at 18:49
  • 2
    and yet when I read the code above, theres no big surprise, or usual "oh wait.... thats just javascript being weird...", or maybe I've spend too much with JS, but what you have above, does exactly what I would expect it to, maybe you could try explain this with a better example please? I don't get how this one proves anything other than the ability to create private methods that can't be overridden. – bigmadwolf Feb 17 '15 at 21:13
  • The example is already there in the first example. Both the object literal and the RMP greeter objects have exactly the same public members and the same base behavior. However, when a user overrides the public getName() method, the behavior of the two objects is different. Why should a user have to care how you've implemented your public methods? Which behavior is correct? In my opinion, the object literal is correct and RMP is wrong -- other OOP language objects act like object literal, not RMP. – I-Lin Kuo Feb 17 '15 at 22:33
  • 2
    Your example fails because its a poor implementation of the pattern and not because its the RMP. Its almost trivial to adjust your example to behave in the desired way and still allow you to have private methods and constants resulting in a cleaner API. – bigmadwolf Feb 18 '15 at 10:04
  • 1
    @pushplaybang, you're correct that it is trivial to adjust so that it behaves properly, but once you do that it no longer is the RMP. The major advantage of the Revealing Module Pattern is that you can make the decision to reveal /unreveal or rename members by manipulating only the returned object literal without changing the code in the rest of the module definition. Once you make the correction to the RMP, you lose that easy manipulation. – I-Lin Kuo Feb 18 '15 at 10:32
  • 2
    Theres always more than one way to skin a cat my friend, and in this case a RMP. The difference or advantage is not simply the return literal at the end being easy to manipulate. – bigmadwolf Feb 18 '15 at 13:15
  • simply by attaching a container property to `this` for holding functions to be treated in different ways, one can return using RMP and offer all forms of access js supports through the return values. – That Realty Programmer Guy Jul 15 '22 at 06:04
1

I would bind getName to this, which, it seems, points to the content returned in RMP.

function makeGreeter(name){
    this.getName = function(){ return name;};
    var _sayHello = function(){console.log("Hello, " + this.getName());};
    return {
            getName: getName,
            sayHello: _sayHello
    }
}

I prefer this, though:

function makeGreeter(name){
    this.getName = function(){ return name;};
    var _sayHello = function(){console.log("Hello, " + this.getName());};
    var API = {
        getName: getName,
        sayHello: _sayHello
    };
    return API;
}
A.L
  • 10,259
  • 10
  • 67
  • 98
Sumi Straessle
  • 1,116
  • 12
  • 23
1

The answer given by @I-Lin Kuo looks good, but for one case creates the confusion.

function makeGreeter(name) {
return {
    getName: function() {
        return name;
    },
    sayHello: function() {
        console.log("Hello," + this.getName());
    }
  }
}

var greeter = makeGreeter("Danny");
greeter.sayHello(); //"Hello,Danny"
greeter.getName = function() {
    return "George";
}
greeter.sayHello(); //"Hello,George"

Instead of greeter.sayHello it should have been greeter.sayHello(). Creates a lot of confusion.

Ivan
  • 34,531
  • 8
  • 55
  • 100
Chaitanya Babar
  • 269
  • 3
  • 12