接口上静态方法的替代方法,用于强制一致性

2022-09-01 14:47:18

在Java中,我希望能够定义标记接口,强制实现提供静态方法。例如,对于简单的文本序列化/反序列化,我希望能够定义一个看起来像这样的接口:

public interface TextTransformable<T>{

  public static T fromText(String text);

  public String toText();

由于Java中的接口不能包含静态方法(如许多其他帖子/线程中所述:这里这里这里这个代码不起作用。

然而,我正在寻找的是一些合理的范式来表达相同的意图,即对称方法,其中一个是静态的,并由编译器强制执行。现在我们能想到的最好的是某种静态工厂对象或通用工厂,这两者都不是真正令人满意的。

注意:在我们的例子中,我们的主要用例是,我们有很多很多的“值对象”类型 - 枚举或其他具有有限数量的值的对象,通常不会超过其值,并且我们每秒解析/反解析数千次,所以实际上确实关心重用实例(如浮点数,整数等)及其对内存消耗/ g.c的影响。

有什么想法吗?

编辑1:为了澄清一些混淆 - 我们有很多不同的对象适合这种模式 - 实际上我们正试图为具有2个语义的调用者想出一些优雅的东西:

  • 接口作为契约 - 访问的统一性(例如,文本可转换作为一种功能)
  • 要求子类/实现实现(例如,强制它们实现自己的转换

就我们对Flyweight,Factories的想法而言 - 它们都是我们考虑过的选项,实际上我们正试图看看我们是否可以找到更优雅的东西,而不是依靠JavaDoc说“实现一个工厂并将调用委托给它,或者按照惯例在XXX位置公开它”


答案 1

这真的很适合Flyweight。这基本上就是你试图通过静态来完成的。关于如何为Flyweight对象提供服务,以便您不会创建数千个,这里有一些想法。

一个是工厂,你说你考虑过并拒绝了,尽管你没有说明原因(所以任何其他想法都可能遇到同样的问题),所以我不会深入讨论它。

另一种方法是让值类型具有可以为其转换器提供服务的方法。像这样:

 public class ValueType {
       public static final TextTransformable<ValueType> CONVERT = ....
 }

然后像这样使用它:

 ValueType value = ValueType.CONVERT.fromText(text);

 String text = ValueType.CONVERT.toText(value);

现在,这并不意味着所有ValueType都通过相同的机制提供其转换器,因为我认为您需要某种工厂。

编辑:我想我不知道你对工厂有什么不雅之处,但我认为你专注于来电者,所以这对你来说感觉如何:

  ValueType value = getTransformer(ValueType.class).fromText(text);

上述操作可以通过工厂的静态导入和具有类似如下签名的方法来完成:

   public static <T> TextTransformable<T> getTransformer(Class<T> type) {
         ...
   }

找到合适的转换器的代码不一定是最漂亮的,但从调用者的角度来看,一切都很好。

编辑2:进一步思考这个问题,我看到的是你想控制对象构造。你真的不能那样做。换句话说,在Java中,你不能强迫实现者使用或不使用工厂来创建他们的对象。它们始终可以公开公共构造函数。我认为你的问题是你对执行建设的机制不满意。如果这种理解是正确的,那么以下模式可能是有用的。

创建一个仅包含私有构造函数的对象,该构造函数包装您的值类型。该对象可能具有泛型类型参数,以了解它包装的值类型。此对象使用静态工厂方法实例化,该方法采用工厂接口来创建“真实”值对象。所有使用该对象的框架代码仅将此对象作为参数。它不直接接受值类型,并且如果没有值类型的工厂,则无法实例化该对象。

这种方法的问题在于它非常有限。只有一种方法可以创建对象(工厂接口支持的方法),并且使用值对象的能力有限,因为处理这些文本元素的代码仅通过此对象具有有限的交互。

我猜他们说没有一个软件问题不能通过额外的间接层来解决,但这可能是一个太远的桥梁。至少它值得深思。


答案 2

只是一个需要考虑的想法:您可以将转换逻辑与对象本身分开,然后您有一组固定的转换器,实现以下接口:

public interface Transformer<T>{ 

  public T fromText(String text); 

  public String toText(T obj); 
}

您的实际数据类可以有一个方法 getTransformer() 为它们返回正确的转换器。