【设计模式】简单工厂

简单工厂模式

严格的说,简单工厂模式并不属于 23 中设计模式,不过我浏览了几本与设计模式相关的术后,发现都提及了该知识点,可能作者的目的是为了让我们能循序渐进地理解后面的工厂方法、抽象工厂。总之,这个模式还是一个很值得去理解的模式。

问题的引入

在设计原则中有这么一句话——“要面向接口编程,而不要面向实现编程”。不过我们一直使用 new 关键字创建对象,这好像有悖这一原则。

接口的思想是封装隔离,这里的封装指的是“对被隔离体的行为的封装”,或者说是“对被隔离体职责的封装”;所以面向接口编程,可以免去将来系统发生的一系列修改。假如是面向接口编程,那么今后代码发生改变时,可以通过多态,让任何新的类实现该接口。但是如果我们在代码中将接口引向具体类,那么情况就不是那么乐观了,因为一旦要加入新的具体类,你就必须改变代码。用代码来说明或许会简明了当些

首先我们定义一个接口

public interface Utils {
    /**
    * 定义一个行为
    */
    void log(String s);
}

现在我们再定义一个类来实现这个接口

public class StringUtils implement Utils {
    public void log(String s) {
        sout("StringUtils Implement:" + s);
    }
}

那么此时我们会怎么调用这个接口中的行为呢?使用接口的实现类?

public class Test {
    public static void main(String[] args) {
        Utils utils = new StringUtils();
        utils.log("hello world!");
    }
}

这样看起来这段代码好像没什么问题。但是,接口的思想是“封装隔离”,所以实现类 StringUtils 应该是被接口 Utils 封装并同 Test 离开的,换而言之就是 Test 类不应该知道 Utils 的实现类是 StringUtils。以上代码并不符合DIP(Dependence Inversion Principle)原则,所以在这里,我们就需要有一个简单的方法,当我传递参数调用它的时候,就可以返回一个我要的具体实现对象给我。这就是将要介绍的简单工厂模式。

实现思路

简单工厂的定义
提供一个创建对象实例的功能,而无需关心其具体实现。被创建实例的类型可以是接口、抽象类、也可以是具体的类。

现在在之前代码的基础上,对 Utils 新增加一个实现类

public class LogUtils implement Utils {
    public void log(String s) {
        sout("LogUtils Implement:" + s);
    }
}

接下来就可以创建一个简单工厂了,实例代码如下:

public class Factory {

    // 具体创建 Utils 对象的方法
    public static Utils createUtils(String condition) {
        Utils utils = null;
        if (condition.equals("string")) {
            utils = new StringUtils();
        } 
        else if(condition.equals("log")) {
            utils = new LogUtils();
        }
        return utils;
    }
}

现在我们就可以使简单工厂方法来得到一个对象了

public class Test {

    public static void main(String[] args) {
        Utils utils = Factory.createUtils("String");
        utils.log("hello java");
    }
}

可配置的简单工厂

上面代码就是简单工厂的具体实现了,也达到了我们想要的封装和解耦的目的,但是还有一个问题,就是要怎么样添加一个新的实现类呢?假如我现在对 Utils 接口还有一个新的实现类 DateUtils,那么要怎么样将其添加到工厂中呢?

最简单的方式当然是在工厂类的方法中添加新的代码块了

else if(confition.equals("date") {
    utils = new DateUtils();
}

不过每增加一个新的实现类,就改动一次工厂类,这样的实现方式不是很妥当,所以就要引入其他的方式了,例如通过配置文件来实现添加新的实现类

首先你需要定义一个配置文件“Factory.properties”,然后在配置类中定义你要的实现类

ImplClass=com.java.factory.DateUtils

如果添加了实现类,修改配置文件中的配置即可,就不用去修改代码而从新编译了。

接下来定义新的工厂类的实现

public class Factory() {
    
    public static Utils createUtils() throws Exception {
       
        Properties p = new Properties();
        InputStream in = Factory.class.getResourceAsStream("Factory.properties");
        p.load(in);
        
        Utils utils = (Utils)Class.forName(p.getProperty("ImplClass").newInstance())
        
        return utils;
    }
    
}

emmmm,这样实现应该可以解决上面的问题了。不过这里还有更好的实现方式,就是采用Ioc/DI的方式来实现。

谈一谈优劣

  • 优点:简单工厂帮助实现了封装隔离,使外部调用组件能真正实现面向接口编程,同时也实现了程序间的解耦。
  • 劣势:加重了程序员理解代码的成本,如果工厂需要通过参数来选择实现类,那么就还需要让程序员去理解每个参数所代表的含义。
posted @ 2019-07-21 11:47  周二鸭  阅读(165)  评论(0编辑  收藏  举报