简体   繁体   中英

What is the benefit of this pattern: loos augmentation

So, I have the need for a singleton. It really is a rather large "do something" object. Processes information etc.. it could be extended, and some methods could or might even be inherited, but overall, there doesn't need to exist more than one of them. So, I read a bit here which I love the concept: http://www.adequatelygood.com/JavaScript-Module-Pattern-In-Depth.html

I am thinking more in terms of leveraging the sub module behavior.

But, I'd like to break my obj into sub-modules. But I am not seeing the need to pass in the parent sub-module as the "return" on that parent gives me access anyways. ala. Perhaps I am missing the "robustness" or real usage here.

For example.

var a = {};

a.m = function(){

    var conf = {
        a: 'aaa',
        b: 'bbb'
    }

    var funcs = {
        func1: function(){
            console.log('a.m sub object func1');
        }

    }
    return {  // doing this gives me access
       conf: conf,
       funcs: funcs
    };
}()

// this sub module obj WILL need some behaviors/methods/vals in a.m
a.anothersub = (function(m){

   var anotherSub = m;
   anotherSub.funcs.func1(); // access to a.m methods do I even need to pass it in?
   a.m.funcs.func1();  // also access to a.m methods

}( a.m || {}))

// is a better approach to extend a.anothersub with a.m? 
// jQuery.extend(a.anothersub, a.m);

If both "m" and "anothersub" are part of object 'a'. Is there a need for loose or tight augmentation here and for sake of keeping code compartmentalized and of same function behavior, I am creating these "sub objects".

I read that article and felt I could leverage its power. But not really sure this is the best approach here, or even needed. Your thoughts?

This all comes down to how tightly-coupled your modules/submodules actually are, and how much you can expect them to exist in all places around your application (ie: every page of a site, or at the global level of an application, et cetera).

It's also broaching a couple of different topics.
The first might be the separation of concerns, and another might be dependency-inversion, while another, tied to both, might be code organization/distribution.

Also, it depends on how cohesive two submodules might be...

If you had something like:

game.playerFactory = (function () {
    return {
        makePlayer : function () { /*...*/ }
    };
}());

game.playerManager = (function (factory) { return {/*...*/}; }(game.playerFactory));

It might make sense to have the factory passed into the manager as an argument. At that point, attaching both to game is really just a convenient place to make both accessible to the global scope.

Calling game from inside of one or the other, however, is problematic, in large systems, systems with lots of submodules, or systems where the interface is still in flux (when are they not?).

// input-manager.js
game.inputManager = (function () {
    var jumpKey = game.playerManager.players.player1.inputConfig.jump;
}());

If all of your buttons are mapped out and bound to in that way, for every button for every player, then all of a sudden you've got 40 lines of code that are very tightly bound to:

  1. The global name of game
  2. The module name of playerManager
  3. The module-interface for playerManager ( playerManager.players.player1 )
  4. The module-interface for player ( player.inputConfig.jump )

If any one of those things changes, then the whole submodule breaks.
The only one the input-manager should actually care about is the object that has the .inputConfig interface.
In this case, that's a player object... ...in another case, it might be completely decoupled or stuck on another interface.
You might be half-way through implementing one gigantic module, and realize that it should be six smaller ones.

If you've been passing in your objects, then you really only need to change what you're passing in:

game.inputManager = (function (hasInput) {
    var jumpKey = hasInput.inputConfig.jump;
}(game.playerManager.players.player1));

Can easily become

game.inputManager = function (hasInput) {
    /*...*/
}(game.playerManager.getPlayer("BobTheFantastic").config));

and only one line of code changed, rather than every line referencing game. ...... game. ......

The same can be said for the actual global-reference:

// my-awesome-game.js
(function (ns, root) {
    root[ns] = { };
}( "MyAwesomeGame", window ));

// player-factory.js
(function (ns, root) {
    root[ns] = {
        make : function () { /*...*/ }
    };
}("playerFactory", MyAwesomeGame));

// player-manager.js
(function (ns, root, player) {
    var manager = {
        players : [],
        addPlayer : function () { manager.players.push(player.make()); }
    };
}("playerManager", MyAwesomeGame, MyAwesomeGame.playerManager));

Your code isn't impervious to change, but what you have done is minimize the amount of change that any one submodule needs to make, based on external changes.

This applies directly to augmentation, as well.

If you need to override some piece of some software, in a completely different file, 20,000 lines of code down the page, you don't want to have to suffer the same fate as changing interfaces elsewhere...

(function (override, character) {
    override.jump = character.die;
}( MyAwesomeGame.playerManager.get(0), MyAwesomeGame.playerManager.get(1) ));

Now, every time player 1 tries to jump, player 2 dies.
Fantastic.

If the interface for the game changes in the future, only the actual external call has to change, for everything to keep working.
Even better.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM