接口设计

Version1.0:2015-08-09 18:09

Version1.1:2015-10-10 21:05

新增接口设计中异常和错误码的思考

接口设计

内容列表:

  • 引言

  • 什么是接口

  • 接口返回值

  • 代码示例

  • 异常和错误码

  • 如何选择错误处理方式

引言

今天刚入职一个月,这个月主要做的事情是熟悉团队的技术栈和接口联调,主要包括以下几个方面:

  • 内部web框架学习,主要基于spring做了扩展。

  • 中间件学习:分布式缓存,分布式消息队列,分布式任务调度,分布式配置中心,数据访问层。

  • 内部流程发布平台

    因为曾经在这家公司实习过,所以在入职的第三天就接到了接口的联调的任务,刚开始也觉得接口联调很简单,慢慢的发现并不是想象的那个样子,个人觉得接口联调之前,要首先熟悉一下几个方面:

    在联调的过程中发现有些依赖系统接口设计的不是很合理,自己总结一下。

    • 业务

    • 当前负责系统的上游和下游,对每个接口的调用链路要清楚,自己要能针对某个接口的调用画出调用链路图

    • 依赖系统的接口人

什么是接口

维基百科关于接口的说明,由于近年来软件的规模日益庞大,常常会需要把复杂的系统划分成小的组成部分,编程接口的设计十分重要。程序设计的实践中,编程接口的设计首先要使软件系统的职责得到合理划分。良好的接口设计可以降低系统各部分的相互依赖,提高组成单元的内聚性,降低组成单元间的耦合程度,进而提高系统的维护性和扩展性。接口的几要素:

  • 返回值

  • 名称,唯一标志的,类似方法名:classloader -> package name : class name :function name

  • 传递参数

接口返回值

  • 参考开放平台: 微信关于接口的说明 淘宝开放平台关于接口的说明

  • 返回信息要明确: 如果接口调用失败,要分清楚什么是业务失败,什么是真正的失败。

  • 如果是机遇http协议实现的接口调用:应该使用http的状态码(标志此次请求的结果是成功还是失败,大致原因是在客户端还是在服务端)加上错误码(错误码用来表示失败的具体的原因是什么)

示例代码

定义一个通用的接口返回类,代码如下:

public class Result<T> {
    /**
     * 本次接口调用是否成功。
     * <li>true,表示成功,result返回数据</li>
     * <li>false,表示失败,errorMsg返回失败的原因</li>
     */
    protected boolean success;
    /**
     * success=false,表示失败,errorCode返回失败的错误码
     */
    protected String  errorCode;
    /**
     * success=false,表示失败,errorMsg返回失败的原因
     */
    protected String  errorMsg;
    /**
     * success=true,表示成功,result返回数据
     */
    protected T       result;

    /**
     * 默认构造函数
     */
    public Result() {
        super();
    }

    /**
     * 带数据结果的构造函数,默认为成功
     * @param result
     */
    public Result(T result) {
        super();
        this.result = result;
        this.success = true;
    }

    /**
     * 本次服务是否正常处理:
     * <li>如果是,说明可以获得结果对象</li>
     * <li>如果否,说明发生了异常,可以通过getErrorMsg()了解错误原因</li>
     * Getter method for property <tt>success</tt>.
     * 
     * @return property value of success
     */
    public boolean isSuccess() {
        return success;
    }

    /**
     * Setter method for property <tt>success</tt>.
     * 
     * @param success value to be assigned to property success
     */
    public void setSuccess(boolean success) {
        this.success = success;
    }

    /**
     * 当isSuccess()方法返回false时,返回错误原因
     * Getter method for property <tt>errorMsg</tt>.
     * 
     * @return property value of errorMsg
     */
    public String getErrorMsg() {
        return errorMsg;
    }

    /**
     * Setter method for property <tt>errorMsg</tt>.
     * 
     * @param errorMsg value to be assigned to property errorMsg
     */
    public void setErrorMsg(String errorMsg) {
        this.errorMsg = errorMsg;
    }

    /**
     * 当isSuccess()方法返回true时,可以通过该方法获得处理结果
     * Getter method for property <tt>result</tt>.
     * 
     * @return property value of result
     */
    public T getResult() {
        return result;
    }

