【问题标题】:Creating objects with a type determined at runtime创建具有在运行时确定的类型的对象
【发布时间】:2020-12-27 23:58:46
【问题描述】:

我正在编写一个程序,该程序需要从文件中保存和加载各种对象。这些文件保存为 XML,因此我决定为所有可以转换为 XML 的类创建一个名为“XMLConvertible”的接口。然而,当我读取文件时,我需要根据 XML 文件的内容创建不同类型的对象。

例如:

<Component></Component>
<Module></Module>

需要先创建一个Component类型的对象,然后再创建一个Module类型的对象。

到目前为止,我遇到的最简洁的方法是拥有一个工厂,其中包含可以从 XML 文件中读取的所有不同类的静态生成器列表。像这样(为简洁起见,省略了一些信息):

public interface XMLConvertible {
    public void write();
    public void read();
}

public abstract class Factory {
    private static Map<String, Builder> xmlConvertibles = new HashMap<>();

    public static void addXmlConvertible(String key, XMLConvertible value) {
        xmlConvertibles.put(key, value);
    }

    public static XMLConvertible getXmlConvertible(String key) {
        return xmlConvertibles.get(key);
    }
}

public class Component implements XMLConvertible {
    ...
}

public class ComponentBuilder extends Builder {
    static {
        Factory.addXmlConvertible("Component", new ComponentBuilder);
    }

    public Component build() {...}
}

public class Module implements XMLConvertible {
    ...
}

public class ModuleBuilder extends Builder {
    static {
        Factory.addXmlConvertible("Component", new ComponentBuilder);
    }

    public Module build() {...}
}

问题在于我必须为我添加的每个新 XMLConvertible 类创建 2 个类,一个实际类和一个构建器。我想有一种方法强迫自己添加一个构建器,这样我就不会忘记,或者最好找到一种更干净的方法来实现这样的东西。

抱歉,这篇文章太长了,我试着把它缩短一点。希望它仍然很容易理解问题所在。

【问题讨论】:

  • 这是个老问题了,被问过很多次了。但我现在找不到规范的答案。
  • 您正在实施序列化。看看this
  • 如果我错了,请纠正我,但据我所知,在阅读 XML 时,我仍然需要知道我正在反序列化的类。那么我不会遇到同样的问题吗?

标签: java inheritance design-patterns io


【解决方案1】:

您关心 XML 的格式吗?有像 xstream 这样的工具可以序列化为 XML,例如

import com.thoughtworks.xstream.XStream;
import java.util.ArrayList;
import java.util.List;

public class Example {

    public static void main(String[] args) {
        XStream xStream = new XStream();

        List<Object> things = new ArrayList<>();
        things.add(new Component("foo"));
        things.add(new Module("bar"));

        String xml = xStream.toXML(things);
        List<Object> things2 = (List<Object>) xStream.fromXML(xml);
    }

    public static class Component {
        private final String componentField;

        public Component(String componentField) {this.componentField = componentField;}
    }

    public static class Module {
        private final String moduleField;

        public Module(String moduleField) {this.moduleField = moduleField;}
    }
}

在你的 pom 中你需要:

        <dependency>
            <groupId>com.thoughtworks.xstream</groupId>
            <artifactId>xstream</artifactId>
            <version>1.4.15</version>
        </dependency>

这会产生:

<list>
  <Example_-Component>
    <componentField>foo</componentField>
  </Example_-Component>
  <Example_-Module>
    <moduleField>bar</moduleField>
  </Example_-Module>
</list>

您不需要编写任何特殊的构建器,但是您的 XML 文件的结构与您的类的实现相同,因此如果需要任何跨版本兼容性,更改可能会很困难。

【讨论】:

  • 跨版本兼容性非常重要,但如果我找不到其他答案,我想这是一个开始的地方。谢谢您的帮助。我也更愿意自己从头开始编写解决方案,因为我的程序就像一个练习,可以让我在编程方面变得更好,因为它是一个我将实际使用的系统。
【解决方案2】:

我最终向 XMLConvertible 接口添加了一个抽象 Builder,它将在 XMLConvertible 的所有实现中被覆盖。至少这样我不必为每个新实现创建 2 个新文件。

public interface XMLConvertible {
    public void write();
    public void read();

    public abstract static class Builder {
        public abstract XMLConvertible build([XML data]);
    }
}

【讨论】:

    猜你喜欢
    • 2010-11-02
    • 2012-10-26
    • 2015-07-17
    • 1970-01-01
    • 2010-11-16
    • 1970-01-01
    • 1970-01-01
    • 2011-02-01
    相关资源
    最近更新 更多