简体   繁体   English

java静态工厂中的超类

[英]java static factory in super class

I have some java beans(which have private attributes and getter/setter methods). 我有一些Java Bean(具有private属性和getter/setter方法)。 And could build instances with a Map<String, Object> like this in the constructor: 并可以在构造函数中使用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()
        ...

}

But when there are some children classes that extend class A , there should be a corresponding constructor every time I write a new subclass: 但是,当有一些子类扩展A类时,每次我写一个新的子类时都应该有一个对应的构造函数:

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

I find this annoying and I want to build a static factory method in the parent class A to do this once for all (Maybe some code like this in class A ?): 我觉得这很烦人,我想在父类A建立一个静态工厂方法来一次全部执行一次(也许在A类中这样的代码?):

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

And could someone give me a hint on how to write this factory method? 有人可以给我提示如何编写此工厂方法吗? Or is that possible? 还是可能吗? Maybe some generic trick? 也许是一些通用技巧?

The approach you want to use is not the best one. 您要使用的方法不是最好的方法。 Indeed, if you put the factory method inside the super class, then it will have to know which are its subclasses. 确实,如果将工厂方法放在超类中,则它必须知道哪些是其子类。 So, this approach breaks the abstraction principle . 因此,这种方法破坏了抽象原理 For example, if you give to the base class the responsibility to build it's subclasses, you will have to change it every time you will add a new subtype. 例如,如果您赋予基类构建其子类的责任,则每次添加新的子类型时都必须对其进行更改。 Moreover, this violates the Single Responsiblity Principle . 而且,这违反了单一责任原则

You can use a factory method , but you have to extract it and put to a dedicated class. 您可以使用工厂方法 ,但必须将其提取出来并放入专用的类中。

Having to call a constructor of the super class inside a subclass is a garantee of the fact that the subclasses are a refinement of the super class. 必须在子类内部调用超类的构造函数是事实,即子类是对超类的改进。

Finally, to have a lower level of coupling between the classes of your hierarchy, I suggest you to use inheritance only from an abstract type, such as abstract classes or interface s. 最后,为了使层次结构的类之间具有较低的耦合级别,建议您仅使用来自抽象类型的继承,例如abstract类或interface

To answer your question, and assuming the fully qualified class name in the "class" entry of the map: 要回答您的问题,并在地图的“类”条目中采用完全限定的类名称:

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

I prefer using a helper class, something like the following: 我更喜欢使用帮助器类,如下所示:

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

The usage is a bit awkward that can be taken care by a bit more work. 用法有些笨拙,可以通过多做一些工作来解决。

As other answers describe, the super-class cannot know the type of the sub-class unless it's passed in runtime. 正如其他答案所描述的那样,除非超类在运行时传递,否则它无法知道子类的类型。

If the issue you're trying to avoid is to have to create the map-constructor in each sub-class, I see only two solutions, each with its flaws: 如果您要避免的问题是必须在每个子类中创建map-constructor,那么我只会看到两个解决方案,每个解决方案都有其缺陷:

1) Use no-arg constructor and an init() method taking the Map: 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);
}

The downside of this is that Animals are no longer immutable, unless you enforce a check in init() such as a flag boolean isInitialized , but it's still kind of ugly. 这样做的缺点是动物不再是不可变的,除非您在init()强制执行检查,例如标志boolean isInitialized ,但这仍然很丑陋。 You also don't get the return type back from init, so you can't do things you would be able to do with a direct constructor+initializer such as new Dog(map).bark() . 您也不会从init获得返回类型,因此您无法使用直接构造函数+初始化程序(例如new Dog(map).bark()可以做的事情。 This can be addressed by making the super-class generic, taking the concrete sub-class as the parameter: 这可以通过使超类通用,并以具体的子类作为参数来解决:

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

Still, this approach suffers from the API having a sense of being incomplete, since it's possible to construct objects that don't have a map because init() is called separately. 尽管如此,该方法仍会遭受API的不完整感,因为有可能因为init()被单独调用而构造了没有映射的对象。 If that's an issue for you of course depends on if this is a published API for third parties to use or just some internal classes where you can live with this pattern. 对于您来说,这是否是一个问题,当然取决于这是已发布的供第三方使用的API,还是仅一些您可以使用此模式的内部类。

2) A static factory method taking a Class<? extends Animal> 2)采用Class<? extends Animal>静态工厂方法 Class<? extends Animal> and relying on reflection to create the class instance: 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();
}

This pattern relies on each sub-class having an accessible no-args constructor, which also isn't very pretty if the purpose is to hide class construction and avoid being able to construct incompletely initialized objects. 这种模式依赖于每个子类都具有可访问的no-args构造函数,如果其目的是隐藏类构造并避免能够构造不完全初始化的对象,则该样式也不是很漂亮。 The syntax is also a bit verbose, having to pass the Class object. 语法也有点冗长,必须传递Class对象。

If all of this is really worthwhile just to avoid having to generate constructors calling super on each sub-class (which any decent IDE would be able to do automatically) is quite debatable... 如果所有这些真的值得,只是避免必须在每个子类上生成调用super的构造函数(任何体面的IDE都可以自动执行),这值得商is。

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

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