中国领先的IT技术网站
|
|

理解面向设计原则

今天我们不光讨论面向对象设计(OOD)核心原则,还将会谈到面向抽象编程提出抽象等内容。

作者:破狼来源:博客园|2012-05-08 10:14

沙龙活动 | 去哪儿、陌陌、ThoughtWorks在自动化运维中的实践!10.28不见不散!


面向对象设计(OOD)核心原则让我的程序模块达到“高内聚低耦合”,这是来自于30年前兴起的结构化设计(structured Design),但是同样适用于我们的OOD。

1.高内聚:

高内聚是指某个特定模块(程序,类型)都应完成一系列相关功能,描述了不同程序,类型中方法,方法中不同操作描述的逻辑之间的距离相近。高内聚意味可维护性,可重新性,因为模块对外部的依赖少(功能的完备性)。如果两个模块之间的修改,互不影响这说明模块之间是高内聚的。模块的内聚和其担当的职责成反比,即,模块的职责越多,模块的内聚性越低,这也是模块的单一原则(SRP),SRP提倡每个类型都最好只承担单一的职责,只有单一的改变因素。

2.低耦合:

耦合是描述模块之间的依赖程度,如果一个模块的修改,都有影响另一个模块则,两模块之间是相互依赖耦合的。(依赖具有传递性,耦合的两个模块可能间接依赖),低耦合是我们的设计目的,但不是不存在耦合不存依赖,依赖是必须的,因为模块之间必须通信交互,不过我的设计依赖应该依赖于不变或者不易变的接口,无需了解模块的具实现(OO封装性)。

在面向对象:我们可以简述为功能完备(高内聚)的对象之间的交互是依赖于不变或不易变的接口契约(低耦合)。

实现高内聚低耦合:行之有效的方式是分了关注点(SOC),将系统拆分成功能不同没有重叠功能集。每个功能只关注一个方面(Aspect)保证模块之间功能没有或者尽量少的重复。模块化内部实现细节隐藏,只暴露必须的接口,使得模块之间依赖于抽象,达到稳定。分离关注点的思想存在于我们软件设计的各个领域。如在.net的世界里SOA(面向服务架构)服务就是关注点,只暴露出必要的契约。分层架构从逻辑上利用接口抽象信息隐藏,减少依赖。MVC,MVP也是遵循分了关注点原则,达到表现层和逻辑的分离。

面向对象设计原则:

1.降低耦合度:对象直接需要交互,这就存在依赖,为了实现低耦合就必须减少依赖,依赖于稳定或不易变抽象。考虑如下订单日志记录场景:我们需要在订单每部操作记录更改日志。

  1. public class OrderManager  
  2. {  
  3.    public void Create(Order order)  
  4.   {  
  5.       //订单处理.  
  6.      Logger log = new Logger();  
  7.      var history=GetHistory();  
  8.      log.log(history);  
  9.  }  

在这里我们的OrderManager和Logger存在高耦合,Logger类的修改可能导致OrderManager的修改,而且不能随意切换我们的日志记录方式,比如文件,控制台,数据库等日志方式。

面向抽象编程提出抽象(接口,abstract类)是不易变的稳定抽象;对于OrderManager来说我不需要了解日志记录组件内部,只需要明白提供那些接口可用,怎么用。

  1. public interface ILogger  
  2. {  
  3.   void Log(History history);  
  4. }  
  5. public class Logger  
  6. {  
  7.   public void Log(History history)  
  8. {  
  9. //内部实现  
  10. };  

那么我们可以从设计模式工厂模式(工厂模式是负责一些列相似对象的创建)Create 日志组件ILogger。

我们的OrderManager 就可以实现为:

  1. ILogger log =LoggerFactory.Create();  
  2. log.Log(history); 

这样我们的OrderManager就依赖于ILogger,而隔离Logger具体实现,将依赖于抽象,把变化缩小到Factory内部(同样也可以用抽象工厂),如果日志实现变化我们可以重新实现ILogger ,修改Factory逻辑,如果内部利用配置我的需求变更转移到配置。这就是面向对象第一原则,依赖于抽象而隐藏实现。(利用IOC是一种更好的方式)

2.代码的重用性:尽量保证相同功能代码只出现一次(Code once run anywhere)。代码的重用在面对对象设计中有继承和组合两种方式,一般推荐组合优先。组合依赖于接口,组合更安全,易于维护,测试。继承存在父类访问权限,父类的修改导致子类的变化,太多的继承也有导致派生类的膨胀,维护管理也是件头痛的事。

3.开闭原则(OCP):表述拥抱需求变化,尽量做到对模块的扩展开发,修改关闭。对于新增需求我们完美的做法是新增类型而不是修改逻辑,这就意味着我们必须使用组合或者是继承体系(为了避免上一条重用性,我的继承应该是干净的继承体系,派生类应该只是新增功能而不是修改来自父类上下文),

4.里氏替换(LSP):表述派生类应该可以在任何地方替代父类使用。并不是所有的子类都可以完全替换子类,比如设计父类私有上下文信息的访问,导致子类无法访问。

5.依赖倒置(DIP):描述组件之间高层组件不应该依赖于底层组件。依赖倒置是指实现和接口倒置,采用自顶向下的方式关注所需的底层组件接口,而不是其实现。DI框架实现IOC(控制反转)就是DIP很好的插入底层组件构造框架(分构造注入,函数注入,属性注入)。微软Unity,Castle windsor,Ninject等框架支持。

最后分离关注点,衍生出声明式编程,面向方面编程(AOP)实现纵切关注点,把具体业务逻辑和日志安全等框架集公用逻辑分离。 关于IOC/AOP参见博客我的IOC/AOP随笔目录 不在累赘。

原文链接:http://www.cnblogs.com/whitewolf/archive/2012/05/08/2489425.html

【编辑推荐】

  1. 浅谈.NET独有精巧泛型设计模式
  2. 利用 SPL 快速实现 Observer 设计模式
  3. 设计模式系列之代理模式
  4. 从理发店流程抽象设计模式中的组合模式
  5. 大话恼人的那些设计模式
【责任编辑:彭凡 TEL:(010)68476606】

点赞 0
分享:
大家都在看
猜你喜欢

读 书 +更多

入侵的艺术

黑客也有优劣之分。很显然对他们的奖励之一是利用黑客手段非法入侵我们公司的安全站点或个人系统。另一种奖励可能是他们的黑客行为构成了黑...

订阅51CTO邮刊

点击这里查看样刊

订阅51CTO邮刊
× Python最火的编程语言