我正在考虑编写一个程序来检查 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。如果底层机制改为soemthing(比如说JPA是一个不同的数据库抽象库)那么异常也都需要改变。因此,底层数据库库正在泄露。
答案 0 :(得分: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,该怎么办?这是泄漏吗?这是否有资格泄露该值为整数?
所以我不确定这是否是机器可以轻易捕获的代码味道。
答案 1 :(得分:2)
所以
如何检测API是否泄漏实现细节或不保持相同的抽象级别。
您可能会看this talk。它解释了API的外观和设计(您可以从良好实践中减去哪些不良做法)
例如
功能应易于解释。 如果它的硬名称通常是一个糟糕的设计。
从中您可以发现,如果方法或返回参数给出详细说明,则它们与抽象级别不一致。
例如高级方法
-initProcess(): SGN_MTL
可能会泄漏返回值的实现细节。
这里的难点在于检测抽象级别何时发生变化。
例如,在你的方法中,如果它自己的代码是JDCB层的实现,那么可以全部抛出SQLExceptions。
此列表中您可以看到更多这些主题的其他来源。 http://www.jetbrains.com/idea/documentation/inspections.jsp
参见“抽象”下的项目即。
经典的例子是:
private ArrayList list;
什么时候会更好
private List list;
答案 2 :(得分:0)
您可以考虑扩展您的示例以检查Liskov Substitution Principle的违规行为。有些人会说实现接口的类应该只抛出接口定义的相同异常。
如果你可以分析对象的用法,最好建议使用更通用的类型(例如,当IDBConnection可以使用时,该方法需要SQLConnection)。
答案 3 :(得分:0)
另一个想法。拥有处理非公开类型的公共成员。
class Foo { ... } // local to the package public class Bar { class Car() { ... } public Bar(Foo f) { ... } public Car star() { ... } }酒吧和明星会漏水。您可以看到Bar构造函数b,但只能在与Bar类相同的包中调用它。您可以调用star方法,但只能将返回值用作Object。
对于这两种情况,您会看到您需要考虑将该成员设为私有或包访问或将该类型设为公共。