循序渐进学习设计模式——创建型模式——工厂设计模式

关于工厂设计模式:顾名思义,工厂代表着加工制作,生活中的日常用品、汽车零件等等一系列的东西,都是由工厂加工而成,那么代码是否也能这样呢?

 

在面向对象思想中,最关键的词就是“对象”,我们在使用面向对象思想编程时,第一件事就是“创建对象(new)”。

 

但是,当我们面对大量的类,甚至是一些相似的类,在使用时都需要不断的new,比如当我们要使用一个“狗”的对象,那就要创建“狗”这个对象,要使用“猫”这个对象,就必须要创建“猫”这个对象

 

这样当我们每次需要使用一个新的类,就要去new一个新的对象出来,当我们的需求很多时,new的操作就会让我们感觉到疲惫。那么,有没有一个方法,可以让我们省去new这个步骤呢?

 

方法就是使用工厂设计模式,我们定义一个工厂类,把new的过程存进工厂的方法中,当我们需要哪个对象,就直接调用工厂中的方法。

 

举个例子,当我们需要使用“狗和猫”两个对象,将new的过程存入工厂中。

虽然在我们的主类中的确不需要进行new这个过程,但是代码量并没有减少,因为获取“狗”对象和获得“猫”对象需要分别调用两个方法。

 

那么,如果可以用同一个方法就可以得到不同对象就可以让工厂更加遍历。

 

通过参数返回不同类型子类对象,使用不同的参数来获取不同的对象。

经过改进后的可以只通过一个方法就可以返回各种需要的(只能是同一个父类)对象,也就是说在上面的代码中,只能返回继承了Animal的对象。

 

那么此时的改进工厂的思路就是可以返回任意类型的对象,如何实现?

 

此时工厂的设计只需要满足两个条件:

 

1. 能够创建任意类型的对象

 

2. 能返回任意类型的对象。

 

那么此时延伸出的问题就是:

 

如何能够创建任意类型的对象?使用反射

 

如何返回任意类型的对象?  返回值设置成Object类型

此时的工厂已经初具规模,但是仍然存在问题。

 

如果是接口怎么处理?接口无法创建对象。

 

当类型是Object时,每次调用方法都需要强制类型转换,太麻烦。

 

此时的问题如何解决?使用泛型来解决返回值问题

此时,工厂只剩下一个问题,接口怎么处理?

 

接口无法创建对象,所以此时接口的实现类对象就是解决办法,返回此接口的实现类对象即可。

 

那么如何获得接口的实现类对象?使用配置文件

 

由于XML约束的存在,是的每一个bean标签中都具有id和className两个属性,所以,将接口名设置为id值,将实现类名设置为className值。

 

在工厂中的方法同样传入类对象参数,使用反射(getSimpleName方法)来获取接口名。

 

之后使用dom4j和xpath对XML进行解析,通过接口名来获取配置文件中的className的值,也就是实现类对象的名字。

 

有了实现类对象,就可以通过反射(Class.forName.newInstance)来创建实例对象

 

最后将此对象使用泛型返回,就得到了一个接口的实现类对象。

 

至此,工厂的设计得到的全面的完善(工厂中读取配置文件地址仍可以使用类加载器改进),可以返回任意类型的实例对象,并且可以返回接口的实现类对象。

 

使用工厂后,在需要创建接口的实现对象时,如果要使用同一个接口的不同实现类,只需要修改同一个接口的className的值即可。如果要使用不同的接口,只需要新增一个标签即可。

测试用例结果:

工厂设计模式最终版(使用类加载器读取配置文件地址)

public class BeanFactory {
    public static <T>T getBean(Class<T> clz) throws Exception {
        //clz 表示接口的类对象,获取接口名
        String simpleName = clz.getSimpleName();

        //此时就是解析XML文件的过程,通过接口名来查找实现类名
        SAXReader saxReader = new SAXReader();
        /*
            使用类加载器读取配置文件
            配置文件必须在src目录下才能使用类加载器
         */
        Document document = saxReader.read(BeanFactory.class.getClassLoader().getResource("beans2.xml"));
        Element element = (Element)document.selectSingleNode("//bean[@id='" + simpleName + "']");

        //配置文件中完整的实现类名称
        String className = element.attributeValue("className");

        //此刻通过实现类名称创建实例对象并返回(反射)
        Object o = Class.forName(className).newInstance();
        return (T)o;
    }
}

 

前人栽树,后人乘凉。

经典的事物永远都不会褪色。

设计模式正是前人们经过认真的探索和思考,代表了最佳的实践,由于设计模式的存在使得我们可以直接地去体验这些经典。

 

posted @ 2020-09-06 20:49  硬盘红了  阅读(120)  评论(0编辑  收藏  举报