Java 作为一个库作者,期望用户根据情况将null传递给方法参数,这是一个糟糕的设计吗?

Java 作为一个库作者,期望用户根据情况将null传递给方法参数,这是一个糟糕的设计吗?,java,api-design,Java,Api Design,我正试图编写一个供外部用户使用的库,我被一些基本的设计决策所困扰 我正在编写一个简单的POJO类,它将包含一些关于OAuth2令牌的信息 此类课程需要满足以下条件: 如果令牌是永久令牌,则用户只需传递令牌 如果令牌是临时令牌,则用户需要传递令牌、expirationDate和refreshToken 永远不会出现只有expirationDate和refreshToken中的一个为空的情况。两者都将为null或都为非null 这就是我到目前为止所做的: public TokenInformat

我正试图编写一个供外部用户使用的库,我被一些基本的设计决策所困扰

我正在编写一个简单的POJO类,它将包含一些关于OAuth2令牌的信息

此类课程需要满足以下条件:

  • 如果令牌是永久令牌,则用户只需传递令牌
  • 如果令牌是临时令牌,则用户需要传递令牌、expirationDate和refreshToken
  • 永远不会出现只有expirationDate和refreshToken中的一个为空的情况。两者都将为null或都为非null
这就是我到目前为止所做的:

public TokenInformation(String token, Date expirationDate, String refreshToken) {
    Objects.requireNonNull(token, "token parameter cannot be null.");
    this.token = token;

    if ((expirationDate != null) && (refreshToken != null)) {
        this.expiresIn = expiresIn;
        this.refreshToken = refreshToken;
        isPermanentToken = false;
    } else if ((expirationDate == null) && (refreshToken == null)) {
        this.expiresIn = null;
        this.refreshToken = null;
        isPermanentToken = true;
    } else {
        // throw some exception here
    }
}  
老实说,我并不完全满意代码的外观

但我确实有以下想法:

  • 有两个构造函数。一个仅具有一个参数(用于永久令牌),另一个具有所有三个参数(临时令牌)。但是,我担心的是,用户不会正确阅读文档,会使用永久令牌构造函数作为临时令牌
  • 有两种不同的工厂方法,而不是构造函数。这些工厂方法将被清楚地命名,因此用户使用错误方法的可能性很小。此外,用户不会被强制显式传递null
我认为第二个想法可能是最好的方法。例如,我想不出任何Java API需要传递null,这可能暗示我粘贴的代码是个坏主意。此外,Java相当多地使用工厂方法,因此对于我的库的用户来说,它不会是一种不熟悉的模式


不过我想听听其他人的意见。因此,如果您需要任何其他信息,请告诉我。

我更愿意将永久和临时令牌行为封装到各自的域中,以便库中的任何用户都清楚地知道正在实例化哪种令牌

我认为建议的课程:

/**
 * The base class encapsulates the behavior of generic token
 */
public class AbstractToken {

    protected String token;

    // other properties that exist very closely with token

    public String getToken() {
        return token;
    }
}
永久令牌的域

/**
 * The domain encapsulates the behaviour of Permanent token - token that never expires
 */
public class PermanentToken extends AbstractToken {

    // more attributes that makes general token as Parmament token

    /**
     * Instantiates a new Permanent token - this token never expires
     *
     * @param token the token
     */
    public PermanentToken(String token) {
        this.token = token;
    }
}
临时令牌的域:

/**
 * The domain for Temporary token.
 */
public class TemporaryToken extends AbstractToken {

    private Date expirationDate;
    private String refreshToken;

    // more attributes that makes general token as temporary token

    /**
     * Instantiates a new Temporary token with token expiry date and refresh token
     *
     * @param token          the token
     * @param expirationDate the expiration date
     * @param refreshToken   the refresh token
     */
    public TemporaryToken(String token, Date expirationDate, String refreshToken) {
        this.token = token;
        this.expirationDate = expirationDate;
        this.refreshToken = refreshToken;
    }
}
现在库的用户清楚地知道他/她想要实例化和使用哪种令牌

附言-我想你可以为你的域名保留更好的名字 你也可以为你的图书馆做生意


基于上面的代码,我认为多构造函数方法比工厂方法更合适。有多个构造函数是一种非常常见的Java设计模式,用户可以在这些构造函数上传递他们确实有数据的字段的信息。构造函数不会复制代码,而是使用默认值相互调用

public TokenInformation(String token) {
  this(token, null, null);
}

public TokenInformation(String token, Date expirationDate, String refreshToken) {
    Objects.requireNonNull(token, "token parameter cannot be null.");
    this.token = token;

    if ((expirationDate != null) && (refreshToken != null)) {
        this.expiresIn = expiresIn;
        this.refreshToken = refreshToken;
        isPermanentToken = false;
    } else if ((expirationDate == null) && (refreshToken == null)) {
        this.expiresIn = null;
        this.refreshToken = null;
        isPermanentToken = true;
    } else {
        // throw some exception here
    }
}
由于您没有创建多种不同类型的
TokenInformation
对象,因此我找不到适合您需要的工厂模式。如果你要使用一种模式,我会选择Builder而不是factory

编辑:我应该更好地阅读你的代码。我将在这里留下我的答案,因为我认为如果您不想更改您的类,我的解决方案仍然适用,但是,我认为@DHRUV BANSAL有更好的方法,因为您的令牌实际上代表不同的东西


在javadocs中阐明您的愿望并用先决条件强制执行它们应该很容易。罪恶在于让用户猜测你的意图。这是基于观点的。在我看来,您应该只创建有意义的消息签名;如果有一个版本中您要传入
null
s,那么您就有一个
(字符串标记)
方法,以及一个所有内容都应该有值的方法。@duffymo我同意。然而,我也希望通过编程尽可能清楚地表达我的意图。有很多人浏览文档,这可能会导致一个滑坡,人们会感到不安,即使从技术上讲这是他们的错。如果可能的话,我想避免这种情况。@DaveNewton-我不同意这是基于观点的。我正在寻找标准的最佳实践。我希望了解普通Java程序员对他们使用的库的期望。最佳实践是根据定义提出的意见。