设计模式之美
王争
前Google工程师,《数据结构与算法之美》专栏作者
立即订阅
20015 人已学习
课程目录
已更新 46 讲 / 共 100 讲
0/6登录后,你可以任选6讲全文学习。
开篇词 (1讲)
开篇词 | 一对一的设计与编码集训,让你告别没有成长的烂代码!
免费
设计模式学习导读 (3讲)
01 | 为什么说每个程序员都要尽早地学习并掌握设计模式相关知识?
02 | 从哪些维度评判代码质量的好坏?如何具备写出高质量代码的能力?
03 | 面向对象、设计原则、设计模式、编程规范、重构,这五者有何关系?
设计原则与思想:面向对象 (11讲)
04 | 理论一:当谈论面向对象的时候,我们到底在谈论什么?
05 | 理论二:封装、抽象、继承、多态分别可以解决哪些编程问题?
06 | 理论三:面向对象相比面向过程有哪些优势?面向过程真的过时了吗?
07 | 理论四:哪些代码设计看似是面向对象,实际是面向过程的?
08 | 理论五:接口vs抽象类的区别?如何用普通的类模拟抽象类和接口?
09 | 理论六:为什么基于接口而非实现编程?有必要为每个类都定义接口吗?
10 | 理论七:为何说要多用组合少用继承?如何决定该用组合还是继承?
11 | 实战一(上):业务开发常用的基于贫血模型的MVC架构违背OOP吗?
12 | 实战一(下):如何利用基于充血模型的DDD开发一个虚拟钱包系统?
13 | 实战二(上):如何对接口鉴权这样一个功能开发做面向对象分析?
14 | 实战二(下):如何利用面向对象设计和编程开发接口鉴权功能?
设计原则与思想:设计原则 (12讲)
15 | 理论一:对于单一职责原则,如何判定某个类的职责是否够“单一”?
16 | 理论二:如何做到“对扩展开放、修改关闭”?扩展和修改各指什么?
17 | 理论三:里式替换(LSP)跟多态有何区别?哪些代码违背了LSP?
18 | 理论四:接口隔离原则有哪三种应用?原则中的“接口”该如何理解?
19 | 理论五:控制反转、依赖反转、依赖注入,这三者有何区别和联系?
20 | 理论六:我为何说KISS、YAGNI原则看似简单,却经常被用错?
21 | 理论七:重复的代码就一定违背DRY吗?如何提高代码的复用性?
22 | 理论八:如何用迪米特法则(LOD)实现“高内聚、松耦合”?
23 | 实战一(上):针对业务系统的开发,如何做需求分析和设计?
24 | 实战一(下):如何实现一个遵从设计原则的积分兑换系统?
25 | 实战二(上):针对非业务的通用框架开发,如何做需求分析和设计?
26 | 实战二(下):如何实现一个支持各种统计规则的性能计数器?
设计原则与思想:规范与重构 (11讲)
27 | 理论一:什么情况下要重构?到底重构什么?又该如何重构?
28 | 理论二:为了保证重构不出错,有哪些非常能落地的技术手段?
29 | 理论三:什么是代码的可测试性?如何写出可测试性好的代码?
30 | 理论四:如何通过封装、抽象、模块化、中间层等解耦代码?
31 | 理论五:让你最快速地改善代码质量的20条编程规范(上)
32 | 理论五:让你最快速地改善代码质量的20条编程规范(中)
33 | 理论五:让你最快速地改善代码质量的20条编程规范(下)
34 | 实战一(上):通过一段ID生成器代码,学习如何发现代码质量问题
35 | 实战一(下):手把手带你将ID生成器代码从“能用”重构为“好用”
36 | 实战二(上):程序出错该返回啥?NULL、异常、错误码、空对象?
37 | 实战二(下):重构ID生成器项目中各函数的异常处理代码
设计原则与思想:总结课 (3讲)
38 | 总结回顾面向对象、设计原则、编程规范、重构技巧等知识点
39 | 运用学过的设计原则和思想完善之前讲的性能计数器项目(上)
40 | 运用学过的设计原则和思想完善之前讲的性能计数器项目(下)
设计模式与范式:创建型 (2讲)
41 | 单例模式(上):为什么说支持懒加载的双重检测不比饿汉式更优?
42 | 单例模式(中):我为什么不推荐使用单例模式?又有何替代方案?
不定期加餐 (3讲)
加餐一 | 用一篇文章带你了解专栏中用到的所有Java语法
加餐二 | 设计模式、重构、编程规范等相关书籍推荐
春节特别加餐 | 王争:如何学习《设计模式之美》专栏?
免费
设计模式之美
登录|注册

