融会贯通——工厂模式全方位解析

工厂模式是面向对象设计模式中非常重要的模式,使用非常广泛,是应该首先被理解透彻的模式。

我们讲对象的相关职责包括:

  1. 对象本身的职责(数据和行为)
  2. 创建对象的职责
  3. 使用对象的职责

而对象的创建在Java中有四种方式:

  1. new
  2. 反射
  3. clone()
  4. 工厂类创建

工厂模式是创建型设计模式

程序员的敏感地带:

  1. 大量的重复性代码,大量的if...else...语句
  2. 一个类过于复杂,违反了“单一职责原则”
  3. 如果有扩展会引发修改,违反了“开闭原则”
  4. 使用对象时不要用new来创建对象,耦合度高

我们先描述一个场景,某产品展销会上,用户甲需要了解多种产品的详细情况,他需要自己去根据每个产品名字在地图上查找其陈列位置,然后跑过去挨家看。而此时,待着家里的用户乙也想了解一下这个展销会里面的产品,他给举办此次展销会的厂商丙打了个电话,厂商接待人员说,“你想了解哪个产品,把名字告诉我,我直接就告诉那个产品的情况。”

  • 用户甲自己new完成了产品实例的查找及创建,然后调取了产品的内部详细信息。总共只有一个类,丝毫没有设计可言,所有代码堆砌在这个类中,维护性,灵活性,扩展性,复用性全为0。
  • 用户乙通过工厂类丙创建的方式获得了产品的实例,然后调取了产品的内部详细信息。

上面提到了对象的三种职责,根据“单一职责原则”。

两个类A和B之间的关系应该仅仅是A创建B或者A使用B,而不能是两种都有。

工厂模式就是要增加一层厂商丙来统一管理对象的创建职责,而不是让对象实例化的代码掺杂在多个类中到处都是。


厂商丙的多种实现方式之一:简单工厂模式

定义一个工厂类,增加一个静态方法(所以也叫静态工厂模式),传入不同的参数,内部根据这个参数进行判断,返回不同类的实例,这些被创建实例的类通常都有共同的父类。我们看一下在简单工厂模式中,用户是如何获取一个实例化对象的。

class User {  
    public static void main(String args[]) {
        Book book1 = Factory.createBook("novel");  
        book1.read();
    }
}

而工厂类的内容是:

class Factory {  
    public static Book createBook(String bookType) {
        if("novel".equals(bookType){
            return new NovelBook();  
        }else if("poem".equals(bookType){
            return new PoemBook();  
        }else if("history".equals(bookType){
            return new HistoryBook();  
        }else{
            return null;
        }
    }
}

缺陷:

  1. 工厂类内部复杂,有大量if...else...判断
  2. 扩展要修改工厂类中的if...else...语句,违反“开闭原则”

厂商丙的多种实现方式之二:工厂方法模式

定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。
我们看一下在工厂方法模式中,用户是如何获取一个实例化对象的。

class User {  
    public static void main(String args[]) {
        Factory factory = new NovelBookFactory();
        Book book1 = factory.createBook();
        book1.read();
    }  
}

稍作解读,就是在简单工厂模式里的工厂类的基础上又抽象出一层,将其改为工厂接口Factory,声明一个工厂方法factoryMethod(),而具体产品要有对应的工厂类去实现Factory接口,并重写工厂方法返回对应的具体产品实例。将实例化时机又转移给了用户,用户来决定用哪个具体产品实例。

interface Factory {  
    Book creatBook();
}
class NovelBookFactory implement Factory{  
    Book creatBook(){
        return new NovelBook();
    }
}

这与前面提到的场景中,用户甲也是自己判断然后创建实例是截然不同的,因为工程方法只是让用户决定实例化哪个具体产品,而不会让他去创建,创建的工作还是交给工厂,所以在扩展的时候,当前工厂类,产品类都是不用改动的,只需要再增加一对具体工厂类和具体产品即可。

缺陷:

  • 每次扩展需要增加一对具体工厂类和具体产品类,久而久之,会造成程序中类的数量爆炸。
问题:我们为什么不这样写?
class User {  
    public static void main(String args[]) {
        Book book1 = new NovelBook();
        book1.read();
    }  
}

看上去与上面的工厂方法模式的使用没有任何区别,扩展的时候,也只是需要新增一个子类即可,符合“开闭原则”,但是,这样写是不是就违反了“单一职责原则”呢?我们希望能将具体对象的创建职责通过工厂去管理起来,使程序的可读性更高,也降低类之间的耦合度,不在使用对象时直接new其实例。

厂商丙的多种实现方式之三:抽象工厂模式

提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
接着上面的代码改造一下。

class User {  
    public static void main(String args[]) {
        Factory factory = new LuXunBookFactory();
        Prose prose1 = factory.creatProseBook();
        Essay essay1 = factory.creatEssayBook();
        Novel novel1 = factory.creatNovelBook();
        prose1.read();
        essay1.read();
        novel1.read();
    }  
}
class LuXunBookFactory implement Factory{  
    Prose creatProseBook(){//散文
        return new ProseBook();
    }
    Essay creatEssayBook(){//杂文
        return new EssayBook();
    }
    Novel creatNovelBook(){
        return new NovelBook();
    }
}

相较于工厂方法模式,抽象工厂模式将具体工厂类中的具体产品类进行了组合的关联。如上面代码所示,用户要得到的是鲁迅全集的实例,鲁迅全集工厂类就要包括散文,小说和杂文,而这些都是书的子类。如果是工厂方法模式的话,恐怕就要每种书记形式都要建一个自己的工厂类,那实在是庞大至极。程序变得很冗长。所以抽象工厂模式在具体子类中具备一定的关联关系的时候,非常好用。


我们看到了上面用户在使用工厂模式创建实例时,避免了直接new具体对象实例的方式,但是能否把new LuXunBookFactory()也避免了呢?进一步解耦?
答案是肯定的。

使用反射加配置文件代替new LuXunBookFactory()

我们在创建一个实例的时候,能否直接用字符串本身当做参数来创建对象呢?使用反射就可以达到这个目的。

Factory factory = (Factory) Class.forName("LuXunBookFactory").newInstance();

这样就代替了

Factory factory = new LuXunBookFactory();

那么如果要完全符合“开闭原则”,即用户使用的部分也不去修改,那么就可以采用配置文件的方式。
将字符串"LuXunBookFactory"保存在xml配置文件中。

<?xml version="1.0"?>  
<config>  
    <className>LuXunBookFactory</className>  
</config> 

每次利用Java工具XMLUtil类去读取该配置文件,用变量代替原代码中字符串的位置。


三种模式的适用场景

  • 简单工厂模式之所以没有被官方收入,是因为它只是用来学习入门工厂模式的,在实际工作中,很少被使用。
  • 工厂方法模式更适合那种子类之间并没有直接关联关系的结构,没办法,只能每种产品建立一个独立的工厂去创建。
  • 抽象工厂模式就适合那种子类之间有明显的关联关系的结构,将他们拆分归并,仅给一批关联在一起的子类建立一个独立的工厂,它可以批量生产出这批子类的所有实例。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值