2

抽象类降低子类可读性探讨

 3 years ago
source link: https://www.v2ex.com/t/800852
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

V2EX  ›  Java

抽象类降低子类可读性探讨

  kerb15 · 5 小时 6 分钟前 · 461 次点击

有个抽象类 Job,代码如下

public abstract class Job {
    
    public boolean start(){
        int id = a();
        processId(id);
        String data = b();
        processData(data);
        return c();
    }
    
    public abstract int a();
    public abstract String b();
    public abstract boolean c();
    
    public void processId(int id){
        ...
    }

    public void processData(String data){
        ...
    }
}

Job 封装了一个任务的主流程:a()->b()->c(),其中 abc 方法均为抽象类,由子类实现,中间穿插一些公共的方法,如 processId,processData,在父类实现。

这是一种比较常见的封装,乍一看没什么问题。

但是当 Job 的实现类很多,同时整个主流程变得复杂的时候,各种抽象方法和父类的公共方法穿插调用,特别是其他人去看代码的时候,就会变得特别痛苦,需要不断从子类和父类中跳转,以看清整个流程的全貌,此时子类的可读性就会变得很差。

作为读者,在面对多个子类任务的时候,我希望每个类都能看懂整个流程,作为开发者,有没有什么好的设计模式,能够在适当的封装下,又提高代码的可读性呢?

8 条回复    2021-09-09 17:51:54 +08:00

newtype0092

newtype0092   4 小时 34 分钟前

说明你读代码的顺序有点问题,抽象类就是抽象出整个流程的大概逻辑,下面的子类在去实现小的差别。
如果你每个具体的方法都要深入下去看具体实现,像深度优先遍历一样读代码,肯定是要花费大量时间的,一般有空闲了才这么读。
快速读完大概逻辑,碰到确实需要了解的细节再去看下层实现,这样比较高效。

eric96

eric96   4 小时 14 分钟前

这不是抽象模板吗,从抽象类可以获取整个流程的逻辑,具体的操作由子类实现,如果抽象类里面比较多的一些默认方法,阅读起来确实有点麻烦,但是遵循一次只阅读一个字类的逻辑还是很容易理清楚逻辑的。
只要不出现子类互相调用这种神操作

yamasa

yamasa   4 小时 13 分钟前   ❤️ 1

不管读代码还是优秀框架的源码,最基本的一点都是不要陷入无穷的细节里。有些方法你最开始只需要知道它大概负责什么,完成了什么,即便实现里有再精巧的设计,也放到把框架和流程理清楚之后再深入。

11232as

11232as   4 小时 10 分钟前

在父类的流程控制方法里打个断点找个用例跑一遍就可以了吧,或者打在子类方法里也可以,在调用栈里就能方便的在子类和父类中跳转。不过看代码是下下策,维护好文档就能避免折腾这套了...

monetto

monetto   3 小时 49 分钟前

如果是我的话,我会这样实现,Job 是一个接口。AbstractJob 实现了 processId 和 processData 方法。

任务的主流程封装为 Executor 类,使用 模板模式,直接调用 抽象接口 job.a -> job.b -> job.c -> processId -> processData,这样大家在看到 Executor 这个主流程就会非常清晰是这样的执行顺序。

尽量不要让 processId 和 processData 是在中间执行,因为最清晰的一定是,每个 job 的执行流程是一样的,都是 abc,id,data 这种,让大家一通百通。

如果想要一个任务主流程是 先执行流程 X 子类(X 实现了 a b c 三个接口),而后又要执行流程 Y 子类(Y 实现了 a b c 三个接口),那就使用 组合模式 或 责任链模式,定义一个 SyncJobChain (同理也可以实现 AsyncJobChain ),SyncJobChain 的 a 方法分别调用 X.a 和 Y.a,b 方法分别调用 X.b,Y.b,实现 SyncJobChain 的好处就是让任务整体都是一个执行顺序。看起来很舒服。

如果这样无法满足的话,也可以再扩展一些,即 job.abc 方法 不做整个逻辑的执行,abc 方法分别封装三个都是就绪状态的 Event,想办法把 Event 执行的逻辑搞成一样的,如果是 Spring 的话,通过 Publisher 发布出去,不是 Spring 的话,也可以自己定义一个线程池,扔到线程池里跑。在最后的 Spring Listener 或者线程池中完成 Event 的执行。

如果更灵活的话,可以加入 final 方法,在重写 a,b,c 的基础上,重写 final 方法,final 方法用于区分 X 类和 Y 类的最后节点任务(例如 X 是调用 Dubbo 接口,Y 是发送 MQ )

如果业务逻辑比较复杂,也可以参考 DDD 的设计思路进行设计。还有,如果很明确的东西,就尽量不要用 Interface 去搞 了,直接写 class 就好。

不知道答的在不在点子上,希望可以帮到忙。

securityCoding

securityCoding   3 小时 45 分钟前

我吃过这种亏读源码一开始最忌讳陷入细节 .
如果能按照自顶向下的思路搞清楚框架的设计脉络再去细读就不会难受了

exonuclease

exonuclease   3 小时 40 分钟前

讀代碼的正確姿勢應該是關注接口和抽象類 避免深入具體實現啊

Seayon

Seayon   3 小时 11 分钟前

最近刚实现了一个批量处理任务,就是这么写的。
同事说看不懂我写的

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK