美文网首页
单一职责原则的理解与实现

单一职责原则的理解与实现

作者: 木白no1 | 来源:发表于2018-01-07 12:32 被阅读177次

何谓单一职责原则

按字面理解,单一职责原则就是自己只负责自己的事,不需要理会别人的事。如果了解面对对象编程,那么应该会很容易了解这个单一职责原则。

在面对对象编程中,每个对象只负责自己的任务,比如该提供数据的就只是提供数据,该负责提供服务的就只提供服务,或者只是维护对象之间的关系,这样的开发方式代码耦合度较低,较灵活,易扩展。当然也可以一个对象负责多个任务,但是任务多了修改起来就比较容易影响到其他的任务。

Object Design: Roles, Responsibilies, and Collaborations这本书中提出可以从以下几方面判断出一个对象的多个行为构造出的是多职责还是单职责。
1、Information holder - 该对象设计为存储对象并提供对象信息给其他对象
2、Structurer - 该对象设计为维护对象与信息间的关系
3、Service provider - 该对象设计为处理任务与提供服务给其他对象
4、Controller - 该对象设计为负责控制一系列职责的任务处理
5、Coordinator - 该对象设计为把任务绑定/委托到其他对象上
6、Interfacer - 该对象设计为在各个对象间负责转化信息或者请求

一旦你知道了这些概念,那就狠容易知道你的代码到底是多职责还是单一职责了。

实现物品添加购物车

有这样的需求,有一些产品,需要以产品列表形式展示,并且提供双击产品添加到购物车。有一个购物车,接收产品添加到购物车的操作,添加之后并显示出来

这种简单的需求如果使用面对过程的方式来实现时很容易的,代码量也很少,但是不益于以后扩展。比如资源的来源、种类变了,或者添加方式变了,改一个东西都容易影响到其他逻辑,使用面对对象的方式来实现就会比较灵活,可以把这个需求抽象,然后再慢慢实现。

Paste_Image.png

简单抽象,就可以抽象出产品、购物车两个对象,购物车与产品需要通信所以需要一个通信的对象。再细化一下,把产品与购物车细分为信息提供与负责交互的两个对象并把这些对象转化为编程对象。

Paste_Image.png

由上可以抽象出6个对象,产品对象、事件对象、购物车对象均有一个负责对外的对象,类似于门面模式。Product、Event、Cart这三个对象比较灵活,可以复用或者拓展,当然实现起来会相对复杂一点,代码量也会多一点。但是这只是相对于简单不需要拓展的需求上,如果是比较庞大的需求或者是比较灵活的架构,使用面对对象编程的方式不仅可以节省代码,而且扩展维护更方便,使用哪种方式只是根据需求来选择了。

再对这些对象细化成基本工程,以下是基本交互思路图。

Paste_Image.png

以下是具体代码实现

Product.js 负责提供产品数据

function Product(id, description) {
    /**
     * 获取商品ID
     * 
     * @return {int   }  商品id
     */
    this.getId = function() {
        return id;
    };

    /**
     * 获取商品描述
     * 
     * @return {string} 商品描述
     */
    this.getDescription = function() {
        return description;
    }
}

module.exports = Product;

产品控制器ProductController.js, 负责对外交互

var ProductRepository = require('./ProductRepository.js');

function ProductController(productRepository, eventAggregator) {
    // 获取所有的物品
    var products = productRepository.getProducts();
    this.onProductSelect = function(id) {
        var product;
        products.forEach(function(pro) {
            if(pro.getId() == id){
                product = pro;
            }
            console.log('product is ' + pro.getId());
        });
        // 触发双击添加购物车事件
        eventAggregator.publish('productSelected', {
            product: product
        });
    }
    // 列出所有物品
    products.forEach(function(product) {
        var id = product.getId(),
            description = product.getDescription();

        console.log('product ' + id + ' , and description is ' + description);
        // 触发双击添加购物车
        if(id == 1){
            this.onProductSelect(id);
        }
    }.bind(this));
}

