簡體   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