4

我正在考虑编写一个程序来检查 Java 中的“泄漏抽象”的想法。立即想到的一个领域是例外:

public class X
{
    // this one is fine, Readers throw IOExceptions so it is 
    // reasonable for the caller to handle it
    public void parse(final Reader r)
        throws IOException
    {
    }

    // This one is bad.  Nothing in the API indicate that JDBC 
    // is being used at all.
    public void process()
        throws SQLException
    {       
    }
}

请注意,我不希望对已检查/未检查异常的相对优点进行争论。我正在寻找的是人们拥有的其他示例(不一定是异常处理),这些示例也可以通过检查源代码或类文件来合理地捕获。

我知道 checkstyle、findbugs 和 PMD,而 AFAIK 都没有处理这个问题(我不反对将检查放入其中一个工具中,而不是自己编写)。

您是否想到了其他可以静态检查的泄漏抽象示例?

编辑:

第二个不好的原因是该方法抛出了一个异常,客户端无法知道正在使用 JDBC(例如,它可能是任何东西)。因此,“泄漏抽象”是正在使用 JDBC。如果底层机制更改为其他东西(例如 JPA,它是一个不同的数据库抽象库),那么异常也都需要更改。所以底层数据库库被泄露了。

4

4 回答 4

2

由于我没有在原始帖子中发现问题,因此我会漫无目的。

任何此类工具都必须让用户告诉它哪些异常符合给定类的非泄漏,并且任何不在此类列表中的东西都将是泄漏。

那只是例外。据我了解,泄漏抽象适用于更多。考虑一下:

class Client
{
    private Integer ID;

    public Integer ID() { return this.ID; }
}

这是否符合泄漏的条件?如果以后我需要将 ID 表示为 Long,那就可以了。您可能会修复这样的场景:

class Client
{
    private ClientID ID;

    public ClientID ID() { return this.ID; }
}

class ClientID
{
    private Integer value;

    public ClientID(String id) { this.value = Integer.parseInt(id); }

    public String asString() { return value.toString(); }

    ... other methods that don't reveal value's type here ...
}

这将解决客户端的泄漏,但现在您将在 ClientID 上运行您的工具。是否漏水?

如果您必须检查不是简单 getter 的方法,这可能会变得更加复杂。如果 ClientID 有一个方法可以对其 ID 进行一些数学运算(请原谅我)并返回一个 Integer。那是泄漏吗?这是否有资格泄漏该值是一个整数?

所以我不确定这是否是机器可以轻易捕捉到的代码气味。

于 2009-02-24T03:32:44.973 回答
2

所以。

如何检测 API 是否泄漏了实现细节或没有保持相同的抽象级别。

你可能会看这个谈话。它解释了好的 API 的外观和设计(您可以从好的实践中推断出哪些是不好的实践)

例如

功能应该很容易解释。如果它是一个硬名称,它通常是一个糟糕的设计。

从中您可以发现,如果方法或返回参数提供详细说明,它们与抽象级别不符。

例如高级方法

 -initProcess(): SGN_MTL

可能会泄露返回值的实现细节。

这里的难点是检测抽象级别何时发生变化。

例如,在您的方法中,如果它自己的代码是 JDCB 层的实现,则可以到处抛出 SQLExceptions。

您可以看到更多这些主题的其他来源是此列表。 http://www.jetbrains.com/idea/documentation/inspections.jsp

请参阅“抽象”下的项目,即。

  • 具体类的实例变量:当实例变量的类型声明为具体类而不是接口时报告。

经典的例子是:

 private ArrayList list;

什么时候会好

 private List list;
于 2009-02-24T04:11:26.657 回答
0

您可以考虑扩展您的示例以检查是否违反了Liskov Substitution Principle。有些人会说实现接口的类应该只抛出与接口定义的相同的异常。

如果您可以分析对象的用法,建议可以使用更通用的类型(例如,该方法需要 SQLConnection,而 IDBConnection 可以)。

于 2009-02-24T02:48:58.480 回答
0

又想到了一个。拥有一个处理非公共类型的公共成员。

class Foo { ... } // 本地包

公共课吧
{
    类汽车(){...}

    公共酒吧(Foo f){ ... }

    公共汽车之星(){ ... }
}

酒吧和明星会漏水。您可以看到 Bar 构造函数 b 但只能从与 Bar 类相同的包中调用它。可以调用星号方法,但只能将返回值作为对象使用。

对于这两种情况,您会看到您需要考虑将成员设为私有或包访问或将类型设为公共。

于 2009-02-24T20:05:58.020 回答