    /**
     * Setter method for property <tt>result</tt>.
     * 
     * @param result value to be assigned to property result
     */
    public void setResult(T result) {
        this.result = result;
    }

    /**
     * Getter method for property <tt>errorCode</tt>.
     * 
     * @return property value of errorCode
     */
    public String getErrorCode() {
        return errorCode;
    }

    /**
     * Setter method for property <tt>errorCode</tt>.
     * 
     * @param errorCode value to be assigned to property errorCode
     */
    public void setErrorCode(String errorCode) {
        this.errorCode = errorCode;
    }

    /**
        ErrorEnum代表系统的错误码枚举
     * @param err
     */
    public void setError(ErrorEnum err) {
        if (err != null) {
            this.errorCode = err.getCode();
            this.errorMsg = err.getErrorMsg();
        }

    }

    /** 
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        return success + ", " + errorCode + ", " + errorMsg + ", " + result;
    }

    /**
     * @param <T>
     * @return
     */
    public static <T> Result<T> newResult(){
        return new Result<T>();
    }
}

通过此次联调,使得自己更加懂得在写接口的时候要把返回值描写清楚,因为自己的系统对别人来说完全是黑盒子,给定一个输入应该明确的给出返回信息。

异常和错误码

    在层与层之间调用接口的时候,有两种方式处理中间的错误。主要是异常、和错误码。关于哪种方式更好,网上也有很多争论,下面会先介绍异常和错误码,稍后给出自己的观点。

异常:

    本文以java语言为例来说明异常相关的问题。异常就是在执行一个程序正常逻辑的过程中,出现错误的现象。java中提供了很健壮的异常处理机制,来处理这些现象。

    首先要介绍一下,java中异常的类型,如下图所示。

    接口设计_第1张图片

1、Error:这种错误,比如常见的OutOfMemoryError 、StackOverflowError。这些错误信息都是无法恢复的,再比如硬件问题、jvm挂掉,所以在继承关系中,你会发现分成了Error和Exception。

2、Checkd Exceptions:检查型异常,是我们在程序中显示的声明并捕获的异常,并尝试恢复的异常。比如如过我们要从远程获取某个列表,但是网络出现异常,我们恢复的策略可以从本地读取。

3、Runtime Exceptions:运行时异常,往往是由于编程错误产生的,这种异常,一般我们会重新实现自己的异常类来使用。

    异常的类型介绍完了,给出一个自定义运行时异常的示例:

public class CustomerException extends RuntimeException {
    /**
     * success=false,表示失败,errorCode返回失败的错误码
     */
    protected String  errorCode;
    /**
     * success=false,表示失败,errorMsg返回失败的原因
     */
    protected String  errorMsg;
    //get&set
}

    在开发的过程中,每个开发者都不喜欢异常,那满满的红色,但是异常又是我们不得不每天面对的事情。什么情况下会产生异常呢?我们在开发的过程对输入的参数未做check、网络异常、db当掉、服务器挂掉等等的时候会出现异常。我们要理解java中异常处理的机制才能更好的使用异常。

    java是一种面向对象的编程语言,当程序执行某条语句出现异常的时候会抛出一个Exception的对象,然后运行时环境会中断正常的流程,并尝试找到该异常的处理策略。异常的对象包含很多信息,比如调用堆栈、出错的行号、异常的类型等。

    上面提到的异常处理策略实质上就是一段接收Exception对象并提供后续操作的代码。策略很简单,运行时环境首先找到抛出异常(check exception)的函数来寻找Exception Handler,如果找不到,此时调用该函数的方法肯定要处理,如果不处理他要在抛到上层,总有一个地方要处理,这里讲的是检查型异常。举个例子,如果调用关系是A->B->C,而且在C中是抛出异常的,那么运行时环境寻找Exception Handler的顺序是C->B->A。而运行时异常无需捕获。

错误码:

    错误码很简单,类似如下图描述的结构:

接口设计_第2张图片

我们需要一个全局唯一的错误码,然后需要一个字典文件描述错误码对应的错误信息,更好的话,也可以提供错误码对应的解决方案。

如何选择错误处理方式

//TODO

你可能感兴趣的:(接口设计,联调)