我想知道我是否可以通过一种很好的方式来设计这个。我会采用我的方法,但我认为有更好的解决方案(因此问题:))。
我想创建一个枚举(清除选项并避免使用单例体系结构),它具有从另一个对象创建一个对象的访问器。但那些对象是非常灵活的。
将其视为限制此转换选项数量的一种方法。
让我进入一点层次结构。如果我从各种各样的对象转到这样的东西:
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可能会发生变化(随着sys的增长)。在所有这些情况下我都可以返回一个“基地”,但它需要一个演员和一个支票。我宁愿没有那个。
我是否有必要使用expectedRtn参数?我应该使用泛型吗?我对Java很新,所以我并不总是确定处理这种情况的最佳方法。
感谢您的任何提示!!!!
答案 0 :(得分:2)
这不是一个非常好的设计,我甚至无法分辨这个枚举试图完成的内容。首先,您使用每个枚举值实现的通用方法,这意味着方法的调用者可以决定他们想要的类型T
......但这不是你想要什么,因为这些方法实际上是关于他们将返回什么类型的对象。
Class<String> foo = ValueTransformer.VALUE_B.getReturnType();
String string = ValueTransformer.VALUE_A.createVo(String.class, "");
鉴于您的代码,上述内容完全合法,但您的代码实际上并未处理此问题。通用方法不会像你认为的那样做。
我觉得你真正想要的只是将特定类型的对象转换为ValueA
或ValueB
类型的对象的简单方法。最简单的方法是让每个可以通过这种方式转换的类提供一个方法,在每个这样的类上执行该操作:
public class CanBeTranslatedToB {
...
public ValueB toValueB() {
ValueB result = new ValueB();
...
return result;
}
}
然后,如果你有一个CanBeTranslatedToB
的实例,而不是:
CanBeTranslatedToB foo = ...
ValueB b = ValueTransformer.VALUE_B.createVo(ValueB.class, foo);
你只是这样做:
CanBeTranslatedToB foo = ...
ValueB b = foo.toValueB();
这比enum版本更清晰,不易出错。
如果有必要,您可以执行各种操作以简化此操作,例如创建定义toValueA()
和toValueB()
方法的接口,并创建帮助程序类以提供所有实现需要使用的任何常见行为。我没有看到像你描述的枚举有任何用处。
修改强>
如果您无法更改需要转换为ValueB
等的类的代码,则可以选择多种方法。最简单的(在我看来可能是最好的)处理方法是将工厂方法添加到ValueA
和ValueB
,例如:
// "from" would be another good name
public static ValueB valueOf(CanBeTranslatedToB source) {
...
}
public static ValueB valueOf(AnotherClassForB source) {
...
}
然后你可以写:
CanBeTranslatedToB foo = ...
ValueB b = ValueB.valueOf(foo);
如果您不想在ValueB
上使用这些方法,则可以将它们放在另一个类中使用方法名称为newValueB(CanBeTranslatedToB)
的方法。
最后,另一种选择是使用Guava并为每次转化创建Function。这是最接近您的原始设计,但它是类型安全的,适用于所有Function
- 接受Guava提供的实用程序。您可以根据需要在类中收集这些Function
实现。以下是实现从Foo
到ValueB
的转换的单例的示例:
public static Function<Foo, ValueB> fooToValueB() {
return FooToValueB.INSTANCE;
}
private enum FooToValueB implements Function<Foo, ValueB> {
INSTANCE;
@Override public ValueB apply(Foo input) {
...
}
}
但是,我不会将此作为唯一方式进行转换...最好使用上面提到的静态valueOf
方法并提供此类{ {1}}只是为了方便您的应用程序需要一次性转换整个对象集合。
答案 1 :(得分:0)
关于泛型,Java没有“真正的”泛型,这在这种情况下既有益又有害。如果在编译时不确切知道正在处理的对象类型,那么使用泛型是很棘手的。如果消耗此信息的代码实际上知道它应该从对ValueTransformer.ValueA.createVo的调用中期望它的类型,那么应该诚实地期望它转换返回的值。我希望这个电话看起来更像这样:
MyTypeA myType = (MyTypeA)ValueTransformer.ValueA.createVo(sourceObject);
如果我从这个方法中得到错误的类型,我宁愿在这一行上看到一个Cast异常(问题确实发生了),而不是稍后的空指针异常。这是正确的“快速失败”练习。
如果真的不喜欢显式转换,我会看到一个很酷的技巧,让你可以隐式地投射这些东西。我认为它是这样的:
public abstract <T> T createVo (Object obj) {...}
MyTypeA myType = ValueTransformer.ValueA.createVo(sourceObject);
但是,我并不真的推荐这种方法,因为它仍然在运行时执行转换,但没有人会怀疑通过查看您的使用代码。
我可以看到你可能希望实现的一些目标:
除非你有其他要求我没想到,否则工厂似乎更可取:
public class ValueFactory
{
public ValueA getValueA(Object obj) {return new ValueA();}
public ValueB getValueB(Object obj) {return new ValueB();}
}
这满足了上述所有要求。此外,如果您知道生成ValueA对象所需的对象类型,则可以在输入值上使用更明确的类型。
答案 2 :(得分:0)
我花了一些时间,最终设法实现了基于enum的工厂,看起来像你在寻找什么。
以下是我工厂的源代码:
import java.net.Socket;
public enum EFactory {
THREAD(Thread.class) {
protected <T> T createObjectImpl(Class<T> type) {
return (T)new Thread();
}
},
SOCKET(Socket.class) {
protected <T> T createObjectImpl(Class<T> type) {
return (T)new Socket();
}
},
;
private Class<?> type;
EFactory(Class<?> type) {
this.type = type;
}
protected abstract <T> T createObjectImpl(Class<T> type);
public <T> T createObject(Class<T> type) {
return assertIfWrongType(type, createObjectImpl(type));
}
public <T> T assertIfWrongType(Class<T> type, T obj) {
if (!type.isAssignableFrom(obj.getClass())) {
throw new ClassCastException();
}
return obj;
}
}
以下是我如何使用它。
Thread t1 = EFactory.THREAD.createObject(Thread.class);
String s1 = EFactory.THREAD.createObject(String.class); // throws ClassCastException
就个人而言,我不太喜欢这个实现。枚举定义为枚举,因此无法在类级别进行参数化。这就是必须将类参数(我的示例中的Thread和Socket)传递给工厂方法本身的原因。工厂实现本身也包含产生警告的转换。但是从另一方面来说,至少使用这个工厂的代码足够干净并且不会产生警告。