公共代码重构

创建通用的共享组件导致了一系列问题,比如耦合、协调难度和复杂度增加。

当我看到一个个巨大的 common 包时,我开始痛恨 commonbaseutil 这些该死的包,还有它们目录下统一管理的 bean。我们真的已经把它们用烂了,所以你应该重新审视一下你的项目代码。

所以,从这种意义上来说:复用与低耦合 ,本身存在一定的互斥关系。

公共代码往往缺乏抽象,又或者是追求过度的复用。

它真是个 util 吗?

哦,不,它是个恶魔,因为它是 util。

你会往 xxUtil 不加思索地扔入逻辑,正如你会往 common/bean 中扔入所有的 model,直次有一天,你拥有一个巨大无比的 base、common 代码。

大多数情况下,所有和业务相关的 Util 都存在一定的问题,如 CaptchaUtil,它要么应该划到自己的上下文中去,要么扔到诸如于 domain/shared 等共享上下文,而不是和其它 util 放到一起。

而诸如 FileUtil、DateUtil、RedisUtil、JdbcUtil 这些都可以说是基础设施相关的部分,它们可以划到 infrastructure/file 又或者是 infrastructure/date 目录下,而不是统一的管理这些 util。

StackOverflow 的相关问题所列,我们还有诸如 Coordinator、Builder、Writer、Reader、Handler、Container、Protocol、Target、Converter、Controller、View、Factory、Entity、Bucket 等名称。

含义更加丰富的名字启示如下:

XXX器[拟物化]:

Listener 监听器 Adapter 适配器 Filter 过滤器 Iterator 迭代器 Buffer 缓冲器 Connector 连接器
Decortor 装饰器 Iterepter 解释器 Interceptor 拦截器 Reactor 反应器 Configurator 配置器 Wrapper 包装器
Proactor 主动器 Monitor 监视器 Controller 控制器 Translator 转换器 Acceptor 接收器 Selector 选择器
Container 容器 Manager 管理器 Evictor 驱逐器 Activator 激活器 Mapper映射器 Locator 定位器
Handler 处理器 Assembler 汇编器 Driver 驱动器 Spliterator 分割器 Builder 构建器 Formatter 格式器
Scanner 扫描器 Timer 定时器 Converter 转化器 Dispatcher 分配器 Multicaster 广播器 Transfer 传输器
Desriptor 描述器 Encoder编码器 Decoder 解码器 Introspector 内省器 Tokenizer 分词器 Loader 加载器(ClassLoader)
Logger 记录器 Parser 解析器 Resolver 分解器 Incrementer 增加器 Counter 计数器 Collector 收集器
Initializer 初始化器 Setter 设置器 Getter 取值器 Marshaller 编组器 UnMarshaller 解组器 Helper 帮助器
Accessor 访问器 Visitor 访问器 Reflector 反射器 Embedder 嵌入器 Finalizer 回收器 Specifier 标识器
Supplier 供应器 Processor 处理器 Joiner 接合器 Recorder 记录器 Reducer 归集器 Analyzer 分析器
Invoker 调用器 Provider 供应器 Renderer 渲染器 Holder 持有器 Closer 关闭器 Operator 操作器
Appender 添加器 Printer 打印器 Tuplizer 元组器 Caller 调用器 Identifier 标识器 Walker 漫步器
Brower 浏览器 Server 服务器 Aggregator 聚合器 Binder 绑定器 Validator 校验器 Finder 查找器
Launcher 发射器/启动器 Weaver 织入器 Messenger 信差/消息器 Extractor 提取器 Sampler 取样器 Profiler 优化器
Tracer 追踪器 Estimator 预估器 Generator 生成器 Instrumenter 插装器 Viewer 查看器 Debugger 调试器
Analyser 分析器 Inspector 检查器 Linker 链接器 Editor 编辑器 Recognizer 识别器 Decompiler 反编译器
Translator 解释器 Lexer 词法分析器 Tracker 追踪器 Constructor 构造器 Destructor 析构器 Executor 执行器
Synchronizer 同步器 Barrier 障碍器 Allocator 分配器 Bundler 打包器 Applier 分发器 Trigger 触发器

XXX者[拟人化]:

Consumer消费者 Producer 生产者 Observer 观察者 Caller 调用者 Supervisor 监管者 Keeper 管理员(ZooKeeper)
Wokrer 工作者

器和者的一些名字可以互换。比如Builder 可以是构建器,也可以是构建者。名字选择很多,但是不要过度封装,用最简单的概念表现更多的含义。

试着干掉 Utils ,你将收获更多的类,笑~。

Utils / Helper 多数是恶魔,无法满足单一职责和开闭原则。好的 OO 设计,大部分的类只表示一个事物,及其所有属性和操作。

  1. 尽可能减少 Utils / Helper 类。好的 OO 设计,大部分的类只表示一个事物,及其所有属性和操作。
  2. 如果使用一个 Utils 用于操作类,如 IList,那么它应该划到类中。除非该类不存在于当前的应用中。
  3. Utils 中的方法应该是无状态的,比如没有 static 变量。
  4. 如果有大量的 Utils 方法,应该把划分到类中,以便快速找到它们。

过度设计

好的设计是尽可能简单的,它最易于适应新的设计,并能跟随业务的变化而变化。

  • 开发人员:『这个功能是给未来准备的』
  • Tech Lead:未来是多久?一个月后?半年后?
  • 开发人员:……

图片出自:https://stackoverflow.com/questions/1001120/what-is-over-engineering-as-applied-to-software

与之相对应的设计不足,则是因为经验的缘故。

重新定义:消除二义性

当我们谈论 service 的时候,我们谈论的是同一个 service 吗?当我们谈论 model 的时候,我们谈论的是同一种 model 吗?

若对于一个文法的某一句子存在两棵不同的语法树,则该文法是二义性文法。

如果有多种不同类型的类,都被放置在 model 包下。那么,你应该消除 model 这个包,改为更表意的名称,如 Entity、Request、Response 等等。同理,一旦你们展开对某个名称的讨论时,是时候好好考虑其中的二义性。

最后,你还需要有一个相关领域的名词表。避免产生二异性的词语。

类进行内聚

参考下文中的模型重构

划分技术部分

如 Spring 框架的源码:

└── springframework
    ├── cache
    │   ├── annotation
    │   ├── concurrent
    │   ├── config
    │   ├── interceptor
    │   └── support
    ├── context
    │   ├── annotation
    │   ├── config
    │   ├── event
    │   ├── expression
    │   ├── i18n
    │   ├── index
    │   ├── support
    │   └── weaving

划分业务部分

业务模块中的技术部分。

职责少 => 平级

└── orm
    ├── context
    ├── support

业务代码多 => 再按业务拆分

infrastructure
└── repository
    ├── context
    │   ├── blog
    │   ├── advert
    │   └── pages
    ├── kafka
下一节:模块/组件是软件的部署单元,是整个软件系统在部署过程中可以独立部署的最小实体。 —— 《架构整洁之道》