经常在群讨论系统架构的事情,从三层架构 — 四层架构 — 六边形架构,开始有了鄙视链,使用过四层架构的开始鄙视三层架构了,用过六边形架构的就更不用说了。这就好比使用了领域模型后开始吐槽 “Service + 贫血模型” 和 “事务脚本” 。
也经常看到吐槽的,现在公司用的三层架构就是咸鱼,事实上三层架构和四层架构之间只有一个区别,那就是应用层。应用层做的事情则是对领域服务对编排,事务的处理。
那对于三层架构来说,我们可以通过设计模式中“组合模式”或者“外观模式”去创建编排领域服务。
void save(){ xxxSerivce.save(); xxxxService.save(); xxxxxService.save(); }
private Animal cat; private Animal dog; public AnimalFacade() { this.cat = new Cat(); this.dog = new Dog(); } public String dogWalk(){ return dog.walk(); } public String catWalk(){ return cat.walk(); }
@Test public void should_show_test() { AnimalFacade animalFacade = new AnimalFacade(); assertThat(animalFacade.catWalk(), is("cat walk")); assertThat(animalFacade.dogWalk(), is("dog walk")); }
另外,有同学会说大部分系统不需要什么复杂的架构模式,也不需要什么CQRS,只需要把代码库写得更 OO 一些,把SOLID设计原则理解透了就行。
是的,没错,我们经常忽略了代码的问题,忽略一个可复用面向对象的代码库。经常去套用这些架构模式,事实这些架构模式本质是通过SOLID原则和GoF 设计模式演变而来的。
昨天我们小伙伴分享了一篇文章叫 Java函数优雅之道,分享的特别好。讲到了很多人写代码不注意的地方,导致系统可维护性、可调整、可读性变差。
分享几个案例:
1、一个方法干几件事情,代码可读性差,要使用内部的业务方法没法使用
void save(){ if (relateTable.equals("1")){ // 去做等于1事情的代码 // xxxxx // xxxx } if (relateTable.equals("2")){ // 去做等于2事情的代码 // xxxxx // xxxxx } if (relateTable.equals("3")){ // 去做等于3事情的代码 // xxxxx // xxxx } }
调整如下:
void save(){ if (relateTable.equals("1")){ relateTable1(); } if (relateTable.equals("2")){ relateTable2(); } if (relateTable.equals("3")){ relateTable3(); } } void relateTable1(){ // 业务 } void relateTable2(){ // 业务 } void relateTable3(){ // 业务 }
还能优化吗? 肯定是可以的,if 里面判断是不是可以提取一个方法。
void save(){ if (isRelatedTable("1")){ relateTable1(); } if (isRelatedTable("2")){ relateTable2(); } if (isRelatedTable("3")){ relateTable3(); } } private boolean isRelatedTable(String relatedTable) { return relateTable.equals(relatedTable); }
2、利用 return 提前返回,这个是大多数时候都忽略的点,我们的习惯都是当某个对象不为空,然后去处理业务,否则 再干什么。代码如下:
User modify() { User user = getUser(id); if (user != null) { // xxxx 业务 } else { return null; } return null; }
调整如下
User modify() { User user = getUser(id); if (user == null) { return null; } // 业务 return user; }
是不是看着舒服,清爽些。
3、尽量使用基础类型
Double price = 5D; Integer number = 2; Double calculate(Double price, Integer number){ return price * number; }
建议如下
double price = 5D; int number = 2; double calculate(double price, int number){ return price * number; }
为啥?因为Java 内一切皆对象,基本类型不是对象,一个是将值存在堆栈中,一个是将对象实例存在堆中。哪个快自然就知道了。
4、参数进行封装,这个是最基本的,早期个人没有注意导致代码可读性差,修改起来贼麻烦。
void modify(int id, String name, String age, String sex, String phone);
要加一个字段,你得把数据层、领域层、用户层都给改一遍,你就痛苦了。
void modify(User user);
不管你后期要加什么,只需要修改对象user,和对应资源库的映射。
5、Creation Method 替换构造函数,构造函数最头疼的是属性参数数量的问题,构造函数一多,new 的时候就不知道用哪个了,出现问题非常不好找,导致要去一个个去对照相应的属性。
class Order{ int id; String no; double total; double discount; int number; Date date; public Order(int id, String no, double total, double discount, int number, Date date) { this.id = id; this.no = no; this.total = total; this.discount = discount; this.number = number; this.date = date; } public Order(int id, String no, double total, double discount, int number) { this.id = id; this.no = no; this.total = total; this.discount = discount; this.number = number; } public Order(int id, String no, double total, double discount) { this.id = id; this.no = no; this.total = total; this.discount = discount; } public Order(int id, String no, double total) { this.id = id; this.no = no; this.total = total; } }
建议如下:
public static Order saveOrder(int id, String no, double total, double discount, int number, Date date) { return new Order(id, no, total, discount, number, date); }
很好的暴露了使用意图,便于后来开发者调用。
写了一篇随笔,最后做个总结
忘了,在哪本书,哪篇文章里面看到,面向对象最难的在于如何将一个表里面20个属性,拆分成一个个行为对象,这个得依据实际经验与实际场景。
重视代码、重视代码、重视代码,重要的事情说三遍。