What is meant by "public function can't be overridden if a patch is necessary." in Addy's description of the Revealing Module Pattern?
Asked Answered
T

3

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

Tripitaka answered 21/2, 2014 at 5:7 Comment(2)
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.Thrill
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?Flasher
M
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.

Maggi answered 21/2, 2014 at 10:40 Comment(14)
but actually, is such a "patch" not considered very clean to begin with?Flasher
There's nothing wrong with patching or overriding. It's the indiscriminate use that's a problem.Maggi
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.Skein
@user2734550, you should use the original variant of the Module Pattern, not RMP.Maggi
How does Revealing Module Pattern affects unit testing the makeGreeter object?Diazole
@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.Maggi
This example feels a little contrived as in and of itself doesn't seem to reflect how I ever consider using a "private" method.Baudekin
@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.Maggi
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.Baudekin
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.Maggi
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.Baudekin
@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.Maggi
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.Baudekin
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.Adulterer
A
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;
}
Alixaliza answered 23/2, 2015 at 14:18 Comment(0)
A
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.

Audryaudrye answered 15/5, 2018 at 15:7 Comment(1)
@ScottBeeson yes sure will consider this from next time.Audryaudrye

© 2022 - 2024 — McMap. All rights reserved.