防止依赖Java / Kotlin中的类,但允许扩展

时间:2019-06-08 05:26:51

标签: java android oop design-patterns kotlin

我希望创建一个有用的基本Java类,其中包含一些受保护的方法和钩子,以便可以轻松实现子类。

但是,我希望此类仅可用于派生子类,但不能作为依赖项使用。 原因是为了防止一些初级/粗心的开发人员将其代码耦合到该基类。

例如,如果我的基类名为BaseActivity.java,则任何人都可以创建自己的

public class MyNewActivity extends BaseActivity

但是没有人可以使用字段或方法签名直接引用BaseActivity,例如,不应允许这样做:

public void doSomethingOnBaseActivity(BaseActivity activity);

private BaseActivity someField;

public BaseActivity getActivity();

在Java中有什么方法可以实现这种限制吗? 也许在科特林有可能吗?

编辑: 这不是Kotlin: Can an abstract super class have an abstract constructor?的副本。 我希望防止对基类的依赖,而不仅仅是实例化。 “摘要”在这里无济于事。

2 个答案:

答案 0 :(得分:1)

首先,让我们解决为什么为什么在寻找这个? 您正在寻找一种方法来防止使用者在基类上调用不需要的方法。

如果您认为自己在寻找其他东西,请再考虑一下。如果您只是想隐藏它,请三思。最终用户根本不会在乎实施细节。


如果您创建了基类,那么首先不要发布允许此操作的API Clean Code中专门有一个章节。

如果您的基类扩展了另一个基类,则您会遇到麻烦。 如果要扩展而不是封装,则无法隐藏已经发布的API

  

我希望此类仅可用于派生子类,但不能作为依赖项使用。有什么办法可以在Java中完成这种限制?也许在科特林有可能吗?

不。这不是一个意见,这是设计使然。


可能有一种复杂的方法可以隐藏父类的方法,但不能在消费者与之交互的类上(扩展)。

您可能在其自己的Gradle模块中有几层基类,并且分别设置了implementation类型依赖性,但是如果您可以扩展该类(如果可以看到它,请对其进行引用),也可以在任何地方使用

想象一下:

  • 消费者模块-> ConsumerActivity扩展了ExtensibleActivity
  • 您的库模块-> ExtensibleActivity扩展了BaseActivity
  • 您的基础库模块-> BaseActivity扩展了Activity
  • Android SDK-> Activity

Consumer模块只能看到“您的库模块”内部的内容。它知道ExtensibleActivity,但看不到其任何超级类型。使用者仍然可以引用ExtensibleActivity及其方法。副作用是因为从消费者的角度来看超类是未知的,您不能ExtensibleActivity的实例作为Activity传递,因为类型系统没有知道它扩展了Activity,因为它看不到BaseActivity中介类型。这是消费者看到的图形:

ConsumerActivity -> ExtensibleActivity -> BaseActivity (doesn't exist) -> ??? (don't know)

这时,您只需要问自己“这应该首先扩展Activity吗?”。

这太糟糕了。对于您不需要担心的事情,浪费了很多精力。


如果您想隐藏某些内容,请使用在继承上优先使用。将您的逻辑放入Fragment中,或者更好地,将您的逻辑放入自定义生命周期感知组件中。这样,您就可以完全控制API 。做到这一点,这样您就不必担心从何处调用它。

为您的代码和使用手册好的文档

如果我选择使用不当,请允许我破坏您该死的图书馆。

您的API中有多种方法吗?大!没有人会阻止我打电话给他们。您可以在手册中写出应该如何使用它,但是最终,我正在使用您的库编写程序,如果我做错了,那是我的错。很好。

答案 1 :(得分:1)

否,这是不可能的。这个答案对所有类型都是正确的,无论抽象与否,接口或类。当您处于某个类的范围内(例如同一包),并且该类未密封时,则该范围内的每个人都可以继承它。只要您在范围内,就可以引用此类型。这就是访问修饰符的重点。允许类型的扩展但不引用它是没有意义的。这与概念相矛盾。你为什么想这么做?无论如何,您都不能删除该基类,因为这会破坏所有继承程序的代码。允许扩展但不允许引用是没有意义的。这是什么原因。也许有另一种方式可以实现您的目标。有人从类型继承的那一刻就创建了依赖关系。这种依赖关系称为继承。子类型是超类型。您无法在编译器中隐藏这一事实。

如果您想省略依赖项但重用代码或提供代码模板,则不要使用类型。您可以使用文件模板或代码生成器来生成可重复使用的代码(如代码片段)。