超类中的java静态工厂

时间:2015-06-30 10:33:12

标签: java factory

我有一些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.
  }

有人可以给我一个关于如何编写这个工厂方法的提示吗?或者这可能吗?也许是一些通用技巧?

4 个答案:

答案 0 :(得分:2)

您想要使用的方法并不是最好的方法。实际上,如果将工厂方法放在超类中,那么它必须知道它的子类。因此,这种方法打破了抽象原则。例如,如果您向基类提供构建其子类的责任,则每次添加新子类型时都必须更改它。而且,这违反了单一责任原则

您可以使用工厂方法,但必须将其解压缩并放入专用类。

必须在子类中调用超类的构造函数是保证子类是超类的细化。

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

答案 1 :(得分:0)

回答你的问题,并在地图的“class”条目中假设完全限定的类名:

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

答案 2 :(得分:0)

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

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

使用方法有点尴尬,可以通过更多的工作来处理。

答案 3 :(得分:0)

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

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

1)使用no-arg构造函数和init()方法获取Map:

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>并依赖反射创建类实例的静态工厂方法:

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都可以自动执行)是非常有争议的......