我有一组对象,我希望在它们之间自由转换。我们称他们为A至F.
对象之间的关系可能看起来像这样
ctrl .req r0
push {...}
vmrs ctrl, fpexc // Check vfp status
tst ctrl, #(1 << 30) // fpexc.en
bne .L.undefinedHandler.die // if vfp enabled -> there is another reason for this exception
// enable VFP and try again
ldr ctrl, =fpexc.en.mask // enable vfp
vmsr fpexc, ctrl
// Reloading vfp state & d0-d31 regs
// some code is skipped here
pop {...}
subs pc, lr, #4 // return & try faulty instructions again
.L.undefinedHandler.die:
// F... really unknown instruction
这意味着A可以转换为B但不能转换为C,如果要将A转换为C,则必须首先将其转换为B,然后再转换为C.
我无法想出一个可扩展且灵活的实现方法,可以轻松添加新类型以及与之相关的转换。即使经过各种设计模式的梳理,我也能发现我没有接近提出答案。
最初,我有一个看起来像这样的
的Converter类A -- B -- C -- F
| |
D -- E ----
当我尝试添加更多类型时,这被证明是笨拙的。接下来,我尝试让多个转换器对象都扩展相同的抽象类
public class Converter
public B convA2B(A in){...}
public A convB2A(B in){...}
public C convB2C(B in){...} etc
基本上会发生什么情况是每个转换器在将数据传递给下一个转换器之前将数据转换为路径中的下一个对象类型。
即,如果我想从A转换为C,则A.toC(x)将调用B.toC(A.toB(x))。
这不起作用,因为每个转换器类型需要对所有类型之间的关系有一些基本的了解,以便知道转换器何时调用下一个,这意味着添加新的转换器变得非常困难,甚至可能导致如果处理不当,无限循环。
我该怎么办?我读到的许多设计模式似乎都接近我正在寻找的东西,比如调解员,责任链,翻译,但我不确定如何调整它们来做我想做的事。
答案 0 :(得分:1)
有趣的问题。这就是我想出的:
A,B,C ......将扩展的基本抽象Model类:
public abstract class Model {
protected Set<Class<? extends Model>> targets;
public abstract Set<Class<? extends Model>> targets ();
public abstract <T extends Model> T convert (Class<T> target);
}
例如B类(因为它具有最多的连接)
public class B extends Model {
public B () {
}
@Override
public Set<Class<? extends Model>> targets () {
if (targets == null) {
targets = new HashSet<> ();
targets.add (A.class);
targets.add (C.class);
targets.add (D.class);
}
return targets;
}
@SuppressWarnings ("unchecked")
@Override
public <T extends Model> T convert (Class<T> target) {
if (target == A.class) {
return (T)toA ();
}
if (target == C.class) {
return (T)toC ();
}
return (T)toD ();
}
private A toA () {
A a = new A ();
// your conversion code
return a;
}
private C toC () {
C c = new C ();
// your conversion code
return c;
}
private D toD () {
D d = new D ();
// your conversion code
return d;
}
}
你的转换器类:
public class Converter {
public Converter () {
}
public <S extends Model, T extends Model> T run (S source, Class<T> target) throws Exception {
if (!source.targets ().contains (target)) {
throw new Exception ("Inconvertible types.");
}
return source.convert (target);
}
}
最后,在你的代码中,你要做的是:
B b = new B ();
Converter converter = new Converter ();
try {
C c = converter.run (b, C.class);
F f = converter.run (c, F.class);
E e = converter.run (f, E.class);
} catch (Exception e) {
e.printStackTrace ();
}
答案 1 :(得分:0)
这个解决方案与你的第一个想法是一回事,但它通过将Converter
视为可以组合的对象,巧妙地避开了组合爆炸的笨拙。
将interface Converter<A, B>
定义为从A
到B
的转换器。这最终会与Function<A, B>
相同,但我们会为它添加一些额外的语义,所以我们不要混淆它们:
@FunctionalInterface
interface Converter<A, B> {
B convert(A a);
}
定义一堆Converter
s。对于每个类,仅为其直接邻居定义Converters
(Converter<A, B>
,但不定义Converter<A, C>
)。
Converter<A, B> aToB = a -> { ... };
Converter<B, C> bToC = b -> { ... };
Converter<B, D> bToD = b -> { ... };
// etc.
现在,要从A
转到D
,我们需要以某种方式合并Converter
。这可以通过添加到Converter
:
@FunctionalInterface
interface Converter<A, B> {
B convert(A a);
default <C> Converter<C, B> after(Converter<? super C, ? extends A> pre) {
return c -> this.convert(pre.convert(c));
}
default <C> Converter<A, C> then(Converter<? super B, ? extends C> post) {
return a -> post.convert(this.convert(c));
}
}
现在,你可以写
// For a mathematician
Converter<A, C> aToC = bToC.after(aToB);
// If you aren't a mathematician
Converter<A, C> aToC = aToB.then(bToC);
你不应该将这些作品存放在static
的某个地方,因为这样你就会得到一个难以管理的组合爆炸,一个用于图表中的每个路径。相反,代码只是根据需要使用after
和then
创建它们。添加新类型涉及在图中为其直接邻居添加新的Converter
。
如果您不想使用Converter
,可以使用java.util.function.Function
,这是相同的事情(但convert
是apply
,{{1 } {是after
,compose
是then
)。我编写新界面的原因是andThen
在语义上与Converter
不同。 Function
是您特定类型范围的一部分; Converter
是任意两种类型之间的函数。虽然它们具有相同的代码,但它们意味着不同的东西(你可以说Function
虽然)。