设计帮助!枚举工厂变压器中的java泛型!
我想知道我是否可以获得一些关于设计这个的好方法的意见。我将提出我的方法,但我认为有更好的解决方案(因此有问题:))。
我想创建一个枚举(以明确选项并避免单例架构),它具有用于从另一个对象创建一个对象的访问器。但这些对象的内容非常灵活。
将其视为限制此转换的选项数量的一种方法。
让我简单介绍一下层次结构。如果我要从一组不同的对象变成这样的东西:
class Base {...}
class ValueA extends Base {...}
class ValueB extends Base {...}
我正在考虑做这样的事情:
public enum ValueTransformer{
VALUE_A{
@Override
public <T> T createVo (Class<T> expectedRtn, Object obj) {
ValueA retObj = null;
if (expectedRtn == getReturnType ()) {
if (obj != null && CanBeTranslatedToA.class == obj.getClass ()) {
retObj = new ValueA ();
/*...*/
}
}
return retObj;
}
@Override
public Class<ValueA> getReturnType () { return ValueA.class; }
},
VALUE_B {
@Override
public Class<ValueB> getReturnType () { return ValueB.class; }
@Override
public <T> T createVo (Class<T> expectedRtn, Object obj) {
ValueB retObj = null;
if (expectedRtn == getReturnType ()) {
if (obj != null && CanBeTranslatedToB.class == obj.getClass ()) {
retObj = new ValueB ();
/*...*/
} else if (obj != null && AnotherClassForB.class = obj.getClass ()){
retObj = new ValueB();
/* ... */
}
}
return retObj;
}
};
public abstract <T> Class<T> getReturnType ();
public abstract <T> T createVo (Class<T> expectedRtn, Object obj);
}
这是一个不错的设计吗?这个枚举可能会增长,并且可以创建的 ValueA 和 ValueB 可能会改变(随着系统的增长)。在所有这些情况下我都可以返回“Base”,但这需要演员表和检查。我宁愿没有那个。
我是否需要有expectedRtn参数?我应该使用泛型吗?我对 Java 相当陌生,所以我并不总是确定处理这种情况的最佳方法。
感谢您的任何提示!
I am wondering if I could get some input on a good way to design this. I will put my approach but I think that there is a better solution (hence the question :) ).
I want to create an enum (to make clear the options and to avoid a singleton architecture) that has accessors for creating one object from another. But what those objects are is pretty flexible.
Think of it as a way to limit the number of options for this transformation.
Let me go into a little of the hierarchy. If I am going from a diverse set of objects to something like this:
class Base {...}
class ValueA extends Base {...}
class ValueB extends Base {...}
I was thinking of doing something like this:
public enum ValueTransformer{
VALUE_A{
@Override
public <T> T createVo (Class<T> expectedRtn, Object obj) {
ValueA retObj = null;
if (expectedRtn == getReturnType ()) {
if (obj != null && CanBeTranslatedToA.class == obj.getClass ()) {
retObj = new ValueA ();
/*...*/
}
}
return retObj;
}
@Override
public Class<ValueA> getReturnType () { return ValueA.class; }
},
VALUE_B {
@Override
public Class<ValueB> getReturnType () { return ValueB.class; }
@Override
public <T> T createVo (Class<T> expectedRtn, Object obj) {
ValueB retObj = null;
if (expectedRtn == getReturnType ()) {
if (obj != null && CanBeTranslatedToB.class == obj.getClass ()) {
retObj = new ValueB ();
/*...*/
} else if (obj != null && AnotherClassForB.class = obj.getClass ()){
retObj = new ValueB();
/* ... */
}
}
return retObj;
}
};
public abstract <T> Class<T> getReturnType ();
public abstract <T> T createVo (Class<T> expectedRtn, Object obj);
}
Is this a decent design? This enum will probably grow, and what ValueA and ValueB can be created from might change (as the sys grows). I could return a 'Base' in all those cases, but it would require a cast and a check. I'd prefer to not have that.
Is it necessary for me to have the expectedRtn parameter? Should I be using Generics at all? I am fairly new to Java so I am not always sure the best way to handle this case.
Thanks for any tips!!!!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
这不是一个很好的设计,我什至不知道这个枚举想要完成什么。首先,您使用每个枚举值实现的通用方法,这意味着该方法的调用者可以决定他们希望
T
成为什么类型...但这不是您想要的,因为这些方法实际上对它们将返回的对象类型有自己的看法。考虑到您的代码,上述内容是完全合法的,但您的代码实际上并未处理此问题。通用方法并不像您想象的那样做。
我觉得您真正想要的只是一种将特定类型的对象转换为
ValueA
或ValueB
类型的对象的简单方法。最简单的方法是让每个可以以这种方式转换的类提供一个在每个此类上执行此操作的方法:然后,如果您有一个
CanBeTranslatedToB
实例,而不是这样做:你只需这样做:
这更清晰,并且不像枚举版本那样容易出错。
如有必要,您可以执行各种操作来简化此操作,例如创建一个定义
toValueA()
和toValueB()
方法的接口,以及创建帮助程序类来提供任何常见的所有实现都需要使用的行为。我不认为像您所描述的那样有任何用处。编辑:
如果您无法更改需要转换为
ValueB
等的类的代码,您有多种选择。最简单(在我看来可能是最好的)处理方法是向ValueA
和ValueB
添加工厂方法,例如:那么你可以这样写:
如果你如果您不想在
ValueB
上使用这些方法,您可以将它们放在另一个类中,其方法名称类似于newValueB(CanBeTranslatedToB)
。最后,另一种选择是使用 Guava 并创建一个 函数。这是最接近您的原始设计的,但它是类型安全的,并且可以与 Guava 提供的所有接受函数的实用程序配合良好。您可以根据需要在类中收集这些
Function
实现。下面是一个单例实现从Foo
到ValueB
转换的示例:但是,我不会使用它作为执行此操作的唯一方法转换...如果您的应用程序需要转换整个对象集合,最好使用我上面提到的静态
valueOf
方法,并提供此类Function
只是为了方便一下子很多。This isn't a very good design and I really can't even tell what this enum is trying to accomplish. To start with, you're using generic methods that each enum value implements, which means the caller of the method gets to decide what type they want
T
to be... but that's not what you want, because the methods are in fact opinionated about what types of objects they'll return.The above is totally legal given your code, but your code does not actually handle this. Generic methods don't do what you seem to think they do.
I feel like what you actually want is just a simple way to transform objects of specific types to objects of type
ValueA
orValueB
. The simplest way to do this is just to have each class that can be transformed in this way provide a method that does that on each such class:Then, if you have an instance of
CanBeTranslatedToB
, rather than doing:you'd just do:
That's much clearer and not error-prone like the enum version.
If necessary, you can do various things to make this easier such as making an interfaces that define the
toValueA()
andtoValueB()
methods and making helper classes to provide any common behavior that all implementations need to use. I don't see any use for an enum like you describe.Edit:
If you can't change the code for the classes that need to be transformed to
ValueB
etc., you have several options. The simplest (and probably best, in my opinion) way to handle that would be to add factory methods toValueA
andValueB
such as:Then you can just write:
If you don't want those methods on
ValueB
, you could have them in another class with method names likenewValueB(CanBeTranslatedToB)
.Finally, another option would be to use Guava and create a Function for each conversion. This is the closest to your original design, but it is type safe and works well with all the
Function
-accepting utilities Guava provides. You could collect theseFunction
implementations in classes as you see fit. Here's an example of a singleton implementing a conversion fromFoo
toValueB
:However, I wouldn't use this as the only way to do the conversion... it would be better to have the static
valueOf
methods I mentioned above and provide suchFunction
s only as a convenience if your application needs to transform whole collections of objects at once a lot.关于泛型,Java 没有“真正的”泛型,在这种情况下这既有利又有害。当您在编译时不确切知道正在处理的对象类型时,使用泛型会很棘手。如果使用此信息的代码实际上知道它应该从对 ValueTransformer.ValueA.createVo 的调用中期望得到哪种类型的对象,那么它应该诚实地期望转换返回的值。我希望调用看起来更像这样:
如果我从此方法中得到错误的类型,我宁愿在这一行(问题真正发生的地方)看到一个 Cast 异常,而不是稍后看到空指针异常。这是正确的“快速失败”做法。
如果您真的不喜欢显式转换,我看到了一个很酷的技巧,可以让您隐式转换这些内容。我认为它是这样的:
但是,我并不真正推荐这种方法,因为它仍然在运行时执行强制转换,但没有人会通过查看您的使用代码来怀疑这一点。
我可以看到您可能希望实现的一些目标:
除非你有我没有想到的其他要求,否则工厂似乎会更好:
这满足了上述所有要求。此外,如果您知道生成 ValueA 对象需要什么类型的对象,则可以对输入值使用更明确的类型。
Regarding Generics, Java doesn't have "real" generics, which can be both beneficial and detrimental in this case. Using generics is tricky when you don't know at compile time exactly what type of object you're dealing with. If the code consuming this information actually knows which type of object it's supposed to expect from a call to ValueTransformer.ValueA.createVo, then it should honestly be expected to cast the returned value. I would expect the call to look more like this:
If I'm getting the wrong type out of this method, I would rather see a Cast exception on this line (where the problem really happened) than a null pointer exception later on. This is correct "fail-fast" practice.
If you really don't like the explicit casting, I've seen a cool trick that lets you cast these things implicitly. I think it goes something like this:
However, I don't really recommend this approach because it still performs the cast at runtime, but nobody would suspect that by looking at your usage code.
I can see a few goals that you may be hoping to achieve:
Unless you have other requirements I'm not thinking of, it seems like a factory would be preferable:
This satisfies all the requirements mentioned above. Furthermore, if you know what type of object is required to produce a ValueA object, you can use a more explicit type on the input value.
我花了一些时间,终于成功实现了基于枚举的工厂,看起来就像您正在寻找的那样。
这是我工厂的源代码:
这是我如何使用它。
我个人不太喜欢这个实现。 Enum 被定义为 Enum,因此不能在类级别进行参数化。这就是类参数(在我的示例中为 Thread 和 Socket)必须传递给工厂方法本身的原因。此外,工厂实现本身包含会产生警告的强制转换。但从另一方面来看,至少使用这个工厂的代码足够干净并且不会产生警告。
I spent some time and finally managed to implement enum based factory that looks like what you are looking for.
Here is the source code of my factory:
Here is how I use it.
Personally I do not like too much this implementation. Enum is defined as Enum, so it cannot be parametrized on class level. This is the reason that classes-parameters (Thread and Socket in my example) must be passed to factory method itself. Also the factory implementation itself contains casting that produces warning. But from other hand at least code that uses this factory is clean enough and does not produce warnings.