module.exports = ProductController;

购物车基本功能

function Cart(eventAggregator) {
    this.items = []; // 购物车列表
    /**
     * 添加物品到购物车
     * 
     * @param {object} item 商品对象
     */
    this.addItem = function(item) {
        this.items.push(item);
        console.log('add Item ' + item);
        // 触发添加购物车事件
        eventAggregator.publish('itemAdded', item);
    };
}

module.exports = Cart;

购物车控制器CartController.js ,负责购物车事件处理

function CartController(eventAggregator, cart) {
    // 订阅物品添加事件
    eventAggregator.subscribe('itemAdded', function(eventArgs) {
        var id = eventArgs.getId(),
            description = eventArgs.getDescription();

        console.log('the ' + id + 'has been add to cart &' + description);
    });

    // 订阅物品加入购物车事件
    eventAggregator.subscribe('productSelected', function(eventArgs) {
        console.log('recieved productSelected event ' + eventArgs);
        cart.addItem(eventArgs.product);
    });
}

module.exports = CartController;

事件对象Event.js,提供基本事件处理

function Event(name) {
    this.handlers = []; // 事件回调数组

    /**
     * 获取事件名称
     *              
     * @return {objecg} 事件对象
     */
    this.getName = function() {
        return name;
    };

    /**
     * 给事件添加处理函数
     *
     */
    this.addHandler = function(handler) {
        this.handlers.push(handler);
    };

    /**
     * 删除已经添加的事件处理函数
     * 
     * @param  {function} handler 需要删除的事件处理函数
     * @return {void}
     */
    this.removeHandler = function(handler) {
        var i = 0,
            len = this.handlers.length;
        for (; i < len; i++) {
            if(this.handlers[i] == handler){
                this.handlers.splice(i, 1);
                break;
            }
        }
    };

    /**
     * 执行事件处理函数
     * 
     * @param  {*} eventArgs 处理函数调用参数
     * @return {void}
     */
    this.fire = function(eventArgs) {
        this.handlers.forEach(function(h) {
            h(eventArgs);
        });
    }
}

module.exports = Event;

事件聚合器EventAggregator.js,负责提供发布、订阅方法给其他对象进行通信

var Event = require('./Event.js');

function EventAggregator() {
    this.events = []; // 事件对象集合

    /**
     * 根据事件名称获取事件对象
     * 
     * @param  {string} eventName 事件名称
     * @return {object}           事件对象
     */
    function getEvent(eventName) {
        var event;
        this.events.forEach(function(ev) {
            if (ev.getName() == eventName) {
                event = ev;
            }
        });
        //console.log('get event after ' + event);
        return event;
    }

    /**
     * 事件发布
     * 
     * @param  {string} eventName 事件名称
     * @param  {*     } eventArgs 调用事件处理函数时传的参数
     * @return {void}           
     */
    this.publish = function(eventName, eventArgs) {
        var event = getEvent.call(this, eventName);
        if (!event) {
            event = new Event(eventName);
            this.events.push(event);
        }
        event.fire(eventArgs);
    };

    /**
     * 订阅事件
     * 
     * @param  {string} eventName 事件名称
     * @param  {function} handler   事件触发时处理函数
     * @return {void}           
     */
    this.subscribe = function(eventName, handler) {
        var event = getEvent.call(this, eventName);
        if (!event) {
            event = new Event(eventName);
            this.events.push(event);
            console.log('add event ' + event.getName());
        }
        event.addHandler(handler);
    }
}

module.exports = EventAggregator;

以上代码就是购物车需求的实现,代码相对于面向过程变成的确挺多,但是职责清晰,易于理解。以上代码实现均参考汤姆大叔博客

相关文章

网友评论

      本文标题:单一职责原则的理解与实现

      本文链接:https://www.haomeiwen.com/subject/ofwjgttx.html