通过公共API导出非公共类型

Ale*_*nov 7 java api inheritance

如果我有很少的工厂方法返回非公共类型和配对方法集合给出这种非公共类型的变量怎么办?这会在NetBeans中带有标题警告消息.

结果公共API将只包含两组配对方法.原因是密封我的类型层次结构(如Scala中的封装类),并允许用户仅通过工厂方法实例化这些类型.所以我们在某种意义上得到了DSL.

例如,Schedule类由日历字段的约束表示.有一些类型的约束 - Range,Singleton,List,FullSet - 以NumberSet接口作为根.我们不希望公开这些类型以及Schedule如何与它们进行交互.我们只想要用户的规范.所以我们将NumberSet包私有化.在课程表中,我们为约束创建了一些工厂方法:

NumberSet singleton(int value);
NumberSet range(int form, int to);
NumberSet list(NumberSet ... components);
Run Code Online (Sandbox Code Playgroud)

以及创建Schedule对象的一些方法:

Schedule everyHour(NumberSet minutes);
Schedule everyDay(NumberSet minutes, NumberSet hours);
Run Code Online (Sandbox Code Playgroud)

用户只能以下列方式使用它们:

Schedule s = Schedule.everyDay( singleton(0), list(range(10-15), singleton(8)) );
Run Code Online (Sandbox Code Playgroud)

这不好主意吗?

Dir*_*irk 9

这个想法本身就是合理的.但是如果你将root类型(here NumberSet:)包装为private ,它将无法工作.要么公开该类型,要么使用公共接口.应该(并且可以)隐藏实际的实现类型.

public abstract class NumberSet {

    // Constructor is package private, so no new classes can be derived from
    // this guy outside of its package.
    NumberSet() {
    }
}

public class Factories {

    public NumberSet range(int start, int length) {
        return new RangeNumberSet(start, length);
    }

    // ...
}

class RangeNumberSet extends NumberSet {
   // ... must be defined in the same package as NumberSet
   // Is "invisible" to client code
}
Run Code Online (Sandbox Code Playgroud)

编辑从公共API公开隐藏/私有根类型是一个错误.请考虑以下情形:

package example;

class Bar {
    public void doSomething() {
            // ...
    }
}

public class Foo {

    public Bar newBar() {
        return new Bar();
    }
}
Run Code Online (Sandbox Code Playgroud)

并考虑使用此API的客户端应用程序.客户可以做什么?它无法正确声明变量具有类型,Bar因为该类型对包外的任何类都是不可见的example.它甚至无法调用从某个地方获取publicBar实例上的方法,因为它不知道这样的公共方法存在(它不能看到类,更不用说它暴露的任何成员).所以,客户可以在这里做的最好的事情是这样的:

Object bar = foo.newBar();
Run Code Online (Sandbox Code Playgroud)

这基本上没用.另一种方法是使用公共接口(或抽象类)而不是包私有接口,如上面定义的代码.在这种情况下,客户端实际上可以声明一个类型的变量NumberSet.它不能创建自己的实例或派生子类,因为构造函数对它是隐藏的,但它可以访问定义的公共API.

再次编辑即使您想要一个"无特征"值(从客户端的角度来看),即一个值,它没有定义客户端可能想要调用的任何有趣的API,在公开基类型中仍然是一个好主意.以上描述的方式.并且只是为了编译器能够执行类型检查并允许客户端代码将这样的值临时存储到(正确声明的)变量中.

如果您不希望您的客户在该类型上调用任何API方法:那没关系.没有什么可以阻止您不在您的(其他)公共基础类型上提供公共API.只是不要声明任何.使用"空"抽象基类(从客户端的角度来看是空的,因为所有有趣的方法都是包私有的,因此是隐藏的).但是,您必须提供公共基类型,或者您应该使用plain Object作为返回值,但是在编译时您将丢失错误检查.

经验法则:如果客户端必须调用某个方法来获取值并将其传递给其他API,那么客户端实际上知道,有一些神奇的特殊值.并且它必须能够以某种方式处理它("传递它",至少).除了(完全合适的)编译器警告之外,没有为客户端提供适当的类型来处理这些值并不会给你带来任何好处.

public abstract class Specification {

    Specification() {
        // Package private, thus not accessible to the client
        // No subclassing possible
    }

    Stuff getInternalValue1() {
        // Package private, thus not accessible to the client
        // Client cannot call this
    }
}
Run Code Online (Sandbox Code Playgroud)

就客户端代码而言,上述类是"空的"; 除了Object已经提供的东西之外,它不提供可用的API .拥有它的主要好处:客户端可以声明此类型的变量,并且编译器能够键入check.但是,您的框架仍然是唯一可以创建此类型的具体实例的地方,因此,您的框架可以完全控制所有值.