• JavaScript面向对象的设计原则(二)


    这一期,我们来看下JavaScript面向对象设计中的核心--对象.
    面向对象的本质是将现实世界抽象层到计算机的世界,Class所体现的正是人类在观察世界中所经常进行的一项工作,分类.

    我们可能会经常整理屋子,然后将整理后的东西分门别类的放好.我们根据什么分类?电器,食物,书籍,这些属性是我们分类的依据,这些不同类别的用品都有什么功能?食物可以吃,可以加工,书籍可以看,电器可以使用.抽象到计算机的世界,于是就有了class,property,method,类,属性,方法.

    所以我们日常中所进行的活动,很多都是分类的活动,你在工作中生活中所做的分类活动就是一种面向对象的设计行为,相对于类的概念,对象更具体,粒度更小,我们可以将具有一类共性的对象称为类.

    当一个对象很具体,我们就称其为一个实例.

    示例

    var personInstance = {
      name:'jacky',
      age:60,
      six:'meal'
    }
    

    当一个对象很抽象,我们可以称其为类.

    示例

    var PersonClass = {
      name:'',
      age:'',
      six:''
    }
    

    这里我们没有使用任何JavaScript模拟类的技术,也没有用上ES6的class,但实际上这就是一个实例,一个类,从中我们可以看到,面向对象的设计其实是语言无关的,只要你能够表达出其中的含义,如何实现,并不是重点.


    JavaScript类设计

    抛开各种模拟类的语法糖,让我们直面本质来看看如何设计一个JavaScript类.

    1.合理封装

    封装的目的是找出代码中的变化点,将其包裹起来,通过统一的具有语义的API接口来输出结果,隐藏其中的实现和具体算法.

    示例

    function MenuTree () {
    	this.dataSource = [],
    	this.template = '<div id="menu-tree"><div>',
    	this.element = null,
    	this.init = function(domString){
    		var scope = this;
    		scope.element = document.querySelector(domString);
            scope.render = function () {
                scope.element.innerHTML = scope.template;
            };
            return scope;
    	}
    }
    //for example
    new MenuTree().init(domString).render();
    

    这里我们封装了一个MenuTree的类,这是一个用来初始化树形菜单的类,我们这么写问题不大,当然假设需求也很简单,模板是固定的,于是我们只要传入一个domString来将一个dom节点变成一个MenuTree就完成了,对使用者来说,他看代码就知道嗯,这个类使用init方法初始化,然后返回一个包含了render方法的对象,这个对象保存了对dom节点的引用,从而通过render方法来将模板写入节点.不需要过多的文档注释,代码能够表达这个类的含义.

    封装也是我们对旧代码不断重构的利器,不断的抽象出接口来推动软件系统的升级,基于封装的例子我们再来看看如何进行演化和重构.

    随着软件的扩大,我们需要用到很多其他的类于是我们又设计了一个List类:

    示例

    function List () {
    	this.dataSource = [],
    	this.template = '<div id="list"><div>',
    	this.element = null,
    	this.init = function(domString){
    		var scope = this;
    		scope.element = document.querySelector(domString);
    		scope.onBeforeRender();
            scope.render = function () {
                scope.element.innerHTML = scope.template;
                scope.onAfterRender();
            };
            return scope;
    	},
    	this.onBeforeRender = function(){
    		code...	
    	},
    	this.onAfterRender = function(){
    		code...
    	}
    }
    

    然后你会发现List类和MenuTree类有相似的地方,但是需求上更复杂,需要在render前后做些逻辑处理,于是我们将其重构,对List和MenuTree进行一次抽象,从而得到一个UI类.

    UI类示例

    function UI(constructor) {
        this.dataSource = [];
        this.template = constructor.template || '';
        this.element = null;
        this.onBeforeRender = constructor.onBeforeRender || function () {
                };
        this.onAfterRender = constructor.onAfterRender || function () {
                };
    }
    UI.prototype = {
        init: function (domString) {
            var scope = this;
            scope.element = document.querySelector(domString);
            scope.onBeforeRender();
            scope.render = function () {
                scope.element.innerHTML = scope.template;
                scope.onAfterRender();
            };
            return scope;
        }
    };
    

    List重构示例

    function List() {
        UI.apply(this, arguments)
    }
    temp = List.prototype.constructor;
    List.prototype = UI.prototype;
    List.prototype.constructor = temp;
    
    var myList = new List({
        template: '<h1>hello world</h1>',
        onBeforeRender: function () {
            //code...
        },
        onAfterRender: function () {
            //code...
        }
    });
    myList.init('ui-list').render();
    
    function MenuTree(){
    	UI.apply(this, arguments);
    }
    temp = List.prottype.constructor;
    ....
    
    var myMenu = new Menu()
    ....
    

    JavaScript重构的核心就是抽象出对象的共享属性和方法,至于如何实现继承,方法很多,ES6也提供了class的语法糖,大家可参考相关的文章和资料,但是核心在于如何设计抽象基类.

    2.基于职责设计

    类的职责值得是类实现的功能和承担的数据处理任务

    OOD的设计原则:

    1. 每个类的职责必须明确
    2. 每个类只承担一项职责

    避免过大的类,指那些能干一切事的类,他们就像上帝一样,我们称他们为God Object
    这里罗列一些GRASP(General Responsibility Assignment Software Pattern)原则:

    1. Creater
    2. Controller
    3. Pure Fabrication
    4. Information Expert
    5. High Cohesion
    6. Indirection
    7. Low Coupling
    8. Polymorphism
    9. Protected Variations

    Expert/Information Expert原则:

    某些功能需要某些必要的信息,这些信息在哪个类中,就让这个类承担这项职责
    在一些开发场景中,我们可能需要构造一个购物车用来存放用户选择的商品,我们可以称为ShoppingCart类
    对于用户选择的商品数量,商品价格等信息,均应该归属ShoppingCart类而不是前置的Customer类.

    Creator原则

    JavaScript中类本身也是对象,不过函数对象与普通的实例对象并不相同.关于这个原则,我们只要确保代码中构造对象的一致性即可

    Conroller原则

    前端对于MVC以及其各种变种应该是耳熟能详了,尤其在web开发领域,基于时间驱动模型的MVC非常适合用来开发各种web应用,但我们可能很熟悉View和Model的概念,但是对于Controller可能就很难去定义了.

    一般我们对于Controller代码的编写基于这几点,所谓Controller仅仅是控制者,调度者的概念,对于调度物体的内部实现不应该关心也不应该去干预,Controller仅仅应当包含从View到Model的访问代码以及Model返回给View的数据代码,不包含具体的业务逻辑,所谓业务逻辑,就是你的软件功能.

    示例

    //view
    var indexView = app.createView({
    	template:'<div>{name}</div>',
    	onBeforeRender:function(data){
    		//code....
    		return data;
    	}
    })
    //model
    var indexModel = app.createModel({
    	getData:$.post(url,function(){})
    	
    })
    //controller
    app.createController('indexController',function(){
    	indexModel.getData().then(function(data){
    		indexView.setData(data)
    	})
    })
    

    对于返回data的处理应当在indexView中的onBeforeRender中处理,而不应当去controller中ajax的回调中编写.这就是controller原则.

    High Cohesion/Low Coupling

    高内聚低耦合,含义很深啊,自己体会吧,骚年们,不过建议还是有的,记住组合优先于继承,那你的类就会高内聚低耦合.

    Indirection(间接性)原则

    对象之间相互关联,会形成复杂的交互网络,为减少耦合,通过创建一个中间对象将多对多的关系拆散为一对多关联.

    Pure Fabrication原则

    当某些功能很难找到特定归属的业务逻辑相关的类时,就创建一个新类,这是一个折衷的办法,折衷类我们称为Pure Fabrication,就是生造出来的类,惯例我们称为XXXXXService类.这也是实现低耦合高内聚的一种方法

    Protected Variations原则

    这个原则称为隔离变化,简单的说,当我们设计一个JavaScript类时应该考虑,如果上下文环境变换,这个类还能正常工作么?.简单的例子,如果你在一个UI类中对dom结构强要求,那么就很容挂了.

    Polymorphism 多态性原则

    如果一个类中对于不同状态的实现依赖于大量的switch和if/else if判断,那就应该考虑通过继承的方式,让不同的子类去处理.

    写着写着就下班了,祝大家新年快乐!

  • 相关阅读:
    根据界面上的button增加、删除、重命名文件夹,名字是数据库下面某一表单的某一列的名字
    打包测试的过程记录
    java中return的作用
    UVA
    UVA
    UVA
    HDU
    HDU
    spring技术详解
    Java对象的生命周期与垃圾回收以及四种引用
  • 原文地址:https://www.cnblogs.com/jackyKin/p/5183463.html
Copyright © 2020-2023  润新知