V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
boydfd
V2EX  ›  程序员

代码有重复不应该想尽办法去除重复吗?

  •  
  •   boydfd · 2016-03-25 10:24:45 +08:00 · 6847 次点击
    这是一个创建于 3170 天前的主题,其中的信息可能已经有所发展或是发生改变。

    我负责用 Go 写一个从 Redis 或 MySQL 中取数据,并且通过 http 协议将数据返回给客户端。

    现在有三个查询,分别是当前,分时,分天的数据。因为发现重复的代码很多,所以我就想通过接口来让它们达到泛化的目的。

    我现在的流程时这样的,三个 http 请求都使用同一个回调函数,并传入一个接口,通过接口实现 Redis 或 MySQL 部分在查询的时候,三种不同的处理,由于返回结果的数据接口是一样的,所以返回不成问题。

    后来我的上司看了之后,说我这样写不好,要我分开写,他的理由是: 1:这三个请求是不同的请求,不要统一处理。 2:这么写的话,别人看我的代码会比较麻烦。

    请问,他说的对吗?能否针对他提的两个理由进行分析一下

    56 条回复    2016-03-26 09:17:21 +08:00
    rogerchen
        1
    rogerchen  
       2016-03-25 10:36:10 +08:00 via Android   ❤️ 1
    给用户的 API 不要搞万能 API , API 的内部实现可以尽量复用相同部分。
    just4test
        2
    just4test  
       2016-03-25 10:40:23 +08:00
    一个内部的可以传入时间段的接口,再另包三个给客户端
    repus911
        3
    repus911  
       2016-03-25 10:44:17 +08:00
    老板说的对...
    boydfd
        4
    boydfd  
    OP
       2016-03-25 10:45:32 +08:00
    @rogerchen 给用户的 API 不是一样的,三个不同的请求,但是之后调用的是同一个函数。我上司的意思是,三个不同的请求,之后调用三个函数。因为里面处理的过程是完全一样的,所以才想着去重复。
    @just4test 上司的意思是内部的这个不能做成传时间段的接口,要分成三个写啊。
    crayygy
        5
    crayygy  
       2016-03-25 10:46:56 +08:00
    API 内部实现可以使用调用某个方法去操作数据,但是对于无法看到内部实现的外部人员来说,能够直观的知道自己调用不同的 API 的用途和思路,这样会有更加清晰的逻辑。
    学生党个人观点,如果有什么不足的希望能够指正!谢谢。
    boydfd
        6
    boydfd  
    OP
       2016-03-25 10:47:26 +08:00
    @repus911 好吧,主要是感觉,每次有改动的地方都要在三个地方进行改动。既然他是对的,那就没办法了
    boydfd
        7
    boydfd  
    OP
       2016-03-25 10:48:27 +08:00
    @crayygy 外部人员肯定是调用不同的 API 啊,不然怎么区分这三种请求方式啊
    hitmanx
        8
    hitmanx  
       2016-03-25 10:52:20 +08:00
    可以留三个回调 callback_a() callback_b() callback_c()
    然后实现一个 xxx_common()

    callback_a()\_b()\_c()都调用这个 xxx_common()
    crayygy
        9
    crayygy  
       2016-03-25 10:53:25 +08:00
    @boydfd 我是这样的理解的啊不知道是不是。

    Interface_A{};
    Interface_B{}
    Interface_C{}

    这三个接口是给用户调用的,在三个接口的实现里,将能够复用的代码抽象成 function(),设置不同的参数调用。这样在修改的时候可以只修改 function()的代码。
    repus911
        10
    repus911  
       2016-03-25 11:00:49 +08:00
    @boydfd 哦 如果是内部还用三个函数接口的话 那要看函数的复杂度了 值不值得重构了(比较重复的话 建议重构)

    当然 作为老板提提对外的 API 的意见也就算了 为什么要管我的内部实现?

    别人看我的代码,谁来看?真的看不懂?看注释懂不懂?都是不确定的问题
    hitmanx
        11
    hitmanx  
       2016-03-25 11:06:59 +08:00
    @boydfd 哈才发现是你啊,已经找到工作了?恭喜了!
    boydfd
        12
    boydfd  
    OP
       2016-03-25 11:10:50 +08:00
    @hitmanx 我的做法就是这样,但是老板说我需要写成 callback_a()\_b()\_c()调用 a(),b(),c()的情况。

    @crayygy 不是这样的,是 interface interface_xxx {},然后有三个回调函数 a,b,c 在这里调用 xxx_common(interface_xxx),在 xxx_common 的内部进行针对不同的类型进行处理

    @repus911 现在的情况是我都写完了,他让我改成有重复的情况= =
    boydfd
        13
    boydfd  
    OP
       2016-03-25 11:11:43 +08:00
    @hitmanx 没,就是之前说的实习,用的 golang ,在这里先干着,我的理想还是 c++啊!!!!
    jmc891205
        14
    jmc891205  
       2016-03-25 11:17:23 +08:00
    看情况而定吧。。复用当然是好的
    但也不要为了复用就把可读性搞的一团糟
    xuxu
        15
    xuxu  
       2016-03-25 11:17:47 +08:00
    这个看情况,上司或许看到了业务以后每个接口会不一样
    boydfd
        16
    boydfd  
    OP
       2016-03-25 11:24:04 +08:00
    @jmc891205 我现在已经改完了,改后感觉函数变多了很多,找起来难多了,不知道是不是心理作用

    @xuxu 好的,这个确实是一种可能
    imn1
        17
    imn1  
       2016-03-25 11:26:14 +08:00
    大师兄说得对啊
    expkzb
        18
    expkzb  
       2016-03-25 11:32:29 +08:00
    "我上司的意思是,三个不同的请求,之后调用三个函数。"
    那你在这三个函数中再调用同一个函数如何呢?
    zhicheng
        19
    zhicheng  
       2016-03-25 11:34:17 +08:00
    你知道什么叫“继承”吗?
    jmc891205
        20
    jmc891205  
       2016-03-25 11:37:44 +08:00
    @boydfd 定位函数是 ide 或者 editor 的功能吧
    我之前见过有的人为了复用一个函数,在函数里有好多 if else 那样就不太好了
    boydfd
        21
    boydfd  
    OP
       2016-03-25 11:37:49 +08:00
    @expkzb 可能之前我说的有点难理解,再说清楚点,我写的是,三个请求,会调用不同的三个回调函数,在三个回调函数中,调用同一个函数。
    老板的意思是,三个回调函数中不要调用相同的函数,都分开写,这样写清晰一点。
    所以你的意思应该就是我之前的做法。
    heqichang
        22
    heqichang  
       2016-03-25 11:48:47 +08:00
    我也不理解 callback_a()\_b()\_c()调用 a(),b(),c(),如果 a(),b(),c()都一样的话,为啥要分哩。
    anerevol
        23
    anerevol  
       2016-03-25 11:51:17 +08:00
    重复的代码可以抽成函数,用组合方式减少重复。
    不太清楚以后需求的变化方向,也许不同请求返回接口格式也会发生不同的变化。
    JamesRuan
        24
    JamesRuan  
       2016-03-25 11:59:22 +08:00
    一个函数做一件事,单个函数不要超过一页。

    先按这两个原则去改改看看。

    Copy & Paste 永远都应该避免的,除非他们表示的其实是不同的东西,只是暂时还是相同而已,这个主要看语义。
    mhycy
        25
    mhycy  
       2016-03-25 12:12:11 +08:00
    具体情况具体分析,这个只有当事人理解
    注意重用代码的通用程度,如果三个函数都极其通用,那么这部分通用代码可以抽出来
    不要在通用函数内部做三个事情的分别处理,如果这么做,这叫增加复杂度

    另外楼上的继承其实不能乱用,不是重复代码多就一定要用继承的模式
    如果编写的架构不适用于 OOP 思维,强行使用也是会增加复杂度的

    剩下就自己判断吧
    没贴代码我们不好判断
    boydfd
        26
    boydfd  
    OP
       2016-03-25 12:40:05 +08:00
    @zhicheng golang 里没有继承啊,只能使用接口来复用,我这里的做法其实和继承是一样的。老板的意思是不要继承,分成三个类。

    @jmc891205 没, golang 里面复用是由接口来做的,不需要任何 if else 来判断,只需要针对不同的类型写不同的函数即可

    @anerevol 可能返回会发生变化吧。

    @JamesRuan 好的

    @mhycy 我现在写有点不一样,不是通过抽出通用部分的办法,而是“模板模式”,就是设计模式中的那个,先写好了框架,由于查找 redis 或 MySQL 的语句不是不同么?就是把这个不同,通过 golang 的接口来进行区分。代码的话挺多的,感觉贴不下
    mhycy
        27
    mhycy  
       2016-03-25 12:43:37 +08:00
    @boydfd
    只是查询语句不同而且差别只在某个标记位的话,可以抽出
    cxshun
        28
    cxshun  
       2016-03-25 12:47:38 +08:00
    方法尽量写得通用,一些比较特殊的业务由上层自己去处理,这个应该是尽量去满足的要求,在没法满足的情况下就另外想办法。

    实际上你分三个方法去处理,然后调同个函数,这是没问题的啊。但感觉你可以写个大概的代码结构出来,大家会方便知道具体情况哈。
    expkzb
        29
    expkzb  
       2016-03-25 12:58:19 +08:00
    @boydfd 原来如此,这我就不太理解你老板的意图了。你的做法以后改起来也灵活啊
    dallaslu
        30
    dallaslu  
       2016-03-25 13:31:07 +08:00
    都说代码如诗,可是诗歌算是把重复用到了极至。
    iniwap
        31
    iniwap  
       2016-03-25 13:34:21 +08:00
    忽然想上那张表情图
    9hills
        32
    9hills  
       2016-03-25 13:34:59 +08:00
    老板说的不对,你说的对。

    本质是一个接口,输入是时间,输出是 Data ,为啥要分开。。
    mengzhuo
        33
    mengzhuo  
       2016-03-25 13:35:16 +08:00 via iPhone
    接口和函数不是一个概念啊
    接口尽量单参数甚至无参数,这样看的人就比较清楚
    函数自然不重复
    你老板水平有限就这么简单
    phx13ye
        34
    phx13ye  
       2016-03-25 13:38:18 +08:00
    你老板不懂 dry
    calease
        35
    calease  
       2016-03-25 13:38:25 +08:00 via iPhone
    DRY 不是说代码一定不能重复。
    而是代码要简明扼要。
    所以 copy paste 并不一定是坏毛病,
    花时间构建通用的方法也并不一定就是最好的实现方式。
    很多人都有这种误区。
    至于楼主的个例,没有 context 无法判定。
    楼主不妨和人交流交流,对于对方的回答多问问为什么。不要别人说什么你就好的好的回头问其他人对不对。
    boydfd
        36
    boydfd  
    OP
       2016-03-25 13:47:41 +08:00
    @calease 我之前和老板已经讨论过,我是坚持自己这么写好,但是他只说它们是不一样的东西,就是因为他没有给我关键的理由,所以我才来这里问的啊。 copy paste 最大的坏处我就是觉得改动需要去三个地方改,不能统一进行更改,这样不仅麻烦,而且觉得会出错。
    chuhemiao
        37
    chuhemiao  
       2016-03-25 14:08:35 +08:00
    这你能忍!
    yxaaa123
        38
    yxaaa123  
       2016-03-25 14:11:47 +08:00
    IDEA 里直接大黄色浪线给标出来,一般人都没法忍
    wuyadong
        39
    wuyadong  
       2016-03-25 14:35:06 +08:00
    你是对的,但你 boss 怎么说你就按他的做。
    ybh37
        40
    ybh37  
       2016-03-25 15:31:56 +08:00
    接口 必须是 3 个不同的
    但是实现的时候,你可以使用一个公共函数统一处理。

    你可以理解为原先一个函数,现在拆成了 4 个
    quericy
        41
    quericy  
       2016-03-25 15:33:19 +08:00
    楼主的做法:
    Interface_A{
    //...
    a(parameter_1)
    }
    Interface_B{
    //...
    a(parameter_2)
    }
    Interface_C{
    //...
    a(parameter_3)
    }
    a(parameter){
    //Common Code
    }

    楼主老板想要改成:
    Interface_A{
    //...
    //Common Code
    }
    Interface_B{
    //...
    //Common Code
    }
    Interface_C{
    //...
    //Common Code
    }

    伪代码,不知道理解是不是对的,从 dry 原则上看楼主的做法并没有错,也更利于当前的维护
    但是也许老板是从需求和项目以后的可能性来考虑,
    "他只说它们是不一样的东西",是否意味着这以后是三个独立的业务,只是当前阶段还是相同的罢了?

    所以楼主还是需要根据具体的业务上下文来判断拆分还是复用更利于以后的维护和扩展.
    boydfd
        42
    boydfd  
    OP
       2016-03-25 15:46:27 +08:00
    @quericy 差不多是这样吧,我和他说以后如果需要独立,到时候改也很简单,然后他还是觉得分开好。
    herozzm
        43
    herozzm  
       2016-03-25 15:54:51 +08:00
    老板说的对,你还得考虑后期扩展性,比如需求以后有变动就好对照修改,别人接手也好看
    secret32
        44
    secret32  
       2016-03-25 16:01:51 +08:00
    我觉得即便以后是三个独立的业务,也应该以后再改成三个函数,现在还是用一个的好。
    用三个函数对 Java 来说,只有业务独立后可以支持热部署这一点好处,然而这好处对 Go 应该等于没有。
    boydfd
        45
    boydfd  
    OP
       2016-03-25 16:08:47 +08:00
    @secret32 我的想法和你是一样的!
    kaizixyz
        46
    kaizixyz  
       2016-03-25 16:22:48 +08:00
    看得懂比方便改更重要。
    xchange
        47
    xchange  
       2016-03-25 16:35:00 +08:00
    可能老板对公司其他程序员的水平比较悲观……
    holy_sin
        48
    holy_sin  
       2016-03-25 17:30:45 +08:00
    不用太纠结,但是问题来了 你觉得那个号
    ```
    if(){
    xxx
    }

    if () {
    xxx
    }

    if ()
    {
    xxx
    }
    ```
    otakustay
        49
    otakustay  
       2016-03-25 17:31:56 +08:00   ❤️ 1
    复制粘贴下我对这种事的看法

    1. 复用有很多的目的,但少写代码不应是其中一个。
    2. 复用的对象是逻辑,而非代码。
    3. 复用所要看的不仅仅是当下,更是未来的发展趋势是否一致。
    4. 参与业务的复用,在不理解业务的前提下往往会起到反作用。

    http://otakustay.com/talking-reuse/
    nonesuccess
        50
    nonesuccess  
       2016-03-25 17:41:27 +08:00
    有重复的话,应该去除。但是这句话不等于“代码”有重复就应该去除。
    应不应该去除,取决于这两段代码是不是一个东西,在你上司的眼里也许他们就不是。
    说简单点,代码里面到处有 int a = 0;你会想到抽取个公共函数么?
    bicoff9527
        51
    bicoff9527  
       2016-03-25 19:11:26 +08:00
    可读性最重要吧
    g00001
        52
    g00001  
       2016-03-25 19:34:38 +08:00
    什么东西都不能绝对化,不是非此即彼,
    有些时候重用的代码要封装,有些时候要避免过度设计、过度封装,以至于弄巧成拙。
    有一些那种什么都封装的代码,的确看着特别恼人,写的人自己觉得很优雅,看的人觉得很笨拙。
    zhaozhiming003
        53
    zhaozhiming003  
       2016-03-25 20:59:17 +08:00
    你老板很久没写代码了吧?
    whenov
        54
    whenov  
       2016-03-25 22:21:11 +08:00
    @boydfd ,给你个也许会让事情变得更加复杂的建议:
    写个简单的脚本,把你喜欢的代码转换成老板喜欢的代码。
    好处是可以如自己所愿,坏处是要多维护一个脚本。
    angelface
        55
    angelface  
       2016-03-25 22:25:31 +08:00
    不上代码,谁看的明, 说的清。
    qiumaoyuan
        56
    qiumaoyuan  
       2016-03-26 09:17:21 +08:00 via iPad
    只说原则:所有重复性的工作都应该教会机器,让机器来做——程序员的主要工作内容之一。

    之二是尽量让其他人更加容易地知道你对机器都说了些什么。

    没有了。

    所以,不让你去除重复,是错的。问题是如何去除重复,而不是要不要消除重复。

    “消除重复”和“代码易读”并没有矛盾,用你所使用的编程语言特性——比如面向对象——找到这两件事的平衡点。


    我总是觉得,新手没有底线的追求消除重复是很有必要,很有好处的。但是过程中遇到问题要想办法解决,而不是偷懒,选择认为此路不通。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3333 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 29ms · UTC 11:54 · PVG 19:54 · LAX 03:54 · JFK 06:54
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.