1

我知道不可能在 Java 中扩展枚举,但我正在尝试为以下问题找到一个优雅的解决方案

我正在尝试对包含跨地区各种 Web 服务的 http 端点的枚举(或类)进行建模,比如我有服务 A 和 B,每个在美国、欧盟、日本或 CN 都有 4 个特定于地区的端点。(这基本上是针对我正在编写的一些单独的调试代码,在生产中,端点将从配置中选择)

我希望做这样的事情(不兼容的java代码)。

public enum IEndPoint {
    NA_END_POINT,
    EU_END_POINT,
    JP_END_POINT,
    CN_END_POINT,
}

public enum ServiceAEndPoint extends IEndPoint {
   NA_END_POINT("http://A.com/");
   EU_END_POINT("http://A-eu.com/");
   JP_END_POINT("http://A-jp.com/");
   CN_END_POINT("http://A-cn.com/");
}

我可以使用接口来做到这一点,其中我为每个区域都有一个方法,但在我看来,枚举方式更具表现力,有没有更好的方法可以对此进行建模?我正在寻找的是是否有更好的方法来模拟继承关系并且还具有枚举的表达能力。

ServiceAEndPoint.NA_END_POINT

对比

serviceAEndPoint.getNAEndPoint()
4

6 回答 6

3

我假设您还需要一个ServiceBEndPoint枚举(和类似的)。在这种情况下,我认为您的模型没有那么大的意义。

IEndPoint实际上是对可能运行服务的环境/区域类型的枚举。它不是服务本身的枚举。每个单独的服务(A、B 或其他)对于每个区域都有不同的地址。

因此我会坚持只使用IEndPoint枚举,然后在一些特定于服务的代码中有一个查找映射,它将为您提供给定端点的地址。像这样的东西:

public enum IEndPoint {
    NA_END_POINT,
    EU_END_POINT,
    JP_END_POINT,
    CN_END_POINT,
}

public class ServiceABroker {
   private static final Map<IEndPoint, String> addressesByEndPoint;
   static {
      addressesByEndPoint = new EnumMap<>();
      addressesByEndPoint.put(NA_END_POINT, "http://A.com/");
      addressesByEndPoint.put(EU_END_POINT, "http://A-eu.com/");
      addressesByEndPoint.put(JP_END_POINT, "http://A-jp.com/");
      addressesByEndPoint.put(CN_END_POINT, "http://A-cn.com/");
   }

   public String getAddressForEndPoint(IEndPoint ep) {
       return addressesByEndPoint.get(ep);
   }
}
于 2013-10-30T14:55:43.323 回答
1

如果这些是静态最终常量,那么只需将它们放在接口中即可。将接口命名为IServiceAEndPointKeys,其中部分是约定。

这是我认为enum更合适和有用的地方:

  • 示例 1:文件类型。包含jpgpdf等的枚举。
  • 示例 2:列定义。如果我有一个包含 3 列的表,我会编写一个枚举声明IDNameDescription(例如),每个都有列标题 name列宽列 ID等参数。
于 2013-10-30T14:54:57.813 回答
1

您可能需要考虑这样的事情:

public abstract class EndpointFactory {
    public abstract String getNAEndPoint();
    public abstract String getEUEndPoint();
}

public class ServiceAEndpointFactory extends EndpointFactory {
    public static final String NA_END_POINT = "http://A.com/";
    public static final String EU_END_POINT = "http://A-eu.com/";

    public String getNAEndPoint() {
       return ServiceAEndpointFactory.NA_END_POINT;
    }

    public String getEUEndPoint() {
       return ServiceAEndpointFactory.EU_END_POINT;
    }
}

public class ServiceBEndpointFactory extends EndpointFactory {
    public static final String NA_END_POINT = "http://B.com/";
    public static final String EU_END_POINT = "http://B-eu.com/";

    public String getNAEndPoint() {
       return ServiceAEndpointFactory.NA_END_POINT;
    }

    public String getEUEndPoint() {
       return ServiceAEndpointFactory.EU_END_POINT;
    }
}

然后你可以像这样直接引用你的字符串:

ServiceAEndpointFactory.NA_END_POINT;

或者,如果在执行之前不知道服务类型,则可以使用基础对象:

EndpointFactory ef1 = new ServiceAEndpointFactory();
String ep = ef1.getNAEndPoint();

这样做的缺点是在每个子类中重新定义了 get*Endpoint() 函数。您可以通过将静态最终变量移动到基类中的非静态变量并将 getter/setter 仅一次放入基类中来消除这种情况。但是,这样做的缺点是您无法在不实例化对象的情况下引用这些值(这基本上模拟了我认为 ENUM 有价值的东西)。

于 2013-10-30T14:55:57.080 回答
1

我不确定我是否理解您的问题,但您可以向枚举添加方法,例如您可以执行以下操作:

public enum ServiceAEndPoint{
    NA_END_POINT("http://A.com/");
    EU_END_POINT("http://A-eu.com/");
    JP_END_POINT("http://A-jp.com/");
    CN_END_POINT("http://A-cn.com/");

    private final String url;

    private EndPoint(String url){
        this.url=url;
    }

    public String getURL(){
        return url;
    }
}
于 2013-10-30T14:56:47.983 回答
1

枚举不能以这种方式扩展,主要是因为枚举不能被子类化,或者它们必须遵守的约束将无法强加。

而是利用接口,像这样

public interface IEndPoint;

public enum DefaultEndPoints implements IEndPoint {
    NA_END_POINT,
    EU_END_POINT,
    JP_END_POINT,
    CN_END_POINT,
}

public enum DefaultServiceEndPoints implements IEndPoint {
   NA_END_POINT("http://A.com/");
   EU_END_POINT("http://A-eu.com/");
   JP_END_POINT("http://A-jp.com/");
   CN_END_POINT("http://A-cn.com/");
}

public void doSomething(IEndPoint endpoint) {
  ...
}

不能以您希望的方式进行子类化的原因与枚举将通过.equals(object)和 via相等的合同有关==。如果你可以子类化,这有意义吗?

if ( (DefaultEndPoints)JP_END_POINT == (DefaultServiceEndPoints)JP_END_POINT) {

}

如果您说“是”,那么我希望能够做到这一点

DefaultEndPoint someEndpoint = DefaultServiceEndPoints.JP_END_POINT;

这将为错误打开一扇门,因为不能保证一个枚举声明中的枚举条目在另一个枚举声明中。

会不会不一样?也许吧,但事实并非如此,而且改变它肯定会引入许多必须彻底考虑的复杂性(或者它会为解决 Java 的强静态类型检查开辟道路)。

于 2013-10-30T15:05:09.830 回答
0

像这样的图案对你有何吸引力?我让enum实现一个interface并在一个调试集和一个发布集中实现接口。然后发布集可以从名称中派生出属性enum名称——这很简洁。

public interface HasURL {
  public String getURL();
}

public enum DebugEndPoints implements HasURL {
  NA,
  EU,
  JP,
  CN;

  @Override
  public String getURL() {
    // Force debug to go to the same one always.
    return "http://Debug.com/";
  }
}

public enum NormalEndPoints implements HasURL {
  NA,
  EU,
  JP,
  CN;
  final String url;

  NormalEndPoints () {
    // Grab the configured property connected to my name.
    this.url = getProperty(this.name());
  }
  @Override
  public String getURL() {
    return url;
  }
}
于 2013-10-30T15:06:44.677 回答