工厂模式和“万能”工厂类

848 查看

需要构造一个类的对象,我们通常会用new的方法来构造:

A a = new A();

但是如果随着这个对象被创建的同时还必须进行其他相对复杂的操作,那么在新建构造一个对象代码就显得非常复杂,这个复杂的代码通常还需要每一次都复写,大大增加了冗余。

因此我们构造了生产这个类对应对象的工厂,用工厂的方法来生产对象。
比如:我们需要在构造一个对象时并不知道这个对象中的某个字段的确切值,该值保存在配置文件中,所以每次构造对象都要进行配置文件的读取,依据配置文件中的内容来确定这个对象中该字段的信息,这个过程如果直接使用new来实现将会造成大量代码的冗余,大量冗余存在于配置文件的加载和读取,不妨把它封装成工厂类,在工厂类内部实现配置文件的加载和读取。

有如下配置文件:/src/config.properties:

ImportantString = OK

有如下预创建的对象所属类:/src/A.java:

public class A {
    private String Aname;
    public void Ashout(){
        System.out.println("I am "+Aname+",hahaha !");
    }
    public String getAname() {
        return Aname;
    }
    public void setAname(String aname) {
        Aname = aname;
    }
}

有如下测试文件:/src/Test.java:

public class Test{
    public static void main(String[] args) {
        A a = AFactory.getA();
        a.Ashout();
    }
}

则可以构造工厂类来生产该类的对象,生产过程中加载读取配置文件并将配置文件中的重要字段赋值给创建出的对象,并返回这个创建出的对象。

如下工厂类文件:/src/AFactory.java:

public class AFactory {
    private static Properties properties = null;
    private static String ANameToGet;
    private AFactory() {

    }
    static{
        try {
            properties = new Properties();
            properties.load(new FileReader(AFactory.class.getClassLoader().getResource("config.properties").getPath()));
            ANameToGet = properties.getProperty("ImportantString");
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }
    public static A getA(){
        A a = new A();
        a.setAname(ANameToGet);
        return a;
    }
}

我们可以发现,通过工厂类的包装,在调用工厂类方法构造对象时可以十分轻松地完成任务,而后面的其他任务则交由工厂类的静态代码块完成。

那么什么是万能工厂类呢?万能当然不是指能完成一切的工厂,而是说在很多情况下,我们可能需要构造各种类的对象,而这些对象在构造过程中可能也会完成很多跟构造相分离的相对复杂的任务,这些任务可能会导致前台代码冗余,而恰恰我们要构造的这些对象虽然分属不同的类却有结构相同的代码,这是我们不妨使用泛型来进一步解决这个问题。

以上面的例子为基础,我们需要两个类,在这两个类的对象的创建过程中都必须要检查配置文件中的一对Key是否相等,相等的Key才能允许对象的创建,
否则不允许创建,这个过程中如果在new之前直接做判断逻辑,将会导致大量的代码冗余,因为每个类的对象创建前都要进行相同的判断。因此我们考虑
把这部分代码写入工厂类,再生产过程前进行判断,过程封装进工厂类。
但是由于我们两个类的对象的生成实现过程类似,因此不需要分别写工厂,而是统一地写一个泛型化的工厂,依据传进的类的类型利用Class.forName(className).newInstance()来创建新的对象。

有如下配置文件:/src/config.properties:

Key1 = RightKey
Key2 = RightKey

有如下预创建的对象所属类:/src/A.java:

public class A {
}

/src/B.java:

public class B {
}

有如下测试文件:/src/Test.java:

public class Test{
    public static void main(String[] args) {
        A a = CommonFactory.getInstance();
        B b = CommonFactory.getInstance();
    }
}

则可以构造工厂类来生产该类的对象,生产过程中加载读取配置文件并将判断配置文件中的Key以确定对象是否能正常生成,如果可以返回这个创建出的对象。

如下工厂类文件:/src/CommonFactory.java:

public class CommonFactory {
    private static Properties properties = null;
    private static String Key1;
    private static String Key2;
    static boolean no;
    private CommonFactory() {
    }
    static{
        try {
            properties = new Properties();
            properties.load(new FileReader(CommonFactory.class.getClassLoader().getResource("config.properties").getPath()));
            Key1 = properties.getProperty("Key1");
            Key2 = properties.getProperty("Key2");
            no = Key1.equals(Key2);
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }
    public static <T> T getInstance(Class<T> clazz){
        try {
            String className = clazz.getName();
            if(no){
                T t = (T) Class.forName(className).newInstance();
                System.out.println(className+" Object Astablished Successfully");
                return t;
            }
            else
            {
                System.out.println(className+" Object Astablished failed");
                return null;
            }


        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }

    }
}