0、背景/解决的问题
anyway,我们的应用系统里有许多仅涉及到数据的id和name的地方。
例如:Web服务端给前端页面的企业检索下拉列表所提供的数据接口,会返回一个包含 enterpriseId、enterpriseName的集合。
再例如:销管(sale)会提供一个rpc接口,返回一个包含saleId、saleName的集合,供WebController接口使用,或者供那些需要saleId、saleName数据的程序使用。
再例如:程序内会有这样的方法,saveByOrderDetailWhenPaySuccess(orderDetail, invoiceClassIdAndName),第二个参数是一组id和name。
除了上面提到的 enterpriseId & enterpriseName、saleId & saleName,我们系统中还包括了 levyId & levyName、providerId & providerName、agentId & agentName,等等等等。用一个词“不胜枚举”来形容,再恰当不过。不仅如此,单就 enterpriseId & enterpriseName 来说,系统里就有不少。
那么,我们能做什么呢?————DataIdNameVO 诞生!
DataIdNameVO,顾名思义,只包含两个属性:id 和 name。
接下来,重点讲解对 DataIdNameVO 的设计。
1、DataIdNameVO 结构
如前文所述,DataIdNameVO 职责极其单一,两袖清风,只包含两个属性:id 和 name。
意味着,我们把 enterpriseId & enterpriseName、saleId & saleName、levyId & levyName、providerId & providerName、agentId & agentName 抽象出来一个公共的 id/name 对儿。
1.1、DataIdNameVO#id 的类型
DataIdNameVO#name 自不必说,是个 String。
DataIdNameVO#id 呢? 通常情况下,我们会将 DataIdNameVO 定义为泛型T, 其中,id的类型是T。这样设计,可以满足所有的场景。
不过,我在定义时做了取舍。一来,泛型对于个别初级同学来说会降低易读性,二来,结合我们系统的实际,我们系统绝大多数数据的id是Long,最终,我选择将id定义为Long。
BTW,有同学将name设计成泛型,当属于过度设计。
2、关于 DataIdNameVO 的实例化
常规的路子,我们通过 new 的方式来实例化 DataIdNameVO 对象。- DataIdNameVO vo = new DataIdNameVO();
- vo.setId(xxx);
- vo.setName(xxx);
- DataIdNameVO 暴露默认无参的 public 构造器。
复制代码 我摈弃了这个方式。
我们应该尽量减少业务代码的工作量。下面1行实例化的方式,比上面3行要好。- DataIdNameVO vo = new DataIdNameVO(xxx, xxx);
复制代码 这时,DataIdNameVO 需要暴露全参构造器。同时,屏蔽掉field的setter。
我摒弃了这个方式。
Java语言的理念里,或者Spring的理念里,它们并不首推利用 new 的方式来实例化对象。我们从hutool、lombok、apache-common等开源组件也可见一斑。
不用new 的方式,自然是builder模式,或通过静态工厂方法的方式,来封装对象的创建逻辑。- DataIdNameVO vo = DataIdNameVO.build(xxx, xxx);
复制代码 按照这样的思路,我在 DataIdNameVO 里定义一个静态 build 方法。- public static DataIdNameVO build(Long id, String name) {
- return new DataIdNameVO(id, name);
- }
-
- public DataIdNameVO(Long id, String name) {
- this.id = id;
- this.name = name;
- }
复制代码 为什么保留了 全参构造器?
这是因为某些情况下,例如序列化、例如MapStruct等bean转换,可能需要它。
我推荐开发者使用 build 来创建 DataIdNameVO 对象。如何让开发者知道呢?---->javadoc 可以派上用场。
3、DataIdNameVO 实例化:进阶设计
上面的设计,局限于DataIdNameVO 本身。
站在使用者的角度,我们有必要对 实例化 DataIdNameVO 再做一番改造。
业务程序里,会怎么使用 DataIdNameVO 呢?- InvoiceCategory entity = InvoiceCategory 实例;
- DataIdNameVO invoiceCategoryIdName = DataIdNameVO.build(entity.getId(), entity.getCategoryName());
- invoiceOrderManager.saveByOrderDetailWhenPaySuccess(orderDetail, invoiceCategoryIdName);
复制代码 从这个使用方式来看, DataIdNameVO 不需要做什么了。
我们再来看下面的使用。- List<EnterpriseVO> list = EnterpriseVO 实例集合;
- List<DataIdNameVO> dataIdNameVOList =new ArrayList<>();
- for (EnterpriseVO enterpriseVO : list){
- dataIdNameVOList.add( DataIdNameVO.build(enterpriseVO.getEnterpriseId(), enterpriseVO.getEnterpriseName()));
- }
- ...
复制代码 从这个使用方式来看, DataIdNameVO 能做什么?
系统里如果存在大量的这种for循环来构建 DataIdNameVO集合,那么,DataIdNameVO 是可以做点事情的。
上面for循环代码,如果改成下面这样,是不是优雅?- List<EnterpriseVO> list = EnterpriseVO 实例集合;
- List<DataIdNameVO> dataIdNameVOList = DataIdNameVO.buildList(list, EnterpriseVO::getEnterpriseId, EnterpriseVO::getEnterpriseName);
- ...
复制代码 是的!
于是,DataIdNameVO 有了下面的静态成员,buildList。该方法将一个包含任意 POJO 对象的列表转换为 DataIdNameVO 类型的列表,通过传入的 Function 接口可以灵活地从 POJO 对象中提取所需的 id 和 name。- public static <POJO> List<DataIdNameVO> buildList(List<POJO> list, Function<POJO, Long> id, Function<POJO, String> name) {
- if (list == null) return null;
- List<DataIdNameVO> result = new ArrayList<>(list.size());
- for (POJO obj : list) {
- result.add(build(id.apply(obj), name.apply(obj)));
- }
- return result;
- }
复制代码 4、DataIdNameVO 全貌
- import lombok.Getter;
- import lombok.ToString;
-
- import java.io.Serializable;
- import java.util.List;
- import java.util.function.Function;
- import java.util.stream.Collectors;
-
- /**
- * 见名知意,只包含 数据id/数据name 的ValueObject
- * <p>
- * eg. enterpriseId/enterpriseName, levyId/levyName, etc.
- *
- * @author zhangguozhan
- * 2024-04-11
- */
- @Getter
- @ToString
- public class DataIdNameVO implements Serializable {
- /**
- * 数据的id
- */
- private Long id;
- /**
- * 数据的name
- */
- private String name;
-
- /**
- * @deprecated :推荐使用 {@link #build,#buildList} 来构造对象
- */
- public DataIdNameVO(Long id, String name) {
- this.id = id;
- this.name = name;
- }
-
- public static DataIdNameVO build(Long id, String name) {
- return new DataIdNameVO(id, name);
- }
-
- public static <POJO> List<DataIdNameVO> buildList(List<POJO> list, Function<POJO, Long> id, Function<POJO, String> name) {
- if (list == null) return null;
- return list.stream()
- .map(obj -> build(id.apply(obj), name.apply(obj)))
- .collect(Collectors.toList());
- }
-
- }
复制代码 总结
小改动,大收益!
我们通过分析我们复杂的企业系统,抽象出来这个 DataIdNameVO,很大程序上可以降低程序的复杂度,增强代码的可读性,并能提高开发效率。
站在宏观的角度,利用OOP设计思想,来不断重构、调优系统,使之可持续发展。当然,这首先需要我们具备这样的能力 和 意识!
/豆包点评/
DataIdNameVO 类在程序设计方面具有较高的质量,通过简洁的代码、良好的注释、合理的设计模式和性能优化,提高了代码的可读性、可维护性、灵活性和性能。
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |