gpt4 book ai didi

java - 枚举与类 : code duplication, 组合、扩展、泛型

转载 作者:太空宇宙 更新时间:2023-11-04 07:40:41 25 4
gpt4 key购买 nike

Note: I realize that this is very similar to the question eliminating duplicate Enum code, but I think it may be useful to discuss this separately since I'm also mentioning issues like extension (sublclassing) and generics. I apologize if this seems redundant.



我在前段时间编写的程序中实现了一些优化。当前的一个尝试消除由于缺乏对 Java 中的抽象枚举的支持而发生的代码重复(......以及次优设计,我承认这一点)。

这是当前问题的简化版本。
有接口(interface) IConfigResourceDescriptor它提供了将一些 XML 加载到某些类(实现 IResourceRoot)的必要信息:

public interface IConfigResourceDescriptor {
String getResourceName();
String getResourceLocation();
<T extends IResourceRoot> Class<T> getRootClass();
IConfigType getConfigType();
...
}

我有不同的配置集,可以由不同的应用程序或同一应用程序的不同部分定义,具体取决于那里的需要。
例如这里我展示了 2 组, ResourceDescriptorAResourceDescriptorB ,这样一个更面向业务逻辑( ResourceDescriptorA ),另一个面向用户界面( ResourceDescriptorB )。如您所见,它们的代码是相同的;它们分开的唯一原因是保持这两组配置相互独立是有意义的,但从它们的实现的角度来看,它们是完全相同的。

public enum ResourceDescriptorA implements IConfigResourceDescriptor {
MODEL("model.xml", "config/", Model.class, ConfigTypeA.MODEL),
RULES("rules.xml", "config/validation/", Rules.class, ConfigTypeA.RULES),
HELP("help.xml", "config/", Help.class, ConfigTypeA.HELP);

private String resourceName;
private String resourceLocation;
private Class<? extends IResourceRoot> rootClass;
private IConfigType configType;

private <T extends IResourceRoot> ResourceDescriptorA(String resourceName,
String resourceLocation, Class<T> rootClass, IConfigType configType) {
this.resourceName = resourceName;
this.resourceLocation = resourceLocation;
this.rootClass = rootClass;
this.configType = configType;
}

public String getResourceName() {
return resourceName;
}
public String getResourceLocation() {
return resourceLocation;
}
public <T extends IResourceRoot> Class<T> getRootClass() {
return rootClass;
}
public IConfigType getConfigType() {
return configType;
}
...
}

public enum ResourceDescriptorB implements IConfigResourceDescriptor {
DIALOGS("dialogs.xml", "config/", Dialogs.class, ConfigTypeB.DIALOGS),
FORMS("forms.xml", "config/", Forms.class, ConfigTypeB.FORMS),
MENUS("menus.xml", "config/", Menus.class, ConfigTypeB.MENUS);

private String resourceName;
private String resourceLocation;
private Class<? extends IResourceRoot> rootClass;
private IConfigType configType;

private <T extends IResourceRoot> ResourceDescriptorB(String resourceName,
String resourceLocation, Class<T> rootClass, IConfigType configType) {
this.resourceName = resourceName;
this.resourceLocation = resourceLocation;
this.rootClass = rootClass;
this.configType = configType;
}

public String getResourceName() {
return resourceName;
}
public String getResourceLocation() {
return resourceLocation;
}
public <T extends IResourceRoot> Class<T> getRootClass() {
return rootClass;
}
public IConfigType getConfigType() {
return configType;
}
...
}

解决方案

我的想法是将代码移动到辅助类并从枚举中引用它。
此外,为了避免有很多方法只是将调用包装到实际的 IConfigResourceDescriptor (助手类),我定义了一个新接口(interface) IConfigResourceDescriptorProvider它只返回 IConfigResourceDescriptor ,然后可用于获取配置的实际描述。
枚举现在实现 IConfigResourceDescriptorProvider而不是 IConfigResourceDescriptor .

包含实际实现的新助手类:

public class ConfigResourceDescriptor implements IConfigResourceDescriptor {

private String resourceName;
private String resourceLocation;
private Class<? extends IResourceRoot> rootClass;
private IConfigType configType;

public <T extends IResourceRoot> ConfigResourceDescriptor(String resourceName,
String resourceLocation, Class<T> rootClass, IConfigType configType) {
this.resourceName = resourceName;
this.resourceLocation = resourceLocation;
this.rootClass = rootClass;
this.configType = configType;
}

public String getResourceName() {
return resourceName;
}
public String getResourceLocation() {
return resourceLocation;
}
public <T extends IResourceRoot> Class<T> getRootClass() {
return rootClass;
}
public IConfigType getConfigType() {
return configType;
}
...
}

枚举实现的新接口(interface)。它只是返回实际的描述符:

public interface IConfigResourceDescriptorProvider {
IConfigResourceDescriptor getResourceDescriptor();
}

枚举被简化:构造函数创建一个 ConfigResourceDescriptor (帮助类)使用参数的值。 ConfigResourceDescriptor是实际的描述符。

public enum ResourceDescriptorProviderA implements IConfigResourceDescriptorProvider {
MODEL("model.xml", "config/", Model.class, ConfigTypeA.MODEL),
RULES("rules.xml", "config/validation/", Rules.class, ConfigTypeA.RULES),
HELP("help.xml", "config/", Help.class, ConfigTypeA.HELP);

private IConfigResourceDescriptor resourceDescriptor;

private <T extends IResourceRoot> ResourceDescriptorA(String resourceName,
String resourceLocation, Class<T> rootClass, IConfigType configType) {
resourceDescriptor = new ConfigResourceDescriptor(resourceName,
resourceLocation, rootClass, configType);
}

public IConfigResourceDescriptor getResourceDescriptor() {
return resourceDescriptor;
}
}

public enum ResourceDescriptorProviderB implements IConfigResourceDescriptorProvider {
DIALOGS("dialogs.xml", "config/", Dialogs.class, ConfigTypeB.DIALOGS),
FORMS("forms.xml", "config/", Forms.class, ConfigTypeB.FORMS),
MENUS("menus.xml", "config/", Menus.class, ConfigTypeB.MENUS);

private IConfigResourceDescriptor resourceDescriptor;

private <T extends IResourceRoot> ResourceDescriptorB(String resourceName,
String resourceLocation, Class<T> rootClass, IConfigType configType) {
resourceDescriptor = new ConfigResourceDescriptor(resourceName,
resourceLocation, rootClass, configType);
}

public IConfigResourceDescriptor getResourceDescriptor() {
return resourceDescriptor;
}
}
ConfigResourceDescriptor 的事实是一个类而不是枚举也意味着它可以扩展以提供额外的功能。也许那时我会有一个 ResourceDescriptorProviderC实例化 AdvancedConfigResourceDescriptor而不是 ConfigResourceDescriptor并且将能够提供更多功能:

public class AdvancedConfigResourceDescriptor extends ConfigResourceDescriptor {
// additional methods
}

现在枚举没有实现 IConfigResourceDescriptor 的事实而是 IConfigResourceDescriptorProvider意思是我以前做的地方...

ResourceDescriptorProviderA.MODEL.getResourceName();

...现在我必须做...

ResourceDescriptorProviderA.MODEL.getResourceDescriptor().getResourceName();

...但是资源描述符的使用在我的代码中非常集中,我只需要进行一些更改。我更喜欢这样,而不是必须在实现接口(interface)的所有枚举中定义所有包装器方法。
我没有任何外部契约(Contract),所以我不会通过改变这个来破坏任何客户。


问题是...

这可以吗,或者有什么我没有预料到可能会出现问题的地方吗?您在这种方法中看到任何大的(或小的,或中等的)禁忌吗?有更好的方法吗?

这是反复出现的问题,我永远不确定正确的方法是什么。
我试图寻找有关使用枚举的好的和坏的做法,但是(令人惊讶的是)我只发现了非常基本的东西或例子,这些东西太具体而对我有用。如果你能推荐我能读到的关于这个主题的好文章/博客/书籍,我也会很感激。
谢谢!

泛型

使用类而不是枚举的另一个优点是我可以使它(和接口(interface) IConfigResourceDescriptor )通用:

public interface IConfigResourceDescriptor<T extends IResourceRoot> {
String getResourceName();
String getResourceLocation();
Class<T> getRootClass();
IConfigType getConfigType();
...
}

public class ConfigResourceDescriptor<T extends IResourceRoot> implements IConfigResourceDescriptor<T> {

private String resourceName;
private String resourceLocation;
private Class<T> rootClass;
private IConfigType configType;

public ConfigResourceDescriptor(String resourceName,
String resourceLocation, Class<T> rootClass, IConfigType configType) {
this.resourceName = resourceName;
this.resourceLocation = resourceLocation;
this.rootClass = rootClass;
this.configType = configType;
}

public String getResourceName() {
return resourceName;
}
public String getResourceLocation() {
return resourceLocation;
}
public Class<T> getRootClass() {
return rootClass;
}
public IConfigType getConfigType() {
return configType;
}
...
}

最佳答案

我看不出你的方法有任何问题。您可能会遇到一个潜在的 future 问题,但应该很容易避免。

当您希望将功能添加到您的 enum 时,就会出现此问题。 s。然后,处理 enum 上的数据这一事实可能会有点令人不安。位于代理对象中,而功能位于 enum 中特别是如果功能取决于数据。

我喜欢你的方法,并打算自己研究它的用途。

同时,您可能还想尝试其他两种技术。

一个与您的相似,但 enum是一个对象的内部类,它继承自一个泛型父类(super class),其泛型类型包括 enum . Here是包含示例的帖子。这里的诀窍是传递 EnumSet枚举到父构造函数,因此它可以提供通用功能。

您可能对第二种技术不太感兴趣,因为它实际上涉及对 enum 进行代理组合。和另一个对象。 Here是最近发布的示例。

关于java - 枚举与类 : code duplication, 组合、扩展、泛型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16105383/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com