• 梳理拯救烂怂代码?我是这么做的


    分享是最有效的学习方式。

    博客:https://blog.ktdaddy.com/

    故事

    这几天的小猫心情还不错,修完了"幂等事件的bug",填完了"缓存击穿的坑",前两天组长交代给他的“整理一份系统现状报告”任务也算是有了思路,阴霾终于散去。好像一切都朝着好的方向发展了。是的,也该过去了,毕竟这些事情折磨小猫都快个把月了。

    这天,小猫提着两杯咖啡找到了产品经理,本来么,礼多人不怪,这不效果就来了么。

    “......整个业务背景呢,其实也就是这样了,然后后面有啥其他问题,也欢迎随时问,知无不答”。产品老汪和小猫足足聊了一半个小时,唾沫星子横飞,似乎还有点意犹未尽。

    “嗯嗯,谢谢了,汪哥,耽误你时间了。”

    “没事儿,不用客气,不过提醒你一下,当前系统经过太多开发的手了,系统代码得好好看看,听说初版本的时候都是外包搞的。你懂得......”

    “嗯嗯,好,太感谢了” 小猫连连点头,老汪的话倒是提醒了他。

    内心开始嘀咕,“哎。看来搞定各种模型关系,业务背景也还是不行啊,面对现实吧,烂怂代码还是得梳理一下的,当前系统的接口定义、以及类的封装貌似都挺乱的......”

    开启“类图”之旅

    说到对系统代码中的模型梳理,其实最好的方式还是使用UML类图。上个章节中老猫没有展开和大家分享UML类图,一个是由于篇幅的原因,第二个是老猫觉得类图对于咱们后续阅读框架源码以及底层设计模式有着相当大的帮助,所以很有必要将其作为单独一篇来和大家分享。如下概要:

    类图概要

    类图的简介

    类图是什么呢?比较专业一点的说法:在软件工程中,类图是一种静态的结构图,描述了系统的类的集合,类的属性和类之间的关系,可以简化了人们对系统的理解;类图是系统分析和设计阶段的重要产物,是系统编码和测试的重要模型。

    其实不仅仅是软件工程中,其他很多时候需要理清复杂关系的时候,咱们也可以用到这种表示方式。接下来,跟着老猫揭开类图的神秘面纱......

    类图的基本属性

    其实类图还是相当简单的,类图只要概括起来就两个比较重要的点,一个是类,第二个是类和类之间的关系,咱们弄清楚这两个,其实类图也就掌握了。
    啥是类?对于OO程序员来说,这个类不就是咱们日常定义的class么,没错,其实就是这概念。其中包含类名、类的属性、类的方法。

    王者荣耀打过吧?老猫用自己比较喜欢的一个英雄“钟馗”为案例和大家解析一下。如下图:

    classDemo

    上图中咱们可以看到:

    1. 类名:最上面的矩形框中即为类名:钟馗(Zhongkui)
    2. 类的属性:类名下的第一个矩形图中表示类的属性,如上钟馗案例中,其名称、血条等等信息即为其属性。
    3. 类的方法:在类属性的下面就是类的方法,其中在钟馗这个英雄类的方法中包含勾人--hook(),锤人--hammer()等等

    上图中我们可以看到无论是类的前面还是方法的前面其实都有不同的符号,其实这类符号就是作用域的概念。在类图中,作用域主要有以下几个:

    1. “+”:表示public,对所有的类都可见。
    2. “-”:表示private,仅仅只对当前类可见。
    3. “#”:表示protected,受保护的属性或者方法,其子孙类可见。
    4. “~”:表示package,表示包内可见
    5. “=”:表示默认值,上述案例中可能没有画出来。大家概念知道一下即可。
    6. “_”: 下划线,表示当前的这个类的方法或者属性是静态的。
    7. 斜体:老猫这里没有画出来,这里其实表示的是抽象,当然有的时候也会用两个尖括号包裹来表示抽象,<<我是抽象类or接口>>。

    方法和属性冒号后面所表示的含义也不一样,方法后面冒号后面的描述例如hammer():boolean,表示的就是返回值为boolean类型,是否锤到人。而属性的冒号后面则表示的是当前该属性的类型。

    类之间的关系

    我们再来看一个每个类和类之间的关系。直接看表示方式,如下图:

    classRel

    上述图中,咱们可能需要记住的是各种线条的表示方式。接下来,咱们一一来看

    泛化(继承)关系

    我们举一个鸭子继承鸟类这样一个例子。我们表示出来的话,如下:

    jc

    表示方法:空心三角+实线,箭头指向父类

    上图中我们清晰地看见子类鸭子继承了父类所有的属性,当然除此之外,子类还丰富了父类的方法,例如鸭子继承下蛋的同时扩充了游泳。

    小代码:

    public class Bird {
    public String feather;
    public String wing;
    public void layEggs() {
    }
    }
    public class Duck extends Bird {
    public String feather;
    public String wing;
    public void layEggs() {
    }
    public boolean swim() {
    return true;
    }
    }

    实现

    抽象了飞翔这么一个接口,然后让各种不一样的鸟类进行实现,如下图:

    classImpl

    表示方法:空心三角+虚线,箭头指向待实现的接口

    表示实现接口中的所有的方法。

    小代码:

    public interface Fly {
    public void doFly();
    }
    public class WildGoose implements Fly {
    public void doFly() {
    }
    }

    依赖

    举个例子,动物都依赖于空气,于是咱们就有了下图:

    classyl

    表示方法:尖括号+虚线,尖括号指向被依赖的类。

    依赖关系表示一个类使用(依赖)另一个类的服务或信息。一般来说,依赖总是单向的,不应该存在双向依赖。就拿上面的例子来说,动物依赖于空气,咱们并不能说,空气依赖于动物,可见依赖是单向的。

    小代码:

    public class Animal {
    public void breath(Oxygen oxygen,Water water) {
    oxygen.supply();
    water.supply();
    }
    }
    public class Oxygen {
    public void supply(){
    }
    }

    关联

    说到关联,种类比较多,但是理解起来还是比较简单,即一类对象和另外一类对象之间存在某种关系。这种关联关系分为单项关联、双向关联、自关联、多重关联。在面向对象语言中,如果表示存在关联关系,那么通常会将一个类的对象作为另外一个类的成员变量。

    单向关联

    例如用户在商城中购买东西之前都需要去设置地址,那么用户和地址之间就是单向关联关系。

    单项关联

    表示方法:尖括号+实线,尖括号指向用户的成员变量,即地址。

    小代码:

    public class User {
    private Address address;
    ...
    }
    public class Address{
    ...
    }
    双向关联

    例如用户购买商品,商品同时也会被不同的人购买,那么这种行为就是双向关联。

    双向关联

    表示方法:实线连接两个类即可。

    小代码:

    public class User {
    private List products;
    ...
    }
    public class Product{
    private User user;
    }
    自关联

    关于自关联,其实我们日常开发中也遇到过,尤其是一些递归类的时候,例如树形结构,大树下面套小树。

    树形Tree

    小代码:

    public class Tree {
    private Tree subTree;
    }
    多重关联

    多重关联关系表示两个关联对象在数量上的对应关系。在UML中,对象之间的多重性可以直接在关联直线上用一个数字或一个数字范围表示。比如一条河上在某个时间有好几只鸭子在上面浪,当然也可以没有。但是在那个时刻对于那只鸭子而言,它只能在一条河里面浪。例子以及数量表示说明如下:

    多重

    小代码:

    private class River {
    private Duck[] ducks;
    ....
    }
    public class Duck {
    }

    聚合

    主要描述聚合关系,描述的是整体与部分的关系。对于聚合来说,成员对象是整体对象的一部分,当然成员对象也可以脱离整体独立存在。这么说的话有点抽象了,打个比方,老猫有最近想要组装一台台式电脑,于是我买了显卡以及主板等等元器件。对于台式机来说显卡以及元器件是其一部分,但是显卡以及主板又可以单独作为商品进行售卖。于是我们就有了下面这样一幅示意图。

    聚合

    表示方法:空心菱形表示连接待聚合的那个类对象

    小代码:

    public class Computer {
    private KeyBoard keyboard;
    //构造方法注入
    public Computer(KeyBoard keyboard){
    this.keyBoard = keyboard;
    }
    }
    public class KeyBoard {
    }

    组合

    组合关系和聚合有点类似,也是描述整体和部分的关系,区别是聚合的话成员对象可以脱离整体单独存在,但是组合是"同生共死"的组合对象关系。例如公司和部门之间的关系。即为组合关系。如果公司都凉凉了,那么还有部门么?
    简单一幅示意图如下。

    组合

    表示方法:实心菱形表示连接待组合的那个类对象

    小代码:

    public class Company {
    private Department department;
    public Company(Department department){
    this.department = department;
    }
    }
    public class Department {
    }

    综合案例

    若是看到此处,相信大家对类图的各种表示方式已经了然于胸了吧。那么咱们接下来就来看看老猫绘制出来的类图关系吧。

    案例

    大家可以试试看看对照着老猫上面的梳理来读懂这样一个真实的模型类图吧。

    如何绘制类图

    写了这么多,相信大家对如何绘制类图用什么工具还是比较好奇的。

    1. 其实在idea中很多时候我们可以直接查看类图。只要在类名上面右键单击,选择 Diagrams -> Show Diagram即可展示如下图:

    demo

    1. 在上一篇文章中也有很多小伙伴问老猫流程图是用什么画的。其实都一样的,老猫一般绘制这种图就用两种工具软件,一种是drawio,另外一种是wps中的流程图绘制。当然其实还有其他绘制工具,例如process on等等,大家可以自己去试试。

    写在最后

    为了让小猫更好地优化梳理烂怂代码,老猫花了好几个晚上整理出来了绘制的方法。其实无论是多么复杂的类,只要我们把握清楚其中的类图关系,然后再结合上一篇文章中的业务模型对照起来一起看,就很清晰了。当然,前提是需要有足够的耐心。

    当然老猫费劲心血梳理uml的类图绘制流程其实还有一个原因,就是接下来咱们要开启“有趣的设计模式之旅”了。小伙伴们,持续关注老猫吧,相信后文更精彩。

  • 相关阅读:
    二维DP: leetcode 1186、
    算法的复杂度分析
    力扣202-快乐数——哈希集合
    【无标题】
    windows 修改 samba 连接端口 445 到指定端口(支持监听ipv6 及ipv4 IP地址)
    STM32 BootLoader设置
    git revert change by this commit
    react学习笔记1
    【Java】笔试-知识点复习
    [PyTorch][chapter 62][强化学习-基本概念]
  • 原文地址:https://www.cnblogs.com/kdaddy/p/17990890