The Revealing Module is an anti-pattern

by I-Lin Kuo

who am i


Full stack developer Java/JavaScript

Columbia, MD

BLOG: ilinkuo.wordpress.com 

@ilinkuo

https://slides.com/i-linkuo




A couple of warnings



  • core JavaScript
  • opinion-based
  • lots of code
  • fast pace

The problem with POJOs




History



  • pre 2007: Plain old JavaScript object (POJO)
    •  _ means private
  • 2007 Module Pattern
  • 2007 Revealing Module Pattern

The Idea of the Module Pattern



Hide private members inside a closure

Original Module Pattern

  • Hide private members inside closure
  • Return an object literal
  • Define public methods in the literal
  • Reference through this

Revealing Module Pattern

  • Hide private members inside closure
  • Define all members in the closure
  • Reveal public members in the literal
  • Reference through closure

RMP Advantages

var RMPGreeter=(function(){
  var _openDoor=function(){/*...*/};
  var _name="Olaf";
  var _sayHello=function(){
    console.log("Hello, my name is " + _name);
  };
  var _greet=function(){
    _sayHello();
    _openDoor();
  }
  return {
    //name: _name,
    //sayHello: _sayHello,
    greet: _greet	
  };
})()
  • reveal/unreveal
  • easy rename
  • No this!

Lots of advantages for the coder

 



Revealing Module Pattern is bad for you.
You should find another pattern to love.

Stub Module Pattern

  • Hide private members inside closure
  • Create a stub return object
  • Attach public members to stub
  • Reference through stub






http://stackoverflow.com/questions/22906662/javascript-design-pattern-difference-between-module-pattern-and-revealing-modul/22918556#22918556

Tomato, Tamato?



Is it just a difference in coding style?

Or is there a real difference?


POOR OBJECT BEHAVIOR

trumps

EASE OF CODING

WWPD?



The public part of a module pattern implementation should behave like a POJO



Addy's warning


http://addyosmani.com/resources/essentialjsdesignpatterns/book/

Most people don't pay attention to it 



RMP returns "Hello, my name is Olaf" in both cases

Why does RMP fail?



This is what the warning means





RMP
Stub
Original



Wat kind of OOP is this!?

WWPD?
POJO picks up the changes
original and stub MP pick up changes
RMP doesn't
Java, Ruby, Python, ... all pick up changes





  stub orig RMP
simple override pass pass FAIL



The Revealing Module Pattern breaks simple extensibility
(Sometimes it works like you expect it to, 
sometimes it doesn't)

Simple Overrides


The Revealing Module Pattern works when:

  • your public methods don't call each other, or
  • you don't override called public methods




The speaker used to be/still is a

java programmer

Prototypal Inheritance


Once again, RMP returns "Hello, my name is Olaf"

two fails




  stub orig RMP
prototype override FAIL pass FAIL

Why does stub fail?



Results so far ...




  stub orig RMP
simple override pass pass FAIL
proto override FAIL pass FAIL




The Revealing and Stub Module Patterns break prototype extensibility

Prototype Extension


Revealing Module can be used for prototype when:

  • public prototype methods don't call each other, or
  • you don't override called public methods on the prototype


Love is irrational

Advanced Overriding


  • mixins: 
    • $.extend()
  • aspects:
    • cujojs/meld 
  • function wrappers
    • _.once
    • _.debounce
    • _.throttle
    • ...

Mixins



  Stub Orig RMP
mixin pass pass FAIL

Aspects


  Stub Orig RMP
mixin pass pass FAIL
aspect pass pass FAIL

signature




  stub orig RMP
simple override pass pass FAIL
proto override FAIL pass FAIL
adv override pass pass FAIL



I've covered the HOW RMP is bad.

Now the WHY?

Real beauty is on the inside
(of the function body)
 


 
  RMP w/ this
simple override pass
prototype pass
adv override pass




  RMP w/ stub
simple override pass
prototype FAIL
adv override pass



  stub w/ this
simple override pass
prototype pass
adv override pass

Skipped



Original Module Pattern w/ stub

Original Module Pattern using closure



How you reference public members
is the only thing that matters


Everything else is superficial.

WWPD?

  POJO stub orig RMP
simple override pass pass pass FAIL
proto override pass FAIL pass FAIL
adv override pass pass pass FAIL

The behavior of original module pattern resembles the POJO.

The original module pattern uses this internally, just like POJO

What would a POJO do?


Which pattern should you date?





The original Module Pattern has warts, too.




Examples are Too SimpLE


... and teaching by example isn't always enough






private1 -> private2 private2()
public -> private private()
public1 -> public2 this.public2()
private - > public ??

What do you do in the original module pattern when a private function wants to call a public function?



Take sayHello() and make it private

Easy for RMP



OK for stub



But ...


 

  naive original
simple override ERROR!
prototype ERROR!
adv override ERROR!



The private function has no this
How can it get name?


private -> public 

is really

public1 -> private -> public2



public1 can pass this to private

Ugly Solution



Please Use .call or .apply






The Missing Advice



When invoking a private function 
which uses a public member

.call(this) or .apply(this)

The complete Referencing Guide



private1 - > private2 private2()
public -> private private()
public1 -> public2 this.public2()
public1 -> private ... - > public2 private.call(this) or
private.apply(this)
,
this.public2()

Or ...




You could ALWAYS use .call(this) or .apply(this)




Revealing Module Pattern doesn't use this

vs.

You should always be passing this context

Functional/Prototype programming


  • Revealing Module
    • Don't use this, use the closure
  • Stub Module
    • Don't use this, use the return object
  • Original Module
    • Use this
  • Original Module + missing advice
    • Use this and call(this) and apply(this), avoid unnecessary closure


I lied

This talk isn't about the Revealing Module Pattern






This talk is about 

function references


Generalize to ALL object creation
  • constructor functions
  • methods that return an object

If you want your return object to be extensible, use 


this or .call or .apply




Refactor into PRototype



A constructor POJO can be refactored into a prototype

Refactor into Parent Closure


A regular function return object can be refactored into a parent closure

But when you Start hiding ...




You pay a price for secrecy

Hide with var self = this


hide sayHello in the closure


SELF = STUB!!!


Using var self = this
has the same pitfalls as the stub module pattern


  stub orig RMP
simple override pass pass FAIL
proto override FAIL pass FAIL
adv override pass pass FAIL

... and it doesn't refactor into prototype


Functions which use the closure can't get pulled into the prototype

... But reference correctly ...


... allows refactoring into prototype.
In practice, you can't always avoid using the closure.

Exercise for Reader


Work out the consequences of different hiding implementations with SnowmanFactory

(I'll fill this in with updated slides  ~ 2weeks)

Summary



  • If you're going to hide things in the closure, do it correctly!
  • Use this & call(this) & apply(this) as much as you can
  • Use the closure only if you can't use this
  • Object literal with _ convention keeps things simple

Contact info


ilinkuo.wordpress.com

Twitter: @ilinkuo

Feedback, please




http://speakerrate.com/talks/39201-the-revealing-module-is-an-antipattern

CREDITS


Olaf picture from Disney

The Revealing Module is an anti-pattern

By I-Lin Kuo

The Revealing Module is an anti-pattern

  • 2,935