抽象迭代对象转换

abstracting iterative object conversions

我有一组对象,我希望在它们之间自由转换。让我们称它们为 A 到 F。

对象之间的关系可能看起来像这样

A -- B -- C -- F
     |         |
     D -- E ----

这意味着A可以转换为B但不能转换为C,如果你想将A转换为C那么你必须先将它转换为B,然后再从那里转换为C。

我在想出一个可扩展且灵活的实现方式时遇到了麻烦,该实现方式允许轻松添加新类型以及随之而来的转换。即使在梳理了所有我能找到的设计模式之后,我也离得出答案还差得很远。

最初,我有一个转换器 class 看起来像这样

public class Converter

public B convA2B(A in){...}
public A convB2A(B in){...}
public C convB2C(B in){...} etc

当我试图添加更多类型时,这被证明是笨拙的。接下来,我尝试让多个转换器对象都扩展相同的抽象 class

public abstract class Converter

final static int typeA = 0;
final static int typeB = 1;
final static int typeC = 2;
int type;

public abstract A toA(Object in);
public abstract B toB(Object in);
public abstract Object fromA(A in);
public abstract Object fromB(B in);
...

public Object convertTo(int type, Object data)
{
    switch(type){
        case 0: return new A().toA(data)
etc etc

本质上,每个转换器会将数据转换为路径中的下一个对象类型,然后再将该数据传递给下一个转换器。
即,如果我想从 A 转换为 C,A.toC(x) 将调用 B.toC(A.toB(x))。

这行不通,因为每个转换器类型都需要对所有类型之间的关系有一些基本的了解,以便知道下一次调用转换器的时间,这意味着添加新的转换器在某些地方变得非常困难,甚至可能处理不好会死循环

我该怎么办?我读到的许多设计模式似乎与我正在寻找的东西很接近,比如调解器、责任链、解释器,但我不确定如何调整它们来做我想做的事情。

此解决方案与您的第一个想法相同,但它通过将 Converter 视为可以组合的对象巧妙地避免了组合爆炸的笨拙。

interface Converter<A, B> 定义为从 AB 的转换器。这将最终与 Function<A, B> 相同,但我们为其附加了一些额外的语义,因此我们最好不要混淆它们:

@FunctionalInterface
interface Converter<A, B> {
    B convert(A a);
}

定义一堆Converter。对于每个class,为其直接邻居定义ConvertersConverter<A, B>,但没有Converter<A, C>)。

Converter<A, B> aToB = a -> { ... };
Converter<B, C> bToC = b -> { ... };
Converter<B, D> bToD = b -> { ... };
// etc.

现在,要从 AD,我们需要以某种方式组合 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 某个地方,因为那样您将得到一个难以管理的组合爆炸,一个对应于图表中的每条路径。相反,代码只是根据需要使用 afterthen 创建它们。添加新类型涉及为其在图中的直接邻居添加新的 Converters。

如果你不想用Converter,你可以用java.util.function.Function,也是一样的(但是convertapplyaftercomposethenandThen)。我编写新界面的原因是 Converter 在语义上与 Function 不同。 Converter 是您特定类型领域的一部分; a Function 是介于任意两种类型之间的函数。尽管它们具有相同的代码,但它们的含义不同(尽管您可以说 Converter<A, B> extends Function<A, B>)。

有趣的问题。这是我想出的:

A、B、C...将从中扩展的基础抽象模型class:

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 class(因为它有最多的连接)

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;
    }
}

你的转换器class:

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 ();
}