面向对象之 — 封装

概述

面对对象的设计和编码中,经常碰到的一个问题,怎么算是面向对象,这个很虚,也很难去描述,也是自己早年很困惑的东西,虽然看了很多的书,但是很难有一个整体层面的理论去概述,一直很想将这种概念进行落地。
这次就个人经验来来做个总结吧,大致上分为三个层面来进行面向对象的落地。

  1. 封装,继承,多态
  2. OO原则:单一职责、开闭原则、无环依赖等等
  3. 设计模式:组合、桥接、工厂等等

以上三点是依据抽象程度进行划分,封装、继承、多态是对于面向对象最为抽象的概述,之后的一切一切都是基于此,不得不佩服前人的智慧,能够在纷乱复杂的软件理念中提炼出这三个概念。
既然是对象,那必然应该有其封装的内在东西,同时将这种封装的内在应该是私有的,但是世间的个体对象都不是孤立的依存的,用马克思的哲学说应该用辨证唯物主义并发展的看待事物,所以延伸出继承和多态又是必然的,继承又是多态的前置条件,多态也是继承一个顺气自然的概念扩展。

当然三个概念又太过抽象,所以就出现一些OO设计原则,总体上有十三个,五个针对代码级别的设计原则(单一职责、开闭、里氏替代、依赖倒置、接口隔离),三个分包原则(发布重用等价、共同封闭、共同重用原则)及三个包依赖原则(无环依赖原则、依赖稳定原则、稳定抽象原则)。到这层面已经提供了一些细化的面向对象原则,其中单一职责、开闭、发布重用、共同重用等原则都是对于上一层中封装的体现,而里氏替代、依赖倒置、接口隔离都是对于继承和多态的良好体现

其实在第二层还只是提出了一些原则性的东西,直到第三层:设计模式才将我们的OO设计落地到代码层面。这个才是我们程序员最为熟悉,也最为理解的东西,但是若没有以上两层的概念铺垫,很容易将对于设计模式的理解停留在形式上,没有本质上去灵活运用。举几个例子来看:工厂模式是对应到第二层的单一职责,对应到第一层的封装,策略模式对应到第二层的里氏替代和第一层的继承和多态!

说了这么多,这篇的主题是封装,就拿个简单的例子来说明如何取使用封装!

一个if语句的改造

XXService{
 if (status == 1 ){
 ...
 }
}

这样的代码可以说是随处可见,那么它有什么问题?
第一点 判断条件中的1是一个魔法数字,我们根本不知道status 为 1 代表什么意思它只是一个数值逻辑上的含义,并没有附上业务含义。那么就进行改造吧

XXService{

// 促销中
staic final int SALE = 1
 if (status == SALE){
 。。。。。
 }
}

这么做,去除了魔法数字,但是这里还是会带来一个问题,这个判断条件简单的情况下,没有问题,但若是if中的条件非常复杂的话,那就代码可读性上带来很大的问题,就有了下面的改造,对于复杂的业务做了一个简单的封装

XXService{

// 促销中
staic final int SALE = 1
 if (isOnSale(status)){
 。。。。。
 }

 private boolean isOnSale(int status){
 return status == SALE;
 }
}

到这步的时候在代码可读性上有了一定的提升,对于一定的业务含义做了封装,但是,但是这样就真的够了吗?若是追求完美的话,考虑一个问题,若是在业务的其他地方也需要做这样的一个判断的话,那么isOnSale这个方法是不是会散落到各个地方呢?为了解决这个问题,我们的先辈们就抽象出一种方式:领域建模,即将一个类型的行为和属性收敛或是封装到一个具体的领域中,这个也就能很好的解决代码副本的查找和消灭!代码就变成了如下:

XXService{

if (XXDomain.isOnSale(status)){
 。。。。。
 }

}
XXDomain{

// 促销中
 staic final int SALE = 1

private boolean isOnSale(int status){
 return status == SALE;
 }
}

到这步就能够很好的解决我们已所知的问题:魔法数字,代码散落,副本的查找和消除,这里也就是面向对象设计中封装的优点所在。

作者: inter12

在这苦短的人生中,追求点自己的简单快乐

发表评论

电子邮件地址不会被公开。 必填项已用*标注