通过公共 API 导出非公共类型 [英] Exporting non-public type through public API

查看:37
本文介绍了通过公共 API 导出非公共类型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如果我几乎没有返回非公共类型的工厂方法和提供此非公共类型变量的成对方法集怎么办?这会导致 NetBeans 中出现标题为警告消息.

What if I have few factory methods returning non-public type and pairing set of methods which gives variables of this non-public type? This results with titled warning message in NetBeans.

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

In result public API will contain only two pairing sets of methods. The reason is to make my type hierarchy sealed (like seald classes in Scala) and allow users only instantiate these types through factory methods. So we get DSL in some sense.

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

For example, Schedule class represented by calendar fields' contraints. There are some types of contraints - Range, Singleton, List, FullSet - with NumberSet interface as a root. We don't want to expose these types and how Schedule interact with them. We just want the specification from the user. So we make NumberSet package-private. In class Schedule we create few factory-methods for constraints:

NumberSet singleton(int value);
NumberSet range(int form, int to);
NumberSet list(NumberSet ... components);

和一些创建Schedule对象的方法:

and some methods for creating Schedule object:

Schedule everyHour(NumberSet minutes);
Schedule everyDay(NumberSet minutes, NumberSet hours);

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

User can only use them in the manner:

Schedule s = Schedule.everyDay( singleton(0), list(range(10-15), singleton(8)) );

这是个坏主意吗?

推荐答案

这个想法本身很合理.但是,如果您将 root 类型(此处为:NumberSet)包设为私有,它将不起作用.要么公开该类型,要么改用公共接口.应该(并且可以)隐藏实际的实现类型.

The idea itself is sound. But it won't work, if you make the root type (here: NumberSet) package private. Either expose that type or use a public interface instead. The actual implementation types should (and can) be hidden.

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
}

编辑 从公共 API 公开隐藏/私有根类型是错误的.考虑以下场景:

Edit To expose a hidden/private root type from a public API is a mistake. Consider the following scenario:

package example;

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

public class Foo {

    public Bar newBar() {
        return new Bar();
    }
}

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

and consider a client application using this API. What can the client do? It cannot properly declare a variable to have type Bar since that type is invisible to any class outside of the package example. It cannot even call public methods on a Bar instance it got from somewhere, since it does not know, that such a public method exists (it cannot see the class, let alone any members it exposes). So, the best a client can do here is something like:

Object bar = foo.newBar();

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

which is essentially useless. A different thing would be to have a public interface (or abstract class) instead of the package private one, as in the code defined above. In this case, the client actually can declare a variable of type NumberSet. It cannot create its own instances or derive subclasses, since the constructor is hidden from it, but it can access the public API defined.

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

Edit again Even if you want a "featureless" value (from the client's perspective), i.e., a value, which does not define any interesting APIs the client may want to call, it is still a good idea to expose a public base type in the way described above. And be it only for the purpose of the compiler being able to perform type checking and allowing client code to temporarily store such a value into a (properly declared) variable.

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

If you don't want your client to call any API methods on the type: that's fine. There is nothing preventing you from not providing a public API on your (otherwise) public base type. Just don't declare any. Use an "empty" abstract base class (empty from the perspective of the client, since all interesting methods would be package private and thus hidden). But you have to supply a public base type nonetheless, or you should use plain Object as return value, but then you forfeit error checking at compile time.

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

Rule of thumb: If the client has to call some method in order to obtain a value and pass it on to some other API, then the client actually knows, that there are some magic special values. And it has to be able to handle it in some way ("pass it on", at very least). Not providing a proper type for the client to deal with the values does not buy you anything except for (entirely appropriate) compiler warnings.

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
    }
}

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

The above class is "empty" as far as the client code is concerned; it does not offer an usable API except stuff, which the Object already offers. The major benefit of having it: the client can declare variables of this type, and the compiler is able to type check. Your framework, though, remains the only place, where concrete instances of this type can be created, and thus, your framework has total control over all values.

这篇关于通过公共 API 导出非公共类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