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

一些程序封装的疑问

  •  
  •   basefas · 2020-04-23 15:42:13 +08:00 · 3216 次点击
    这是一个创建于 1677 天前的主题,其中的信息可能已经有所发展或是发生改变。

    例如一个获取用户信息的功能,但是可能不同接口需要不同的字段。假设 user 表包含 id, created_at, updated_at, username, password, email 字段,一个接口需要 idusername,另一个接口需要 usernameemail,项目为普通的 mvc 架构,controller 层提供接口,service 层对接 controller 和数据库,现在我能想到的有三种方式,不知道大家平时都怎么使用。

    1. service 层方法查出 user 的全部字段,在 controller 层通过创建新 object,将所需字段赋值给 object
    2. service 层方法参数和返回值设置为 interface,在 controller 层调用时将需要查询的 object 作为参数传入
    3. service 层写两个方法,分别返回不同 object

    1 的问题在于需要做额外的赋值操作,2 的话入参结构有不确定性,可能引起问题,3 的话主要是会多些重复代码,但是能保证方法返回可预期,个人比较倾向方法 3 。 其实还有方法 4 就是什么都不处理都返回去,让调用方筛选,不过这个被直接 pass 了。

    30 条回复    2020-04-24 10:16:01 +08:00
    lhx2008
        1
    lhx2008  
       2020-04-23 15:57:17 +08:00 via Android
    把隐藏的字段置空,序列化的时候隐藏
    lhx2008
        2
    lhx2008  
       2020-04-23 15:59:35 +08:00 via Android
    第二个是 controller 序列化的时候加一个策略,传入一个配置来控制序列化器的行为
    lhx2008
        3
    lhx2008  
       2020-04-23 16:00:30 +08:00 via Android
    第三个方法就是配多个 VO 对象,在 controller 转换
    basefas
        4
    basefas  
    OP
       2020-04-23 16:11:45 +08:00
    @lhx2008 你说的这些都是在 service 层把所有字段都查出来,在 controller 层做格式化是么?
    cxe2v
        5
    cxe2v  
       2020-04-23 16:48:43 +08:00
    1,3 都可,不建议用 2
    KentY
        6
    KentY  
       2020-04-23 16:53:28 +08:00
    为什么不能把包含所有字段的 object 给不同请求方呢? 他不需要可以不用呀.
    如果一些信息的保密 /屏蔽是必须的, 那不管怎么都需要另外一步, 要么屏蔽相关信息, 要么 convert into other objects
    johnj
        7
    johnj  
       2020-04-23 17:02:25 +08:00
    如果你用的是 Spring MVC,那么可以用 Jackson 的 JSON View 注解 @JsonView

    1 做一个分类的类(名称随便)
    public class Views {
    public static class Public {
    }

    public static class Internal {
    }
    }
    2 根据分组 在 User 对象属性上加注解 如 @JsonView(Views.Public.class)
    3 在 controller 方法上 确定要返回的分组 如 @JsonView(Views.Public.class)

    参考: https://www.baeldung.com/jackson-json-view-annotation
    basefas
        8
    basefas  
    OP
       2020-04-23 17:03:02 +08:00
    @KentY 总体思想是尽量保持简洁,保持接口返回数据的最小化(说白了就是一种强迫症)。比如 user 表可能有 50 个字段,但是有时候可能只想要一个用户 ID,那返回数据中大部分就都没有用,还占带宽。还有就是例子中说的,可能会有 created_at 这种字段,一般情况下是不想直接返回给请求方的。
    lhx2008
        9
    lhx2008  
       2020-04-23 17:03:52 +08:00 via Android
    @basefas 如果对象查询次数不多的话不用专门写 service
    basefas
        10
    basefas  
    OP
       2020-04-23 17:06:17 +08:00
    @johnj 虽然没用过 Spring MVC,但是这个思想就是用一个 model 都查出来,然后在 controller 层处理数据吧
    johnj
        11
    johnj  
       2020-04-23 17:08:08 +08:00
    @basefas 对的 service 不应该管上层的事
    johnj
        12
    johnj  
       2020-04-23 17:09:06 +08:00
    @basefas 哦 你不是 java 栈的 那这些方法应该不适用了 看你用的序列化库有什么样的支持吧
    KentY
        13
    KentY  
       2020-04-23 17:09:09 +08:00
    @basefas 如果没有实质的业务需求, 我觉得这样没太大必要. 返回值简捷并不算简捷(从某种意义讲).
    比如需要 id, uid 的请求某天说, 我们想要姓氏, 那你后端就要重新改动部署. 这是完全没必要的. 至于带宽性能方面, 我不知道你们的项目是多么的 performance critical, 按一般情况讲, 这点带宽不算什么.

    当然了, 我没了解你的项目需求, 只是说看到这个问题的一般感觉. 特殊情况是需要特殊对待.
    basefas
        14
    basefas  
    OP
       2020-04-23 17:10:01 +08:00
    @johnj 嗯嗯,也是一种思路
    basefas
        15
    basefas  
    OP
       2020-04-23 17:12:06 +08:00
    @KentY 嗯,你说的有道理,我也没有具体的需求,只是考虑在没有限制条件的情况下,怎么写才比较优雅。
    wutiantong
        16
    wutiantong  
       2020-04-23 17:51:55 +08:00
    记好 kiss 原则
    vitoliu
        17
    vitoliu  
       2020-04-23 17:53:47 +08:00
    public class Simple{}
    public class Medium extends Simple{}
    public class Hard extends Medium{}
    WUWENZE
        18
    WUWENZE  
       2020-04-23 18:04:12 +08:00
    这种需求我弄过,使用的是注解+拦截器,内部实现是 Fastjson 的 JSONSerializer

    最终的效果就像这样
    ```java

    @ApiResponseFilters({ //
    @ApiResponseFilter(clazz = UserVO.class, includeFields = "id,name,nickname,photo,type"), //
    @ApiResponseFilter(clazz = UserGroupVO.class, includeFields = "id")
    })



    ```
    aguesuka
        19
    aguesuka  
       2020-04-23 18:06:10 +08:00 via Android
    我也有类似需求。系统有 3 张核心表,这三张核心表 join 其他十多张表可以查出来一条完整的数据。系统里大部分操作就是核心表 join 若干非核心表分页条件动态查询。现在我就想写一个 god 方法,根据前台要查的字段,要过滤的条件自动生成最优 sql,想了想难度不亚于实现个 orm 还是算了。
    WUWENZE
        20
    WUWENZE  
       2020-04-23 18:08:49 +08:00
    @aguesuka 你可能需要的是 https://graphql.cn/
    WUWENZE
        21
    WUWENZE  
       2020-04-23 18:13:57 +08:00
    PopRain
        22
    PopRain  
       2020-04-23 18:16:23 +08:00
    如果是 c# ,分别返回不同对象很简单,就动态声明一个对象,包括需要的对象属性即可。java 不熟
    Cmdhelp
        23
    Cmdhelp  
       2020-04-23 18:21:43 +08:00
    一个接口一个方法,不混用
    MOONYANYI
        24
    MOONYANYI  
       2020-04-23 18:49:43 +08:00
    ![Snipaste_2020-04-23_18-46-31.png]( )
    yafoo
        25
    yafoo  
       2020-04-23 19:54:40 +08:00 via Android
    方法 2,动态传参,最合理。
    方法 1,造成很多多余字段。
    方法 3,部分接口可以使用。
    pastgift
        26
    pastgift  
       2020-04-23 20:20:52 +08:00 via iPhone
    调用方指定需要的字段,统一过滤返回字段,合并成一个接口。
    不能面向前端写后端,一个需求一个接口这么做,后端本身要自洽
    xuanbg
        27
    xuanbg  
       2020-04-23 20:42:59 +08:00
    返回使用不同的 dto 即可,不同类型的转换可以用 A 对象序列化后再反序列化为 B 来转,不需要一个个 get/set
    no1xsyzy
        28
    no1xsyzy  
       2020-04-23 21:19:42 +08:00
    @basefas #15 你这样强行缩略并不优雅
    Premature optimization is the root of all evil.
    neilq
        29
    neilq  
       2020-04-23 21:44:11 +08:00   ❤️ 1
    我是 1,3 结合着来的,
    关于 1,我不是手动对两个 object 赋值,而是用了相应 mapper 库,c#的话用 AuthMapper 。
    但是 1 不能解决返回指定字段,一开始我是让前端参数指定需要的字段,动态得只返回相应字段。后来觉得没有必要,而是根据业务情况,拆分成不同接口,这样语义化比较清晰。基础 get 接口返回一般性的基础字段,最多十几个把。其他字段可能只是具体的某一项业务用到,根据业务语义拆分接口,使用不同的 dto 。
    另外补充一句,对于一个对象或者分页类几十条数据,10 个字段和 50 个字段在 http 上的性能损耗几乎可以忽略。对于一次返回几百几千条数据的可以单独优化,没必要在框架上做抽象。
    buffzty
        30
    buffzty  
       2020-04-24 10:16:01 +08:00
    我是这样弄的, 查询的时候可以带上 scene 参数. 默认是 defaultScene, 普通人获取自己的详情就是 default, 管理员获取就是 adminScene. 注意要对 scene 做权限检查
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2679 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 15:32 · PVG 23:32 · LAX 07:32 · JFK 10:32
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.