如何封装和创建可维护的JavaScript

时间:2014-07-30 15:46:49

标签: javascript design-patterns

我遇到了一个有趣的挑战,我有以下代码:

Sm.screenfragment(function (screen, viewModel) {
    //This can grow very quickly and turn into a mess
    var attribA = screen.get('title'),
        ... 
        ... 
        attribZ = screen.get('status');

    var setAttribA = function (container) {
        //Do Stuff
    };
    ...
    ...
    var setAttribZ = function(event, viewName) {
        //Do Stuff
    };

    //So this can grow hundreads of lines and get messy.
    return {
        model: {
            //Do Stuff
        },
        create: function () {
            //Do Stuff
        },
        prepare: function (callback, config) {
            //Do Stuff
        },
        enter: function () {
            //Do Stuff
        },
        exit: function (callback) {
            //Do Stuff
        }
    };
});

我尝试了一些想法,但它们只不过是搞乱了语法:

我考虑过添加一个新的util对象,我可以构造util对象,所以它只是增加了一点结构,但不多了。

Sm.screenfragment(function (screen, viewModel) {
    //Still can grow into a mess
    var util = {
            attribA : screen.get('title'),
            attribB : screen.get('status'),
            setAttribA : function (container) {
            //Do Stuff
            },
            setAttribB : function(event, viewName) {
            //Do Stuff
            }   
    };

    return {
        model: {
            //Do Stuff
        },
        create: function () {
            //Do Stuff
            util.setAttribA...
        },
        prepare: function (callback, config) {
            //Do Stuff
        },
        enter: function () {
            //Do Stuff
        },
        exit: function (callback) {
            //Do Stuff
        }
    };
});

然后使用点符号来获取属性,但这并不会使混乱消失。我正在重新阅读模块模式上的内容,看看我是否可以在这里应用一些东西,我有极端的情况,我可以在文件的顶部有几十个礼节和功能,它只是打破了结构。如何以更模块化的方式安排代码?所以它不会混乱。

5 个答案:

答案 0 :(得分:1)

你的问题不容易回答,因为缺少一些信息。哪种方法使用哪些属性?分析问题时应解决这个问题(不在设计中,绝对不在代码中)。那么你的代码的哪一部分要与其他部分分离?

在更一般的层面上回答这个问题:

如果您在一个模块内部达到高内聚并且模块之间的耦合低,那么它被认为是良好的面向对象设计(OOD)。在您的情况下,这意味着:如果您的所有方法都引用了所有属性,那么将它们全部保存在一个大文件中就被认为是好的OOD。但通常情况下,现实世界的问题并没有以如此单一的方式连接。

如果你想要分离某些东西,有Single responsibility principle说明你应该将不相互影响的部分分开。在您的情况下,您可以(可能)将关于attribA的所有内容放入一个模块中,将关于attribB的任何内容放入另一个模块中。因此,无论您使用的具体模块实现如何,它都不会搞砸。

答案 1 :(得分:1)

嗯,我至少在可读性方面采取的方法是将函数声明从退回块中拉出来:

Sm.screenfragment(function (screen, viewModel) {
    //Still can grow into a mess
    var util = {
            attribA : screen.get('title'),
            attribB : screen.get('status'),
            setAttribA : function (container) {
            //Do Stuff
            },
            setAttribB : function(event, viewName) {
            //Do Stuff
            }   
    };

    var create = function() {
        //Do Stuff
        util.setAttribA...
    };

    var prepare = function(callback, config) {
        //Do Stuff
    };

    var enter = function() {
        //Do Stuff
    };

    var exit = function(callback) {
        //Do Stuff
    };

    return {
        model: {
            //Do Stuff
        },
        create: create,
        prepare: prepare,
        enter: enter,
        exit: exit
    };
});

然后如果这些函数中的代码是通用/模块化的,将该代码拉出到实用程序文件中并从那里调用它们

答案 2 :(得分:1)

我同意@Waog,好的OOD可能是你问题的解决方案。鉴于您在编写setAttribA函数的对象上可能有很多属性,为什么不使用映射来清理代码呢?可能有很多JavaScript的地图实现,一个简单的谷歌搜索“ map polyfill ”引导我到这个,看起来不错:http://eriwen.github.io/smap.js/

答案 3 :(得分:1)

似乎有机会使用OO方法来封装类似的功能。每个类都可以存在于自己的文件中......这看起来很不错:

// In file attrs.js
function ScreenAttrs(screen) {
    this.screen = screen;
    this.attribA = screen.get('title');
    // ...
    // ... could even categorize attributes into separate methods
    // this.initFooAttrs();
    // this.initBarAttrs();
};

// In file modifier.js
var attrs = new ScreenAttrs(screen);

function AttribModifier(attributes) {
    this.attrs = attributes;
};

AttribModifier.prototype.setAttribA = function() {
    // .. do stuff w/ this.attrs.attribA
};
// ... etc.

// in api.js
var modifer = new AttribModifier(attrs);

function ScreenAPIImpl (modifier) {
    this.modifier = modifier;
};

ScreenAPIImpl.proxy = function(context, method) {
    return function() {
        return method.apply( context, args.concat( slice.call( arguments ) ) );
    };
};

ScreenAPIImpl.prototype.model = function (foo) {
    // operation with this.modifier.setAttribA
    // operation with this.modifier.attrs.attribA
};

ScreenAPIImpl.prototype.fetchAPI = function (screen, viewModel) {
    return {
        // Bind context of method to this object instance
        model: this.proxy(this, this.model),
        // ...
    };
};

// .. etc

var api = new ScreenAPIImpl(modifier);

Sm.screenfragment(api.fetchAPI(screen, viewModel));

这也打开了创建一个帮助器构建器类,它构造了所有东西并返回最终的API对象:

var api = CreateAPI(screen);

答案 4 :(得分:1)

让我们从您的代码中考虑以下摘录,我理解问题所在:

Sm.screenfragment(function (screen, viewModel) {
    //This can grow very quickly and turn into a mess
    var attribA = screen.get('title'),
    ... 
    ... 
    attribZ = screen.get('status');

    var setAttribA = function (container) {
        //Do Stuff
    };
    ...
    ...
    var setAttribZ = function(event, viewName) {
        //Do Stuff
    };

    ...

据我所知,在我看来,没有必要将属性attribA定义到attribZ然后设置它们,然后再为它们定义setter函数。您可以在需要的地方和时间返回或访问screen.get('x')

但如果由于某些原因绝对必要,那么jQuery推广的以下策略可能就足够了:

attributeX = function(x, container){
    // if x and container are undefined, then we can assume API caller intends to
    // read value of property 'x' otherwise, caller intends to write to the property.
    if(container){
        // don't forget to do stuff before setting!
        container.setProp(x); // you get the idea
    } else {
        // well we must have a 'container' to set a prop,
        // if we don't then caller must want to read.
        return screen.get(x);
    }
}

如果这个策略没有缓解这个问题,或者你认为我没有正确理解问题,那么请尝试让案例更清晰,让我们更接近目标。