代码优雅实现讨论 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
请不要在回答技术问题时复制粘贴 AI 成的内容
beryl
V2EX    程序员

代码优雅实现讨论

  •  
  •   beryl 2021-08-16 17:59:05 +08:00 4741 次点击
    这是一个创建于 1522 天前的主题,其中的信息可能已经有所发展或是发生改变。

    RT, Java 代码,一个方法中,逻辑特别多,于是把里面逻辑封装成了几个单独方法,但是也有七八个单独方法,看起来也有点难受:

     fun() { fun1(); // 数据初始化 // 这里有几行代码,处理特殊逻辑 fun2(); fun3(); // 调用第三方 fun4(); // 这里有打印日志 fun5(); // 缓存处理 fun6(); // 这里有一些代码,做对象转换等 fun7(); // 打印日志等 fun8(); // 缓存处理 return xxx; } 

    感觉还是在面向过程去写,有点难受看着,但是有没有优化思路,或者优秀代码参考。

    25 条回复    2021-08-19 10:51:58 +08:00
    cmdOptionKana
        1
    cmdOptionKana  
       2021-08-16 18:44:41 +08:00
    先建模型啊,单单给出正文里这些信息并不足够,你要看看你处理的对象是什么,是用户、商品、文章、图形还是别的什么东西,要先找到对象才能面向对象。
    Rwing
        2
    Rwing  
       2021-08-16 18:46:42 +08:00
    确实是面向过程,你要抽象一下
    然后缓存可以抽出来,日志可以抽出来
    如果可以你可以贴出具体代码
    cmdOptionKana
        3
    cmdOptionKana  
       2021-08-16 18:47:29 +08:00
    找到对象后,就看对象有哪些属性、哪些行为,比如商品有属性“价格”、“分类”,有行为“被预定”、“被销售”等。

    文章有属性“标题”、“发布时间”,有行为“被创建”、“被发布”等。
    misdake
        4
    misdake  
       2021-08-16 18:57:04 +08:00   2
    想到一篇很老的文章,有点无关又有点相关,我贴一下标题和链接。很老的文章,背景也比较复杂,仅供参考。

    John Carmack on Inlined Code

    http://number-none.com/blow/john_carmack_on_inlined_code.html
    CEBBCAT
        5
    CEBBCAT  
       2021-08-16 19:59:02 +08:00
    在我看来,这些逻辑它们本来就是面向过程的,因此你无法通过抽象代码来让代码更优雅。

    不过,可不可以通过 MQ 等,把这些逻辑分开来写呢?我想这是一种有效的解决办法,也是符合直觉和逻辑的
    /ins>
    mxT52CRuqR6o5
        6
    mxT52CRuqR6o5  
       2021-08-16 20:05:15 +08:00
    (如果是 spring 大概会是下面这种思路?)
    数据操作,每张表一个类
    每个第三方的所有操作一个类
    日志一个类
    缓存一个类
    最后一起依赖注入进来
    mxT52CRuqR6o5
        7
    mxT52CRuqR6o5  
       2021-08-16 20:10:01 +08:00
    我看着感觉日志啊缓存啊转换啊,可能有方法去优化掉,就比如日志和缓存有没有什么固定的逻辑,就能抽象出来
    就比如做同一类操作的时候会固定记录日志,那你直接把记录日志的代码放到做这一类操作继承的 class 中去,就不用先调一遍这一类的方法,再手动去记日志
    auh
        8
    auh  
       2021-08-16 20:22:57 +08:00
    这些操作具备对象的特征?个人感觉只能具备业务逻辑的特征。不管是不是面向对象。最后肯定有一个组合逻辑的过程。组合逻辑过程,就像 @misdake 引用的文章类似,采用哪种风格,来管理使得更加灵活或者更加封闭。就算是各个 func 归类到不同的对象中,这个组合过程,还是类似这种逻辑。充其量抽象成接口,能够灵活替换一下。
    wangxiaoaer
        9
    wangxiaoaer  
       2021-08-16 20:57:56 +08:00
    某些 fun 是否提取成对象单独处理,把这个 fun 里面的逻辑交由那个对象负责,也就是 fun()----> anotherObj.fun()
    TypeError
        10
    TypeError  
       2021-08-16 21:05:15 +08:00
    分层加上拆分模块吧
    Kasumi20
        11
    Kasumi20  
       2021-08-16 22:44:59 +08:00
    日志用 AOP, 面向切面编程
    felixin
        12
    felixin  
       2021-08-16 22:55:05 +08:00 via Android
    学习 clean code
    ljzxloaf
        13
    ljzxloaf  
       2021-08-16 23:04:11 +08:00
    需要换个角度去思考问题。

    打个比方,产品需求描述一般是用户场景和交互流程,而这些变化是非常快的,我们不可能给每个场景、每个流程写一套代码,所以需要从业务模型的角度去思考,而不能从业务流程的角度去思考。比如你这种就可以看做是几种业务模型组合起来的流程,其中的业务模型有第三方业务、缓存、日志等,我认为你这种写法没啥问题。如果有些逻辑重复读较高,可以根据业务更为细致的封装。比如 A 业务与 B 业务组合起来形成一个业务。这种组合就看具体需求了。

    流程与对象是相对的,流程的每个节点都是对象,对象的内部逻辑也是流程。

    实践中,我们一般可以同时提供两种接口:一种是不依赖其他业务的“原子”接口;另一种是依赖其他业务的“组合”接口。

    比如搜索,我可以同时提供:返回 id 集合的接口和返回 item 集合的接口。item 信息是我从 item 服务拿到的,这样我等于组合了 item 服务和搜索。反过来也类似,比如敏感内容过滤,我也可以提供两种接口:传参 id 集合和传参 item 集合,如果只传 id 我需要去 item 服务查 item 信息,就是组合接口;如果传给我 item 集合,我就不需要去依赖 item 服务。

    这种做法有什么好处呢?我们可以先考虑这样一个问题,我们有底层服务 A ( atom ),有两个上层服务 C1 、C2 ( combination ),现在有个需求要调用 C1 的接口 C1.I1 和 C2 的接口 C2.I1 ,这两个接口都要调用 A 的接口 A1.I1 ,这样我们为何不先调 A1.I1 ,然后把返回的信息传给 C1 和 C2 呢?当然当我们没有这种冗余调用的时候,还是用原来的接口,这样更方便。

    我分析下来这样做从性能方面应该是有利无害的。从 IO 方面来看,虽然直接传递 item 信息增加了传递参数的网络开销,但是由于不需要去查 item 服务,减少了一次 item 服务返回 item 信息的网络开销,这两者已经抵消了。而后者还减少了查 item 的请求网络开销,item 服务的计算开销,和依赖服务或 db 的计算和网络开销。所以性能无疑是提高了很多。

    上面只讨论了最简单的情况,其实实践中远比这复杂。上层服务会依赖多个下层服务,组合是千变万化的,不可能为每种组合都开个接口。比如 C 依赖 A1 和 A2,那就要四个接口,依赖 A1 的接口( A2 信息通过传参);依赖 A2 的接口( A1 信息通过传参);都不依赖(全部信息通过传参);都依赖。假设依赖 n 个服务,就需要(排列组合 n 选 n 、n-1...1,0 加和)个接口(感觉像科里化过程...),所以还是要根据实际情况具体问题具体分析,只提供那些用户普遍需要的组合。
    sutra
        14
    sutra  
       2021-08-17 00:05:53 +08:00
    非业务相关的逻辑都可以从这个方法里移除。
    比如缓存处理,就可以放到应该被缓存的对象的服务层那里,并通过 annotation,比如 @CacheResult/@Cachable 这类 spring-cache 的注解来完成。
    对象转换,可以通过代码分层到专门做对象转换的类里,比如像他这样: https://juejin.cn/post/6844903685860884488
    jorneyr
        15
    jorneyr  
       2021-08-17 09:04:44 +08:00
    如果是业务代码,且重复出现的次数没有,就这么写,不要优化,否则优化后连自己都不认识了、
    aliveyang
        16
    aliveyang  
       2021-08-17 09:12:08 +08:00
    不只是优化代码,还要优化业务
    sss15
        17
    sss15  
       2021-08-17 09:26:32 +08:00
    @ljzxloaf 手动点赞
    litchinn
        18
    litchinn  
       2021-08-17 10:26:14 +08:00   1
    观察者模式之事件编程之 spring event,可以试下
    CasualYours
        19
    CasualYours  
       2021-08-17 10:48:31 +08:00
    我之前曾经尝试把业务逻辑实现改为链式调用的形式,看上去是舒服了一点。
    TaskWrapperProvider.init(wrapperMapper).createWrapper(taskId).signIn(signParams); // 任务签到的需求
    masterclock
        20
    masterclock  
       2021-08-17 10:50:43 +08:00
    从上到下,没有分支,应该是最完美的代码了,仅次于没有代码。
    最大的问题是写不出这样的代码
    - 业务流程会变化
    - 业务流程会失败
    - 非业务流程的代码也会失败
    - 到处都有可能失败
    - ……

    然后就可以考虑架构设计,代码抽象等来解决问题了
    dqzcwxb
        21
    dqzcwxb  
       2021-08-17 11:24:28 +08:00
    思维导图
    沟通
    spring event
    lambda stream optional
    extract method
    cutepig
        22
    cutepig  
       2021-08-17 13:19:13 +08:00 via Android
    最近在看函式程,
    感如果你拆分的子函 func1,。。。能到函式的那种重用性,那麽感会很不一样
    DICK23
        23
    DICK23  
       2021-08-18 14:00:36 +08:00
    用 AOP 去搞?
    securityCoding
        24
    securityCoding  
       2021-08-18 16:15:44 +08:00
    既然存在上下文关系,考虑定义一个 filter 接口来隔离各个功能实现吧
    funbox
        25
    funbox  
       2021-08-19 10:51:58 +08:00
    把业务逻辑代码写那么复杂 别人砍你
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2933 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 25ms UTC 13:11 PVG 21:11 LAX 06:11 JFK 09:11
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86