36 | 实战二(上):程序出错该返回啥?NULL、异常、错误码、空对象?

王争 2020-01-24
我们可以把函数的运行结果分为两类。一类是预期的结果,也就是函数在正常情况下输出的结果。一类是非预期的结果,也就是函数在异常(或叫出错)情况下输出的结果。比如,在上一节课中,获取本机名的函数,在正常情况下,函数返回字符串格式的本机名;在异常情况下,获取本机名失败,函数返回 UnknownHostException 异常对象。
在正常情况下,函数返回数据的类型非常明确,但是,在异常情况下,函数返回的数据类型却非常灵活,有多种选择。除了刚刚提到的类似 UnknownHostException 这样的异常对象之外,函数在异常情况下还可以返回错误码、NULL 值、特殊值(比如 -1)、空对象(比如空字符串、空集合)等。
每一种异常返回数据类型,都有各自的特点和适用场景。但有的时候,在异常情况下,函数到底该返回什么样的数据类型,并不那么容易判断。比如,上节课中,在本机名获取失败的时候,ID 生成器的 generate() 函数应该返回什么呢?是异常?空字符?还是 NULL 值?又或者是其他特殊值(比如 null-15293834874-fd3A9KBn,null 表示本机名未获取到)呢?
函数是代码的一个非常重要的编写单元,而函数的异常处理,又是我们在编写函数的时候,时刻都要考虑的。所以,今天我们就聊一聊,如何设计函数在异常情况下的返回数据类型。
取消
完成
0/1000字
划线
笔记
复制
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
该试读文章来自付费专栏《设计模式之美》,如需阅读全部文章,
请订阅文章所属专栏。
立即订阅
登录 后留言

