猿问

有没有比使用反射在继承层次结构中通过工厂方法实例化元素更好的方法?

我构建了一个继承层次结构,其中一堆具体类继承自抽象超类A。A具有一个强制属性 Stringa和一个可选的 Mapb和 xml 模型规范的模型元素。和a中可能的键值对b都是 jaxp.NamedNodeList 的一部分。a因此,要设置和 的值,b我总是需要遍历列表并检查当前属性是否具有名称“id”,并分别设置 的值a或将键值对添加到b. 显然有人想将其外包给工厂方法等。但是,在抽象超类中实现静态工厂方法A显然是不够的,因为通过返回 A 的新实例,我需要在使用工厂方法创建它时将实例化向下转换为具体元素。所以我想出了一个使用反射的解决方案,但我真的很不安全,因为没有更简单的方法来解决一个看起来如此普遍的问题。


有没有更简单的解决方案?


这是我的工厂模式,ClassCastException当将 A 向下转换为 B 时,会产生这样的结果SubclassB b = (SubclassB) AbstractSuperClassA.createWith(attributes);:


public static AbstractSuperClassA createWith(NamedNodeMap attributes) {

    Map<String, String> attributeMap = new HashMap<>();

    String a= null;

    for (int i = 0; i < attributes.getLength(); i++) {

        if (attributes.item(i).getNodeName().equals("id")) {

            a = attributes.item(i).getNodeValue();

        }

        attributeMap.put(attributes.item(i).getNodeName(), attributes.item(i).getNodeValue());

    }

    if (a == null) {

        // throw RuntimeException

    }

    return new AbstractSuperClassA (identifier, attributeMap);

}

这是通用的反射实现:


public static <T extends AbstractSuperClassA > T init(NamedNodeMap attributes, Class<T> clazz) {

    Map<String, String> attributeMap = new HashMap<>();

    String a= null;

    for (int i = 0; i < attributes.getLength(); i++) {

        if (attributes.item(i).getNodeName().equals("id")) {

            a = attributes.item(i).getNodeValue();

        }

        attributeMap.put(attributes.item(i).getNodeName(), attributes.item(i).getNodeValue());

    }

    if (a== null) {

        // throw RuntimeException

    }

    try {

        Constructor<T> constructor = clazz.getConstructor(String.class);

        T newElement = constructor.newInstance(a);

        newElement.setAttributes(attributeMap);

        return newElement;

    } catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) {

        log.error(e.getMessage(), e);

    }

    return null;

}


一只名叫tom的猫
浏览 81回答 1
1回答

蓝山帝景

您的init方法似乎需要一种方法来基于单个值创建给定类的实例。String在这种情况下,您不需要反思。无需传入Class实例化和初始化,您可以实现一种“策略模式”形式,其中策略是可变的并且仅定义如何创建新的、ID 初始化的对象。在 Java 8 及更高版本中,您可以为此使用函数式接口和 Lambdas:private <T extends AbstractSuperClassA > T init(NamedNodeMap attributes, Function<String,T> creator) {&nbsp; ...&nbsp; T newElement = creator.apply(identifier);&nbsp; ...}然后适当地使用它,例如B someB = init(attrs, B::new);C someC = init(attrs, id -> {C c = new C(); c.setId(id); return c;});...然而,问题是您如何决定应该实例化哪个具体类。在任何情况下,该逻辑都必须在某个地方进行编码,因此可能有更好的方法来连接逻辑以收集值和初始化新实例的逻辑。是否要求实例id在构造函数中接收?还是可以稍后设置?
随时随地看视频慕课网APP

相关分类

Java
我要回答