【译】SOLID: Part 1 - 单一职责
【译】SOLID: Part 1 - 单一职责原文地址 作者:Patkos Csaba 第一次折腾,有什么不对的地方大家多多指正
单一职责(SRP),开闭原则,里氏代换原则,接口隔离原则以及依赖倒转原则。在编程的过程中应当牢记这五种敏捷原则。 定义
? 出自 Robert C. Martin 的著作 Agile Software Development,Principles,Patterns,and Practices 并在之后发行了 C# 版 Agile Principles,and Practices in C#,它是五种 SOLID 敏捷开发原则之一。它的意思是一个类应该只有一个发生变化的原因,意思很简单,但实现起来却比较棘手。 ? 但是为什么它会如此重要? ? 在静态编译语言中,有各种各样的原因会导致非期望的重新部署。如果一个类会因为两种原因改变,那么就可能会有两个不同的团队因为不同的原因去修改同一份代码。他们都会按自己的需求修改代码,在这种情况下对于一个编译语言(比如 C++,C# 或 Java),可能会导致模块不适用与其他团队或者这个应用的其他部分的问题。 使用者? 学会如何决定一个类的职责是什么不是一件容易的事。举个例子,开发者为了找出改变的原因回去分析用户行为。使用了我们提供的模块应用或系统的用户可能是一类想改变它的人。下面类出了一些模块和可能的使用者。
任务和角色? 将所有的实习和角色联系起来可能会很困难。在一家小公司里一个人可能需要承担不同的任务,然而在大公司里可能几个人承担一个任务。所以更合理的办法是考虑任务。但是任务这个东西比较难定义。什么是任务?我们怎么把它找出来?一种简单的办法是想一下负责这些任务的角色并将我们的用户和它联系起来。 ? 所以如果我们的使用者有理由改变它,那么角色应该能决定用户对象。这对于将实际的人和概念联系起来有很大的帮助。
改变的根源? 从这个原因上说,角色变成了方法集合发生改变的根源。由于角色需求的改变,方法集合也随之改变。
典型例子可以“打印”自己的对象? 假设我们有封装了书的内容和函数的类 class Book {
function getTitle() {
return "A Great Book";
}
function getAuthor() {
return "John Doe";
}
function turnPage() {
// pointer to next page
}
function printCurrentPage() {
echo "current page content";
}
}
? 这看起来是一个合理的类。它能够提供自己的标题,作者,并且可以翻页,也可以打印当前页面的内容。但是有一个小小的问题,让我们考虑一下操作 ? 混合的业务逻辑是不好的,因为它违反了 SRP。看一下下面的代码 class Book {
function getTitle() {
return "A Great Book";
}
function getAuthor() {
return "John Doe";
}
function turnPage() {
// pointer to next page
}
function getCurrentPage() {
return "current page content";
}
}
interface Printer {
function printPage($page);
}
class PlainTextPrinter implements Printer {
function printPage($page) {
echo $page;
}
}
class HtmlPrinter implements Printer {
function printPage($page) {
echo '<div style="single-page">' . $page . '</div>';
}
}
? 即使这个非常基础的例子也展示了业务逻辑的分离与单一职责,对于设计的灵活性有了很大的好处。 可以“保存”自己的对象一个和上面类似的例子,一个对象保存和恢复自己 class Book {
function getTitle() {
return "A Great Book";
}
function getAuthor() {
return "John Doe";
}
function turnPage() {
// pointer to next page
}
function getCurrentPage() {
return "current page content";
}
function save() {
$filename = '/documents/'. $this->getTitle(). ' - ' . $this->getAuthor();
file_put_contents($filename,serialize($this));
}
}
我们可以再次定义各种角色比如图书管理系统和持久数据管理。无论什么时候我们想改变持久数据, 我们需要改变这个类。当我们想从一页翻到另一页时,也需要改变它。这里有几种变化。 class Book {
function getTitle() {
return "A Great Book";
}
function getAuthor() {
return "John Doe";
}
function turnPage() {
// pointer to next page
}
function getCurrentPage() {
return "current page content";
}
}
class SimpleFilePersistence {
function save(Book $book) {
$filename = '/documents/' . $book->getTitle() . ' - ' . $book->getAuthor();
file_put_contents($filename,serialize($book));
}
}
新建一个类来保存数据相关的操作,这使得职责分开了,我们也能够更灵活的扩展它而不需要改变原有的 高层视图? 在我之前的文章中我频繁的提到和展示下图所示的高层架构 ? ? 如果我们分析这张图,可以看到单一职责原则的重要性。应用的入口和对象的创建在右边,一个角色对应一个职责。持久化数据被放在下面。一个分离的模块对应一个分离的职责。最后,在左边的是界面和分发机制。剩下要做的就是添加业务逻辑了。 程序设计注意事项? 当准备写一个程序时我们需要考虑很多因素。举个例子,涉及到多种需求的类将会导致一系列的变化。这些变化会是单一职责的一个来源。很可能发生的一种情况是影响一组方法的一组需求有可能会改变。 ? 为了迎合用户,我们会想满足尽可能多的需求,从这点上来说,软件的核心价值是易于改变,其次才是功能。然而,为了更好的实现第二点,第一点是必须实现的。为了很好的实现第二点,我们必须要有一个易于改变,易于继承,能容纳新方法的并实现了 SRP 的设计。 ? 让我们一步步证明上面的论点
所以当我们设计软件时我们应该:
一个不是很明显的例子class Book {
function getTitle() {
return "A Great Book";
}
function getAuthor() {
return "John Doe";
}
function turnPage() {
// pointer to next page
}
function getCurrentPage() {
return "current page content";
}
function getLocation() {
// returns the position in the library
// ie. shelf number & room number
}
}
? 现在它看起来相当合理了。没有方法和持久数据交互。有 ? ? 用户会对 class Book {
function getTitle() {
return "A Great Book";
}
function getAuthor() {
return "John Doe";
}
function turnPage() {
// pointer to next page
}
function getCurrentPage() {
return "current page content";
}
}
class BookLocator {
function locate(Book $book) {
// returns the position in the library
// ie. shelf number & room number
$libraryMap->findBookBy($book->getTitle(),$book->getAuthor());
}
}
? 引入一个 ? 然而,如果我们的业务目标是取代图书管理员并且创建自助机器,那么我们需要考虑我们第一个例子中的SPR。读者同时也是管理员,他们需要找到书并从自助系统中借阅。这只是一种可能性。重要的是你一定要经常考虑你的业务。 最后的思考? 在编码的过程中一定要频繁的考虑单一职责原则。它对于模块的设计会有很大的影响,对于降低模块间的依赖和实习低耦合很大的帮助。但是凡事总有两面,我们从程序一开始就想用SPR来设计,希望尽可能的想到更多的角色,但从设计的角度来看从一开始就尝试考虑所有的情况是有风险的。过度的考虑 SPR 可能导致的是不成熟的优化而不是一个更好的设计,它会使你的程序过于分散而导致一个单一职责的模块难以理解。 ? 所以,无论何时你当你发现一个类或模块因为各种原因需要改变时,不要犹豫不决,通过必要的步骤去实现SRP,然而要注意不成熟优化。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
- 在Vuex使用dispatch和commit来调用mutations的区别详解
- c# – Custom ReSharper模式 – 使用String.SomeMethod而不
- 依赖项属性
- PostgreSQL索引探究
- iSight does not work with online Flash-based chat sites
- cocos2d-JS中场景(scene)之前如何传参。
- C#解决方案用于渲染PDF和OCR生成的图像?
- flex图片剪切示例--预览、保存到本地、保存到服务器(附源码
- 单元测试 – 不带无参数构造函数的.NET单元测试,以方便依赖
- 在某些AJAX调用中出现“net :: ERR_BLOCKED_BY_CLIENT”错误