精选留言(27)

  • Jxin
    回答问题
    1.抛出异常,因为服务器获取不到host是一种异常情况,并且打印的异常日志不能是warm,而是err,因为该异常不会自动回复。

    2.往上抛,原封不动。应该在api统一出口处处理异常,这样异常代码会比较聚合(个人习惯)。该异常描述已经很准确,且处理异常依旧在genId接口中,所以上层函数可以认识该异常,所以原封不动。(而统一出口函数,则可以抛自定义异常,以收敛api使用方的考虑范围)。

    3.抛出异常,null值裁剪名称是一种异常情况。或则说,对于裁剪名称这个函数,入参不能为null。

    4.返回空字符串。小于等于0说明不需要带随机后缀,这也是一个正常的业务场景。返回空字符串是为了方便调用方不用做null判断。


    分歧:
    1.get,find,select等dao层操作,返回null是正常业务情况,表示数据不存在。但在其应用层,数据不存在可能意味着有脏数据,数据缺失等情况,属于异常情况,需要抛出异常。所以同样是get方法,持久层返回null,业务层返回可能是异常。

    2.异常流开销大,在对响应时间要求很严格的场景。放弃合理的异常处理,采用不合理的特殊返回值的方式也是合理的。所以合理的运用异常流在java也是一个选择项。在可读和性能我们需要权衡,而这两玩意经常是相驳的。

    最后:
    祝栏主和同学们新年快乐!
    2020-01-24
    12
  • 辣么大
    1、不抛。返回null-123123784378-aldjf780。从功能上讲,函数是生成logtraceid,用于给记录加id,便于查找日志。返回null不影响定位问题,同时程序不会蹦。
    2、上抛,到generate中处理。
    3、返回空串
    4、返回空串
    2020-01-25
    2
  • W.T
    Happy new year!
    鼠年大吉🎊🎈🎉🍾️🎆🧧

    作者回复: 新年快乐~

    2020-01-25
    2
  • 李小四
    设计模式_36:
    # 作业
      1. 返回了null,不合理
      2. 直接re-throw
      3. 应该返回NULL值
      4. 抛出一个自定义的异常

    # 感想
      异常这种机制的设计,是为了更好地处理真实的异常情况,要合理地使用,不要为了怕麻烦就乱用,制造出一个个排查问题的灾难。
    2020-02-07
    1
  • Monday
    看业务需求吧,如果是唯一性可以返回一个特殊值。如果后续需要通过id获取主机编号等,就要抛异常
    2020-02-03
    1
  • 平风造雨
    不能恢复的异常应该抛出,能处理能恢复的可以吞掉,但是吞掉的异常要有办法在日志或者其它办法看到异常的原因,便于后续排查问题。异常是否要重新定义异常并抛出,不能一概而论,某些情况下,异常的值和类型本身就是接口约定中的一部分,特别是unchecked异常。
    2020-01-24
    1
  • 南山
    从团队的实践来看,异常统一只靠人为约定是比较难实行的,团队成员理解不一样,实际写代码时候各种原因不按约定来。通过插件,或者IDE自动监测的手段会比较好,比如sonar。
    2020-01-24
    1
  • sunnywhy
    第二种返回Null的情况,可以使用Optional吗

    作者回复: 可以,Java8的语法,因为有些朋友不熟悉java语言,所以高级语法我就没讲了

    2020-01-24
    1
    1
  • Ken张云忠
    对于 generate() 函数,如果本机名获取失败,函数返回什么?这样的返回值是否合理?
    返回null-时间戳-8位随机数字字母符号的字符串.
    这样返回合理.因为业务允许一定概率的id重复,并且时间戳-8位随机的数字字母重叠的概率本身就很低,所以代码可以满足业务继续执行,至于最终要不要继续执行可以由上层业务程序控制.

    对于 getLastFiledOfHostName() 函数,是否应该将 UnknownHostException 异常在函数内部吞掉(try-catch 并打印日志)?还是应该将异常继续往上抛出?如果往上抛出的话,是直接把 UnknownHostException 异常原封不动地抛出,还是封装成新的异常抛出?
    不应该内部吞掉异常,应该直接把异常原封不动地抛出.因为当前是非业务工具类,异常处理该要交由业务程序来处理.

    对于 getLastSubstrSplittedByDot(String hostName) 函数,如果 hostName 为 NULL 或者是空字符串,这个函数应该返回什么?
    hostName为NULL会抛出空指针异常,这时该要抛出异常由业务程序来处理;hostName为空字符串属于正常业务可以返回空字符串.

    对于 generateRandomAlphameric(int length) 函数,如果 length 小于 0 或者等于 0,这个函数应该返回什么?
    length小于0时会抛出异常,这时也该要抛出异常由业务程序处理;length等于0时也是正常业务可以返回空字符串.
    2020-02-07
  • 传说中的成大大
    第一问generate()函数出错 要返回值 那应该返回-1表示一个异常的值
    第二问应该向上抛出 毕竟调用函数还需要关心他的返回值
    第三问 空字符串
    第四问 返回-1表示一个异常值
    2020-02-04
  • 弹簧人
    func1() 抛出的异常是可以恢复。争哥,这个不是很明白。可恢复是指,func1中自己try catch吞掉,然后后续通过查看错误日志,手动恢复这个错误吗
    2020-02-02
  • Demon.Lee
    这节课堪称经典!!!😭
    2020-02-02
  • 守拙
    课堂讨论:



    1. 对于 generate() 函数,如果本机名获取失败,函数返回什么?这样的返回值是否合理?

       本机名获取失败时函数将返回null,是不合理的.除非特殊说明,否则上层调用者可能忽视null的存在,从而引发npe.

       

    2. 对于 getLastFiledOfHostName() 函数,是否应该将 UnknownHostException 异常在函数内部吞掉(try-catch 并打印日志)?还是应该将异常继续往上抛出?如果往上抛出的话,是直接把 UnknownHostException 异常原封不动地抛出,还是封装成新的异常抛出?

       ​ 首先,本人极端不愿使用Checked-Exception.异常在函数内部吞掉是不好的行为,因为发生异常时,上层调用者并不知道.除非经过妥善的处理,否则会影响业务的正常执行.

       ​ 如果在使用CE的情况下,是一定要向上抛出的.视情况是否包装成新的异常:原始异常准确达意,调用者很容易理解就可以直接抛出,原始异常有歧义或可能导致调用者困惑,就应该封装成新的异常.

       

    3. 对于 getLastSubstrSplittedByDot(String hostName) 函数,如果 hostName 为 NULL 或者是空字符串,这个函数应该返回什么?

       先贴原始代码

    ​ @VisibleForTesting

    ​ protected String getLastSubstrSplittedByDot(String hostName) {

    ​ String[] tokens = hostName.split("\\.");

    ​ String substrOfHostName = tokens[tokens.length - 1];

    ​ return substrOfHostName;

    ​ }

    ​ 原始情况下,会因为hostName为null产生的npe导致程序运行crash.可以为此函数添加一个用于校验 hostName合法的isHostNameValid()函数,在hostName非法时抛出运行时异常(Runtime-Exception),或在非法时返回代表非法的常量值.



    4. 对于 generateRandomAlphameric(int length) 函数,如果 length 小于 0 或者等于 0,这个函数应该返回什么?

    ​ 应该抛出运行时异常或返回代表非法的常量值.
    2020-01-28
  • 啦啦啦
    武汉加油
    2020-01-27
  • 逍遥思
    1. 返回”null-1577456311467-3nR3Do45”,不够合理,但勉强能接受
    2. 应该吞掉,因为 generate() 并不关心 getLastFiledOfHostName() 抛出的异常
    3. 返回 null
    4. 返回空字符串

    思考是为了下节课吸收更多!
    2020-01-27
  • javaadu
    对于今天课堂留的作业,我采用了统一的思路—能用异常解决的都用异常解决。

    1.对于 generate() 函数,如果本机名获取失败,函数返回什么?这样的返回值是否合理?
    答:抛出异常,这是一个无法恢复的情况,打断正常的处理流程并进入异常逻辑处理模块

    2. 对于 getLastFiledOfHostName() 函数,是否应该将 UnknownHostException 异常在函数内部吞掉(try-catch 并打印日志)?还是应该将异常继续往上抛出?如果往上抛出的话,是直接把 UnknownHostException 异常原封不动地抛出,还是封装成新的异常抛出?
    答:不应该内部吞掉,应该抛出到上层做统一的异常处理,这里是个单一的模块,不需要再封装

    3. 对于 getLastSubstrSplittedByDot(String hostName) 函数,如果 hostName 为 NULL 或者是空字符串,这个函数应该返回什么?
    答:抛出异常,异常消息是—hostName为NULL或空字符串

    4. 对于 generateRandomAlphameric(int length) 函数,如果 length 小于 0 或者等于 0,这个函数应该返回什么?
    答:抛出异常,异常消息是—参数不合法
    2020-01-27
  • Jeff.Smile
    做一个SZ程序员,不做MS程序员!跟进!
    2020-01-26
    2
  • Frank
    返回错误码这种情况,在写RPC接口,返回时经常用到。在刚开始开发工作时,我常用的函数出错返回类型是返回NULL值和抛出异常对象。后面逐接触到对于集合,如果无数据返回空集合这种方法。学习Java8后,为了避免NPE,可以使用Optional类来处理。听过某人说对于调用别人写的函数返回集合类型的数据,最好自己先判断NULL,再处理。如果是自己写返回集合数据的接口,若无数据,返回空集合,不要返回NULL,不要指望别人帮你处理好NULL的情况。以前对于函数出错时,怎么处理异常,一直没有一个很好的理解,自己常用的做法就是往上抛出。
    2020-01-25
  • L🚲🐱
    1. 对于 generate() 函数,如果本机名获取失败,函数返回什么?这样的返回值是否合理?: 抛出异常, 因为获取失败是一个异常情况, 所以需要返回异常
    2. 对于 getLastFiledOfHostName() 函数,是否应该将 UnknownHostException 异常在函数内部吞掉(try-catch 并打印日志)?还是应该将异常继续往上抛出?如果往上抛出的话,是直接把 UnknownHostException 异常原封不动地抛出,还是封装成新的异常抛出?: 继续原封不动的往上抛出, 可以在 Controller 层统一处理, 这样子异常的代码会比较统一的处理, 该异常的描述已经很明确了, 可以通过类名知道这个异常是什么.
    3. 对于 getLastSubstrSplittedByDot(String hostName) 函数,如果 hostName 为 NULL 或者是空字符串,这个函数应该返回什么?: hostName 为空是一种异常, 应该抛出异常, 因为入参 hostName 不能为空
    4. 对于 generateRandomAlphameric(int length) 函数,如果 length 小于 0 或者等于 0,这个函数应该返回什么?: 按照项目组统一规定来, 个人习惯返回 空字符串, 这么返回是方便调用方不用做非空判断
    2020-01-25
  • Fstar
    个人见解。

    1. 获取本机名失败可以返回一个默认值,比如 "unknown"。因为 Id 只要随机就行,hostName其实不是很重要。不过可以考虑发送一个提醒,告知开发者运行环境有无法获取本机名的问题。
    2. 建议吞掉。因为这个问题并不严重,没有必要停止程序。如果要往上抛出,原封不动地抛出,因为这个抛出错误原因很容易理解,不需要进一步包装。
    3. 返回空字符吧。进行 NULL 判断可读性不好。
    4. 感觉抛出错误比较好,因为这个算是开发者不细心了,让开发者知道错误,然后进行修改。如果不抛出错误,可以考虑返回空字符数组。
    2020-01-24
收起评论
27
返回
顶部