繁体   English   中英

java静态工厂中的超类

[英]java static factory in super class

我有一些Java Bean(具有private属性和getter/setter方法)。 并可以在构造函数中使用Map<String, Object>构建实例,如下所示:

public class A {
    private String name;
    private String gender;
    ...

    public A(Map<String, Object> m){
        BeanInfo beanInfo = Introspector.getBeanInfo(this.getClass());
        for(PropertyDescriptor pd: beanInfo.getPropertyDescriptors()){
            if(pd.getName().equals("class")){
                continue;
            }
            Method setter = pd.getWriteMethod();
            if(setter != null && m.get(pd.getName())!=null){
                setter.invoke(this,m.get(pd.getName()));
            }
        }
    }
    getter()/setter()
        ...

}

但是,当有一些子类扩展A类时,每次我写一个新的子类时都应该有一个对应的构造函数:

public class B extends A {
   public B(Map<String, Object> m){
       super(m);
   }
}

我觉得这很烦人,我想在父类A建立一个静态工厂方法来一次全部执行一次(也许在A类中这样的代码?):

  public static fromMap(Map<String, Object>){
      return new // I really don't have a clue how to write this.
  }

有人可以给我提示如何编写此工厂方法吗? 还是可能吗? 也许是一些通用技巧?

您要使用的方法不是最好的方法。 确实,如果将工厂方法放在超类中,则它必须知道哪些是其子类。 因此,这种方法破坏了抽象原理 例如,如果您赋予基类构建其子类的责任,则每次添加新的子类型时都必须对其进行更改。 而且,这违反了单一责任原则

您可以使用工厂方法 ,但必须将其提取出来并放入专用的类中。

必须在子类内部调用超类的构造函数是事实,即子类是对超类的改进。

最后,为了使层次结构的类之间具有较低的耦合级别,建议您仅使用来自抽象类型的继承,例如abstract类或interface

要回答您的问题,并在地图的“类”条目中采用完全限定的类名称:

public static Object fromMap(Map<String, Object> map) throes Exception
    return Class.forName((String)map.get("class")).getConstructor(Map.class).newInstance(map);
}

我更喜欢使用帮助器类,如下所示:

package empty;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

public class FromMap {

    public static class First {
        private String name;
        private int age;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public int getAge() {
            return age;
        }

        public void setAge(int age) {
            this.age = age;
        }

        @Override public String toString() {
            return "First [name=" + name + ", age=" + age + "]";
        }

    }

    public static void main(String[] args) {
        Map<String, Object> v = new HashMap<String, Object>();
        v.put("name", "My Name");
        v.put("age", 100);
        try {
            First f = FromMap.fromMap(v, new First());
            System.out.println(f);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static <T> T fromMap(Map<String, Object> m, T t) throws IllegalAccessException, IllegalArgumentException,
            InvocationTargetException, IntrospectionException {
        BeanInfo beanInfo = Introspector.getBeanInfo(t.getClass());
        for (PropertyDescriptor pd : beanInfo.getPropertyDescriptors()) {
            if (pd.getName().equals("class")) {
                continue;
            }
            Method setter = pd.getWriteMethod();
            if (setter != null && m.get(pd.getName()) != null) {
                setter.invoke(t, m.get(pd.getName()));
            }
        }
        return t;
    }
}

用法有些笨拙,可以通过多做一些工作来解决。

正如其他答案所描述的那样,除非超类在运行时传递,否则它无法知道子类的类型。

如果您要避免的问题是必须在每个子类中创建map-constructor,那么我只会看到两个解决方案,每个解决方案都有其缺陷:

1)使用无参数构造函数和采用Map的init()方法:

public class Animal {
  public void init(Map<String, Object> map) {
    ...
  }
}

public class Dog extends Animal {
  public void bark() { ... }
}

static void test {
  Dog dog = new Dog().init(map);
}

这样做的缺点是动物不再是不可变的,除非您在init()强制执行检查,例如标志boolean isInitialized ,但这仍然很丑陋。 您也不会从init获得返回类型,因此您无法使用直接构造函数+初始化程序(例如new Dog(map).bark()可以做的事情。 这可以通过使超类通用,并以具体的子类作为参数来解决:

public class Animal<A extends Animal> {
  public A init(Map<String, Object> map) {
    ...
  }
}

public class Dog extends Animal<Dog> {
  public void bark() { ... }
}

static void test {
  new Dog().init(map).bark();
}

尽管如此,该方法仍会遭受API的不完整感,因为有可能因为init()被单独调用而构造了没有映射的对象。 对于您来说,这是否是一个问题,当然取决于这是已发布的供第三方使用的API,还是仅一些您可以使用此模式的内部类。

2)采用Class<? extends Animal>静态工厂方法 Class<? extends Animal>并依靠反射来创建类实例:

public class Animal {
    public static class Factory {
        public static <A extends Animal> A create(Class<A> animalClass, Map<String, Object> map) {
            try {
                A a = animalClass.newInstance();
                a.init(map);

                return a;
            } catch (InstantiationException e) {
            } catch (IllegalAccessException e) {
            }

            return null;
        }
    }

    void init(Map<String, Object> map) {
        // ...
    }
}

public class Dog extends Animal {
  public void bark() { ... }
}

static void test() {
    Animal.Factory.create(Dog.class, map).bark();
}

这种模式依赖于每个子类都具有可访问的no-args构造函数,如果其目的是隐藏类构造并避免能够构造不完全初始化的对象,则该样式也不是很漂亮。 语法也有点冗长,必须传递Class对象。

如果所有这些真的值得,只是避免必须在每个子类上生成调用super的构造函数(任何体面的IDE都可以自动执行),这值得商is。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM