[英]java static factory methods in Spring Model class
[英]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.