`

S.O.L.I.D五大原则之SRP(单一职责)

    博客分类:
  • js
阅读更多
文章转自:http://tech.ddvip.com

单一职责的描述如下:
一个类(JavaScript下应该是一个对象)应该有一组紧密相关的行为的意思是什么?遵守单一职责的好处是可以让我们很容易地来维护这个对象, 当一个对象封装了很多职责的话,一旦一个职责需要修改,势必会影响该对象想的其它职责代码。通过解耦可以让每个职责工更加有弹性地变化。

不过,我们如何知道一个对象的多个行为构造多个职责还是单个职责?我们可以通过参考Object Design: Roles, Responsibilies, and Collaborations一书提出的Role Stereotypes概念来决定,该书提出了如下Role Stereotypes来区分职责:
Information holder – 该对象设计为存储对象并提供对象信息给其它对象。
Structurer – 该对象设计为维护对象和信息之间的关系
Service provider – 该对象设计为处理工作并提供服务给其它对象
Controller – 该对象设计为控制决策一系列负责的任务处理
Coordinator – 该对象不做任何决策处理工作,只是delegate工作到其它对象上
Interfacer – 该对象设计为在系统的各个部分转化信息(或请求)

一旦你知道了这些概念,那就狠容易知道你的代码到底是多职责还是单一职责了。
实例代码
该实例代码演示的是将商品添加到购物车,代码非常糟糕,代码如下:

function Product(id, description) {
    this.getId = function () {
        return id;
    };
    this.getDescription = function () {
        return description;
    };
}
 
function Cart(eventAggregator) {
    var items = [];
 
    this.addItem = function (item) {
        items.push(item);
    };
}
 
(function () {
    var products = [new Product(1, "Star Wars Lego Ship"),
            new Product(2, "Barbie Doll"),
            new Product(3, "Remote Control Airplane")],
cart = new Cart();
 
    function addToCart() {
        var productId = $(this).attr('id');
        var product = $.grep(products, function (x) {
            return x.getId() == productId;
        })[0];
        cart.addItem(product);
 
        var newItem = $('<li></li>').html(product.getDescription()).attr('id-cart', product.getId()).appendTo("#cart");
    }
 
    products.forEach(function (product) {
        var newItem = $('<li></li>').html(product.getDescription())
                                    .attr('id', product.getId())
                                    .dblclick(addToCart)
                                    .appendTo("#products");
    });
})();


该代码声明了2个function分别用来描述product和cart,而匿名函数的职责是更新屏幕和用户交互,这还不是一个很复杂的例子,但匿名函数里却包含了很多不相关的职责,让我们来看看到底有多少职责:
首先,有product的集合的声明
其次,有一个将product集合绑定到#product元素的代码,而且还附件了一个添加到购物车的事件处理
第三,有Cart购物车的展示功能
第四,有添加product item到购物车并显示的功能
重构代码
让我们来分解一下,以便代码各自存放到各自的对象里,为此,我们参考了martinfowler的事件聚合(Event Aggregator)理论在处理代码以便各对象之间进行通信。
首先我们先来实现事件聚合的功能,该功能分为2部分,1个是Event,用于Handler回调的代码,1个是EventAggregator用来订阅和发布Event,代码如下:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312" />
<title>无标题文档</title>
<script type="text/javascript" src="js/mylib2.js"></script>
<script type="text/javascript" src="js/jquery.js"></script>
</head>

<body>

<ul id="products">

</ul>

<ul id="cart">

</ul>

<script type="text/javascript">
// 通过原型声明数组的forEach方法
if (!Array.prototype.forEach) {
    Array.prototype.forEach = function (fn, thisObj) {
        var scope = thisObj || window;
        for (var i = 0, j = this.length; i < j; ++i) {
            fn.call(scope, this[i], i, this);
        }
    };
}

// 事件类
function Event(name) {

	// 事件处理程序
    var handlers = [];
 
	// 事件名称
    this.getName = function () {
        return name;
    };
 
	// 追加事件处理程序
    this.addHandler = function (handler) {
        handlers.push(handler);
    };
 
	// 删除事件处理程序
    this.removeHandler = function (handler) {
        for (var i = 0; i < handlers.length; i++) {
            if (handlers[i] == handler) {
                handlers.splice(i, 1);
                break;
            }
        }
    };
 
	// 执行事件处理程序
    this.fire = function (eventArgs) {
        handlers.forEach(function (h) {
            h(eventArgs);
        });
    };
}
 
// 订阅和发布事件
function EventAggregator() {
    var events = [];
 
	// 取得事件
    function getEvent(eventName) {
        return $.grep(events, function (event) {
            return event.getName() === eventName;
        })[0];
    }
 
	// 执行事件对应的处理程序
    this.publish = function (eventName, eventArgs) {
        var event = getEvent(eventName);
 
        if (!event) {
            event = new Event(eventName);
            events.push(event);
        }
        event.fire(eventArgs);
    };
 
	// 绑定事件名称和事件处理程序
    this.subscribe = function (eventName, handler) {
        var event = getEvent(eventName);
 
        if (!event) {
            event = new Event(eventName);
            events.push(event);
        }
 
        event.addHandler(handler);
    };
}
 
// 产品实现类
function Product(id, description) {
    this.getId = function () {
        return id;
    };
    this.getDescription = function () {
        return description;
    };
}
 
// 购物车实现类
function Cart(eventAggregator) {
    var items = [];
 
    this.addItem = function (item) {
        items.push(item);
        eventAggregator.publish("itemAdded", item);
    };
}
 
// 购物车控制器,负责给购物车绑定处理程序
function CartController(cart, eventAggregator) {
    eventAggregator.subscribe("itemAdded", function (eventArgs) {
        var newItem = $('<li></li>').html(eventArgs.getDescription()).attr('id-cart', eventArgs.getId()).appendTo("#cart");
    });
 
    eventAggregator.subscribe("productSelected", function (eventArgs) {
        cart.addItem(eventArgs.product);
    });
}
 
// 产品库
function ProductRepository() {
    var products = [new Product(1, "Star Wars Lego Ship"),
    new Product(2, "Barbie Doll"),
    new Product(3, "Remote Control Airplane")];
 
    this.getProducts = function () {
        return products;
    }
}
 
// 产品控制器,负责取得产品,显示产品列表
function ProductController(eventAggregator, productRepository) {
    var products = productRepository.getProducts();
 
    function onProductSelected() {
        var productId = $(this).attr('id');
        var product = $.grep(products, function (x) {
            return x.getId() == productId;
        })[0];
        eventAggregator.publish("productSelected", {
            product: product
        });
    }
 
    products.forEach(function(product){
        var newItem = $('<li></li>').html(product.getDescription())
                            .attr('id', product.getId())
                            .dblclick(onProductSelected)
                            .appendTo("#products");
    });
}

(function () {
    var eventAggregator = new EventAggregator(),
        cart = new Cart(eventAggregator),
        cartController = new CartController(cart, eventAggregator),
        productRepository = new ProductRepository(),
        productController = new ProductController(eventAggregator, productRepository);
})();
</script>
</body>
</html>



注:五大原则分别是:
The Single Responsibility Principle(单一职责SRP)
The Open/Closed Principle(开闭原则OCP)
The Liskov Substitution Principle(里氏替换原则LSP)
The Interface Segregation Principle(接口分离原则ISP)
The Dependency Inversion Principle(依赖反转原则DIP)
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics