漫谈面向对象与日常开发

记得第一次跳槽的时候,到招聘网站上翻看各种初级工程师的招聘要求,都会有意无意的加上一句:具有良好的面向对象思维。当时心里很不屑,面向对象无非就是"继承,多态,封装"嘛。直到工作了很多年之后,看了很多书之后,越来越觉得当时的想法有点low了,刚好前一段时间和朋友聊到日常开发中有些设计的优劣,有些心得,想把它记录下来,遂有此文。

面试别人的时候,我偶尔会问一个问题:请用一句话来描述何为restful。对于这个问题,面试者答得各式各样,其实在我心里的答案就是:restful就是用路径定位资源,用get/post/put/delete操作资源。说白了,restful最核心的概念并不是url样式或者get/post方法而是资源。何为资源?这个就是你的应用上的领域对象了。在互联网企业中,现在很少有人再用hibernate/jpa了,其实熟悉jpa的人对restful有一种天生的亲近感,它们天然的描述了对象之间的关系,可以这样说,restful是面向对象思想在api设计上的一种体现,所以风格自由,兼容性更好,观之就能理解业务对象之间的关系。很多人推崇的代码即文档,不过如是。

即便我对hibernate如此推崇,也只是相对日常开发中最常用的mybatis来讲的,在更极致的面向对象设计理念中,hibernate所代表的设计风格,只是一种贫血设计模型。因为万事万物,不可能只有状态而没有行为,通俗来讲,就是不可能只有属性,而没有方法(get/set不算)。举一个简单的例子

public class Person{
  int age;// 年龄
  String name; // 姓名
  String idCard; // 身份证号
  GENDERS gender; // 性别
  Person partner; // 伴侣
  enum GENDERS{
    MALE,FEMALE;
  }
  // 和某人结婚
  void marriadWith(Person somebody){
    if(this.gender.equals(somebody.gender)){
      throw new IllegalStateException("不能和同性结婚")
    }
    if(this.age<18 || somebody.age<18){
      throw new IllegalStateException("未成年人不能结婚") 
    }
    this.partner=somebody;
    somebody.partner=this;
  }
}

如上所示,这才是一个完整体现了面向对象思想的代码设计。可能从事企业开发的朋友们对这种写法会嗤之以鼻,因为——这个例子没有数据库操作。

诚然,因为我们没有无限的内存,导致我们业务对象的生命周期的大部分时间都是以另一种形态静静躺在数据库中,而且由于数据库的各种设计特性,会割裂我们最接近现实的设计。比如上面的例子,在数据库里面存的可能是

create table t_person(
age int,
name varchar(64),
id_card varchar(32),
gender smallint,
partner_id_card varchar(32),
primary key id_card
)

很多朋友喜欢先设计数据库,然后通过mybatis官方提供的generator反向生成对应的业务实体,但是很不幸,大多数互联网公司都是禁止使用外键的,这就导致了我们的业务实体成了下面的样子

public class Person{ 
    int age;// 年龄
  String name; // 姓名
  String idCard; // 身份证号
  int gender; // 性别
  String partnerIdCard; // 伴侣
}

好了,我们辛辛苦苦挖出来的领域知识被消磨殆尽了。

这就是我为什么如此厌恶mybatis和generator了。我厌恶的并不是这两项技术,而是厌恶的这种设计方式湮灭了我们的领域知识,让我们进一步学习业务领域,改进设计方案的可能无限趋近于零了。

在这里,我再次郑重推荐一种设计方法,就是架构师们常常挂在嘴边的DDD,全称是领域驱动设计,我称之为面向对象思想在企业开发中的落地方案。这篇文章不是专门介绍ddd的,就不详细介绍了。

面向对象的"继承,多态,封装"三大特征里面,只要熟练使用设计模式基本上对继承和多态就能有足够的认识了,但是"封装"这两个字,却没那么容易,很多设计上的缺陷就是由"封装"认识不足引发的。

在Java企业开发工作中,很多项目都会按照controller/entity/service/dao这样的职责进行代码的划分,好像这样就做到了"分层"。刚工作的时候,我也是这种划分方式的拥趸,到后来越来越觉得这种划分方式不科学。首先,并不是所有的代码调用都会从controller到dao,也不是所有的controller都会以来entity,按照这种划分方式写,犹如八股文一样繁琐并且又臭又长。再者,被分在同一个包中的controller很少有互相调用,dao更不会互相调用,相反,被分在不同的模块中的XXXController常常会依赖XXXEntity,再通过XXXService调用XXXDao简直就像呼吸一样频繁,这就形成了不同模块之间的高耦合相同模块之间的低内聚,也不能遵循相同模块共同发布的原则,简直就是一个大泥团。这是对面向对象思想的封装思想严重的认识不足,这种情况高级程序员乃至很多架构师亦不能免俗,大概是习惯使然吧。

最近在读一本小说,小说中把"道"和"术"分别比喻成路和脚。诚然,如果没有技术我们就没法去开发,但是如果没有设计思想,就会我们迷失方向,最终导致我们的代码一团乱麻,甚至会导致项目失败。在我们日常开发中 ,我们心里应该有一条准绳,什么样的技术可以用,以及技术如何使用都应该在心里过一遍。做程序员的固然要不断学习,但世上技术千千万,我们只需要学有用的就够了,什么样的技术有用,一旦心里有一条准绳,自然会知道。

你可能感兴趣的:(漫谈面向对象与日常开发)