软件秘笈:设计模式那点事
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

第一部分 创建型模式

2 曹操献刀:工厂方法模式

2.1 三国故事之曹操献刀

中国的四大古典名著《三国演义》、《水浒传》、《西游记》和《红楼梦》你喜欢哪一部?还是都喜欢?笔者个人比较喜欢《三国演义》一书。因为,无论从人物、历史还是故事情节上,《三国演义》都是很深入人心的,人物更是刻画得活灵活现,让人记忆深刻。今天,就让我们重温三国故事之“曹操献刀”(“三国”电影剧照如图2-1所示),看看故事中蕴涵着什么样的设计模式理念。

图2-1 “三国”电影中曹操献刀

很久很久以前……哦,对了,不好意思我们是在说三国故事……

(故事开场序)

话说三国时期,西凉刺史董卓乘朝野之乱,统帅二十万大军进驻洛阳,废了少帝,立了献帝,自封为相国。董卓为人残暴,欺主弄权,朝中正直大臣们都想杀之而后快,然而,董卓权倾朝野,位高权重,身边还有号称天下第一神勇武士吕布陪伴左右,无人敢近,众大臣只能恨于心中。校尉曹操,足智多谋,早有杀董之心。一日,在大司徒王允处借得七星宝刀,进府行刺董卓。

(序完)

(曹操献刀细节部分)

话说,曹操得了七星宝刀,喜出望外。曹操心想,这下可有我的出头之日了!只要杀了董贼,我就是英雄,就会名垂千古,万世流芳!(暗笑)哈哈……曹操身藏宝刀,来到董卓相府,寻找刺杀董卓的机会。

见曹操来到,董卓问曹操:“孟德为何来得这么晚啊?”曹操回答:“马羸行迟耳。”曹操的意思是,我的马太瘦弱了,走不快,所以来晚了(羸:瘦弱,困顿的意思)。董卓听后,命吕布选一匹西凉好马送给曹操,吕布就出去给曹操选马去了(曹操心中大喜,正不知如何让吕布离开呢)。说罢,董卓便侧身于塌上(休息了)。曹操一见机会来了,急抽刀欲刺之,然宝刀光芒四射(夸张了一点),被铜镜反射到了董卓的脸上,董卓猛然转身,追问曹操:“孟德,你想要干什么?!”不巧,吕布这时也牵马从外面回来了。曹操反应迅速,双膝跪地,举刀说道:“操偶得一宝刀,欲献给丞相!”“噢……是吗?”董卓惊讶道。接过宝刀一看,果然是把好刀!“好刀!”董卓不禁大叫道。此时曹操双膝跪地,吓出了一身冷汗,见董卓暂时未识破行刺的计策,曹操果断出击,“操预试马一试,望丞相恩准!”“好,孟德!去试试西凉好马吧!”曹操借马飞奔而去。这时董卓突然醒悟,派人去追,然曹操早已不见踪影。

好了,曹操献刀的故事就讲到这里。下面,我们来看看其中蕴含的设计模式吧,看我们如何使用设计模式实现曹操献刀的故事!

2.2 模式定义

所谓工厂方法模式(Factory Method Pattern),就是定义一个创建产品对象的工厂接口,让子类决定实例化哪一种实例对象,也就是将实际创建实例对象的工作推迟到子类当中,核心工厂类不再负责具体产品的创建。

工厂方法模式是对简单工厂模式进行了抽象。如此一来核心类成为一个抽象工厂角色,仅负责具体工厂子类必须实现的接口,这样进一步抽象化的好处是使得工厂方法模式可以使系统在不修改具体工厂角色的情况下引进新的产品。工厂方法模式完全实现“开-闭”原则,对扩展开放,对修改关闭。在不影响现有软件结构的基础上实现了扩展,可应用于更复杂的层次结构和产品结果复杂的场合。

2.3 故事中的模式分析

2.3.1 故事中的角色

我们再次将话题转到曹操献刀的故事,故事中提到“董卓废了少帝,立了献帝,自封为相国”,这是什么?这是乱臣贼子,人人得以诛之——“朝中正直大臣们都想杀之而后快”,然而,“董卓为人残暴,欺主弄权”、“权倾朝野,位高权重”,更有“天下第一神勇武士吕布陪伴左右”,“众大臣只能恨于心中”、“无人敢近”。大司徒王允也正是“众大臣”当中的一分子,谁都想当英雄,但英雄可不是谁都能当的!校尉曹操,足智多谋,早有杀董之心,得知王允府中珍藏有无比锋利的七星宝刀,预借此刀刺杀董卓。曹操到王允府中借七星宝刀,并表明借刀的意图,王允大喜,因为曹操有胆量,他能做王允想做而不敢做的事情,二人不谋而合,设计刺杀董卓的计划。之后,就有了后面曹操到董卓相府刺杀的一幕。

综合以上的叙述,和我们本章将要讲解的工厂方法设计模式,你能找出它们之间存在的相同之处吗?看工厂方法模式的定义:定义一个创建产品对象的工厂接口,让子类决定实例化哪一种实例对象,也就是将实际创建实例对象的工作推迟到子类当中,核心工厂类不再负责具体产品的创建。我们可以这样理解工厂方法模式:由一个抽象工厂定义了创建产品的接口,但是具体是哪一种产品抽象工厂是不知道的,产品的实例化工作被推迟到了具体工厂当中。就像我们故事中讲到的,大司徒王允拥有宝刀,但是王允不会使用宝刀去刺杀董卓的,因为王允没有这个胆量,然而,曹操却有胆量,他会使用最锋利的七星宝刀去刺杀董卓。好了理解的已经差不多了,我们看一下故事中出现的人物和事物各自对应工厂方法设计模式的哪些部件,如下所示:

大司徒王允—宝刀工厂(负责提供宝刀)

校尉曹操—获得七星宝刀的具体工厂

七星宝刀—具体产品(宝刀中的一类)

三者之间的关系如图2-2所示。

图2-2 三者之间的关系图

2.3.2 抽象化分析方法

从图2-2中可以容易地看出,大司徒王允拥有七星宝刀,曹操在王允处获得七星宝刀。这是没有问题的,但是,这只是表层的对象实物之间的关系,也就是面向具体实现的关系。其实我们可以再抽象化一点:大司徒王允拥有很多宝刀,而七星宝刀是其中最好、最锋利的一把,曹操获得的就是这把最锋利的七星宝刀。OK!我们已经使用了抽象的概念了!抽象之后的关系如图2-3所示。

图2-3所示的就是抽象化的关系图。对比图2-2,图2-3中增加了“各类宝刀”环节,这就是对七星宝刀的抽象,也是对大司徒王允生产宝刀的抽象(他不是只拥有七星宝刀哦,还有其他的宝刀),大司徒王允就相当于一个生产宝刀的抽象工厂(他收藏宝刀的时候可不知道有谁会使用哪一把),他只是负责对别人提供宝刀,具体是谁需要什么样的宝刀,是由借刀的人决定的!看,这不就是我们本章所要讲述的工厂方法设计模式吗?定义一个创建产品对象的工厂接口(大司徒王允就是负责提供生产宝刀的接口),让子类决定实例化哪一种实例对象(曹操就是一个具体工厂,他决定了要使用七星宝刀,即生产七星宝刀对象实例),也就是将实际创建实例对象的工作推迟到子类当中,核心工厂类不再负责具体产品的创建。

图2-3 抽象化的关系图

2.3.3 工厂方法模式的静态建模

人物和宝刀之间的关系我们都已经理顺了,接下来,我们对工厂方法模式进行静态化建模。通过静态类图加深对工厂方法模式的理解。我们还是以故事为根据来实现静态化类图,在图2-3中,可以看到涉及了以下内容:

大司徒王允—抽象工厂

各类宝刀—抽象产品

校尉曹操—具体工厂

七星宝刀—具体产品

各个人物和事物都清晰地对应工厂方法模式中的各个角色。下面,我们来设计故事中对应工厂方法模式角色的各个接口或者类,如下所示:

大司徒王允—ISwordFactory(生产宝刀的抽象工厂)

各类宝刀—AbstractSword(抽象产品)

校尉曹操—Caocao(具体工厂生产七星宝刀)

七星宝刀—QixingSword(具体产品七星宝刀)

ISwordFactory抽象工厂中应该含有一个生产抽象产品AbstractSword的接口方法,如createSword,该方法的返回值类型是抽象产品AbstractSword类型。

抽象产品AbstractSword中应该含有一个宝刀名称的属性及其get、set方法,如name。

Caocao具体工厂就是负责生产具体产品七星宝刀的,它实现了ISwordFactory抽象工厂接口方法createSword,返回具体类型QixingSword。

具体产品QixingSword就是抽象产品AbstractSword的一个子类。

静态类图结构如图2-4所示(图中斜体部分表示无方法体的方法声明,即无方法体的抽象方法或者接口方法。书中各图同此)。

图2-4 故事中的静态类图

图2-4所描述的内容就是:抽象工厂ISwordFactory生产抽象产品AbstractSword,具体工厂Caocao生产具体产品QixingSword;具体工厂Caocao实现抽象工厂ISwordFactory接口,具体产品QixingSword是抽象产品的一个子类。这也就是我们故事当中蕴涵的工厂方法设计模式!

综上所述,你对工厂方法设计模式应该了然于胸了吧!如果你已经理解了图2-4所表示的含义,那么下面所要进行的编码工作就是非常简单的事情了。

2.4 故事的工厂方法模式实现

下面,我们就来通过具体编码实现故事中的情节。相信这将是一件非常有趣的事,因为,你可以使用代码编写历史了哈!

使用Eclipse创建Java工程“FactoryMethodDemo”(不会创建Java工程?去看看《Eclipse实用教程》吧,一步一步教你学会为止)。

首先,我们确立一下工程的结构。

(1)创建一个系统级包“com.demo.factory”,用于存放所有文件;

(2)在“factory”包中创建“itf”包,用于存放接口类;

(3)在“factory”包中创建“model”包,用于存放抽象产品类;

(4)在“model”包中创建“object”包,用于存放具体产品类。

工程结构如图2-5所示。

图2-5 工程结构图

下面,我们就来使用工厂方法设计模式进行系统编码!

2.4.1 建立产品

1.抽象宝刀——AbstractSword

首先,我们需要建立一个抽象宝刀类AbstractSword(为什么首先建立呢?因为,曹操最想要的就是宝刀了),因为在抽象工厂中需要生产抽象产品——宝刀!在抽象宝刀类中含有一个name属性,代表宝刀的名称。AbstractSword类的内容如下所示:

2.具体宝刀——QixingSword七星宝刀

具体工厂需要生产具体产品,所以,接下来定义具体产品—七星宝刀类QixingSword。该类继承抽象宝刀类AbstractSword,并且在构造方法中设置宝刀的名称为“七星宝刀”,内容如下所示:

        package com.demo.factory.model.object;
        import com.demo.factory.model.AbstractSword;
        /**
         * 七星宝刀类
         *
         * @author
         *
        */
       public class QixingSword extends AbstractSword
       {
            /**
            * 构造方法设置宝刀的名称
            */
            public QixingSword()
            {
                this.setName("七星宝刀");
            }
       }

2.4.2 建立工厂

1.抽象宝刀工厂——ISwordFactory生产抽象宝刀

抽象产品和具体产品都已经有了,下面就应该定义抽象工厂ISwordFactory了。抽象工厂中只有一个接口方法createSword,返回值类型是抽象产品类型AbstractSword。该接口的目的是将生产具体产品的控制权交给具体工厂,这样的话,如果有需要更多其他宝刀的人(具体工厂),就会更容易扩展,扩展时也不会对抽象工厂的接口方法产生任何影响,更不会对其他的具体工厂产生任何影响。

ISwordFactory抽象工厂内容如下所示:

抽象工厂也定义好了,下面就要定义具体工厂了。我们的曹操就要出场!

2.具体宝刀工厂——Caocao生产七星宝刀

具体工厂Caocao就是负责生产七星宝刀的,其他的宝刀Caocao是不需要的。在Caocao具体工厂中实现了ISwordFactory抽象工厂的createSword接口方法,返回具体宝刀对象实例—QixingSword。内容如下所示:

现在,万事俱备,所有的创建工作都已经完成。接下来,让我们来设计曹操刺杀董卓的剧情吧!

2.4.3 故事情节的历史重现

在“factory”包中创建主应用程序“MainApp”,开始我们的刺杀剧情!在主应用程序中,首先创建一个曹操(Caocao类实例对象,但是是ISwordFactory接口类型的,面向接口编程),然后曹操获得七星宝刀,最后刺杀董卓!MainApp内容如下所示:

        package com.demo;
        import com.demo.factory.Caocao;
        import com.demo.factory.itf.ISwordFactory;
        import com.demo.factory.model.AbstractSword;
        public class MainApp
        {
            /**
            * @param args
            */
            public static void main(String[] args)
            {
                // 创建曹操实例对象(返回使用接口类型 ISwordFactory)
                ISwordFactory swordFactory = new Caocao();
                // 获得宝刀实例—七星宝刀
                AbstractSword sword = swordFactory.createSword();
                // 刺杀董卓
                System.out.println("曹操使用" + sword.getName() + "刺杀董卓!");
            }
        }

看,曹操刺杀董卓的过程很清晰吧!主应用程序的运行结果如下所示:

我们反过来再看一下,曹操获得七星宝刀的过程:swordFactory.createSword()返回的是AbstractSword抽象类型,在抽象类AbstractSword中还记得有什么内容吗?一个name的名称属性及其set和get方法。swordFactory在构造实例对象的时候使用的是new Caocao(),对吧?这个时候,swordFactory.createSword()就是调用的Caocao类中的createSword()方法:return new QixingSword();就是七星宝刀!因此,在MainApp主应用程序中,输出的是“……七星宝刀……”。

2.4.4 用八星宝刀来考验模式

如果,曹操使用的不是七星宝刀,而是八星宝刀,程序该如何修改呢?在对原有系统进行升级的时候,我们要把握一个原则:保证原有系统的稳定性!也就是说,新增加的内容不要对原有内容造成影响。那么,该如何对系统进行改造呢?

下面,我们就来看一下,如果是八星宝刀,程序该如何改造,以及修改所涉及的范围。我们要保证原有系统的稳定性,因此,对于原来的内容不做任何修改,只是新增加一些内容。

1.创建八星宝刀——BaxingSword

八星宝刀又是一个抽象宝刀的子类,因此需要新建一个BaxingSword类,继承AbstractSword抽象宝刀。该类和七星宝刀类似,就是在构造方法中设置宝刀名称,内容如下所示:

        package com.demo.factory.model.object;
        import com.demo.factory.model.AbstractSword;
        /**
         * 八星宝刀类
         *
         * @author
         *
         */
        public class BaxingSword extends AbstractSword
        {
            /**
            * 构造方法设置宝刀的名称
            */
            public BaxingSword()
            {
                this.setName("八星宝刀");
            }
        }

2.创建具体宝刀工厂——Caocao2生产八星宝刀

除此之外,我们还需要新增加一个Caocao2具体工厂类。同样是实现ISwordFactory接口方法createSword,这次返回的不是QixingSword类型对象,而是返回BaxingSword类型对象实例!内容如下所示:

        package com.demo.factory;
        import com.demo.factory.itf.ISwordFactory;
        import com.demo.factory.model.AbstractSword;
        import com.demo.factory.model.object.BaxingSword;
        /**
         * 使用八星宝刀的曹操具体工厂
         *
         * @author
         *
         */
        public class Caocao2 implements ISwordFactory
        {
            // 生产八星宝刀
            public AbstractSword createSword()
            {
                return new BaxingSword();
            }
        }

3.让曹操使用八星宝刀刺杀董卓

然后,我们再看看主应用程序MainApp需要做怎样的处理。在MainApp主应用程序中,是使用ISwordFactory swordFactory = new Caocao();创建一个曹操对象实例的,该曹操使用的是七星宝刀。现在,假设历史发生了变化,当时八星宝刀才是最锋利的宝刀,这时曹操需要的是八星宝刀,而不是之前的七星宝刀了。好,这时我们就应该创建Caocao2实例对象了!新的曹操类使用的是八星宝刀!修改后的MainApp内容如下所示:

        package com.demo;
        import com.demo.factory.Caocao2;
        import com.demo.factory.itf.ISwordFactory;
        import com.demo.factory.model.AbstractSword;
        public class MainApp
        {
            /**
            * @param args
            */
            public static void main(String[] args)
            {
                // 创建曹操实例对象(返回使用接口类型 ISwordFactory)
                // ISwordFactory swordFactory = new Caocao();
                ISwordFactory swordFactory = new Caocao2();
                // 获得宝刀实例—八星宝刀
                AbstractSword sword = swordFactory.createSword();
                // 刺杀董卓
                System.out.println("曹操使用" + sword.getName() + "刺杀董卓!");
            }
        }

我们使用ISwordFactory swordFactory = new Caocao2();就把曹操换成了使用八星宝刀的曹操!OK,其他的什么都不需要修改了!

运行MainApp主应用程序,结果如下所示:

从以上“改变历史”的过程中,我们发现,原有的内容包括ISwordFactory、AbstractSword、Caocao和QixingSword都没有发生任何变化,我们新增加了两个内容,一个是八星宝刀实现类BaxingSword,另一个是生产八星宝刀的具体工厂Caocao2。在主应用程序调用的时候,只要将工厂实例化成Caocao2,那么就变成了曹操使用八星宝刀刺杀董卓。但是,之前曹操使用七星宝刀的过程还在,我们没有做任何修改(保证了原有系统的稳定性)!只是在这里,我们扩展了故事情节!

在这一系列的变化过程中,应用的是程序设计语言中的多态方式,使用接口调用对象实例的具体方法,获得不同对象的内容,而对外部是表现一致的(相同的方法),这也是面向接口编程的优点。

像这样不在工厂中产生具体的对象实例,而是将实例化的工作推迟到工厂子类中,这种模式被称做工厂方法模式。

示例工程的完整结构如图2-6所示。

图2-6 示例工程完整结构图

2.5 设计原则

1.“开-闭”原则

首先了解一个软件设计的原则—“开-闭”原则。所谓“开-闭”原则,就是指一个软件实体应对扩展开放,对修改关闭。它所阐述的意思就是,在设计一个软件模块的时候应该使这个模块可以在不被修改的前提下被扩展。

“开-闭”原则具备以下优势:

(1)通过已有系统扩展自身的行为,从而满足新的软件需求,具有一定的适应性和灵活性。

(2)原有的软件系统逻辑不被修改,保证了原系统和新系统的稳定性。

工厂方法模式很好地阐述了“开-闭”原则,工厂方法模式的抽象,让子类决定实例化哪一种实例对象,具有很大的适应性和灵活性;具体产品对象是在子类工厂中产生的,没有修改任何父类工厂的代码,因此保证了系统的稳定性。

例如,本例中我们模拟曹操使用八星宝刀刺杀董卓的过程,就很好地体现了“开-闭”原则,这也是工厂方法设计模式的魅力所在!正是将创建具体对象实例的工作推迟到了子类工厂中实现,所以才有效地将工厂和具体类型解耦。需要新的具体产品类型,就需要一个与之相应的具体工厂来生产这个具体产品。工厂方法设计模式在保证系统稳定的前提下,很容易实现系统扩展,灵活有效地应对外部需求的变化。

“开-闭”原则也是软件设计开发中必须考虑的设计原则,它的设计理念是每一个软件开发者必须掌握的,读者一定要认真地领悟、体会这项设计原则,做到灵活运用。通过本章对工厂方法设计模式的学习,希望能够对读者起到一个启迪的作用,使软件系统的结构设计更加具有弹性!

2.依赖倒置原则

所谓依赖倒置原则,就是不论工厂还是产品都应该依赖于抽象,而不是具体的实现类。听起来更像是“针对接口编程,而不是针对实现编程”,但是这里依赖倒置原则更强调“抽象”的概念,不要让高层组件依赖低层组件,更不能依赖具体实现类,都要依赖于抽象。

可能会有读者对于“倒置”的概念很模糊,什么是“倒置”呢?依赖倒置原则中的倒置是指我们的思想要和一般的OO设计思想相反。通常设计一个系统,都是从顶端开始,然后到具体实现类,例如:在工厂中要生产一个产品,但是又不想在工厂中与具体的实现类存在任何关系,否则就对实现类产生了依赖,这是我们不希望得到的结果,此时我们就需要把思想倒置一下,不要从顶端(抽象)开始,而是从具体的实现类开始,看看能抽象出什么,然后一切都依赖于抽象来进行,这样就与我们的目标越来越近了。

在面向过程的软件设计开发中,往往是高层组件调用低层组件,这样的话,高层组件就会依赖于低层组件,当低层组件发生剧烈变动时,高层组件也要跟着变动,就会导致模块的复用性大大降低,并且大大提高开发的成本,也增加了软件维护的复杂度。如图2-7所示,为面向过程软件设计开发的调用结构图。

图2-7 面向过程软件设计开发的调用结构图

从图2-7中可以看出,高层组件调用低层组件,即高层组件依赖于低层组件,当低层组件发生变化时,势必会对高层组件产生影响。

如何改变这种状况呢?面向对象的软件设计开发模式很好地解决了这个问题,一般情况下抽象的变化概率很小,让用户程序依赖于抽象,实现的细节也依赖于抽象。即使实现细节不断变动,只要抽象不变,客户程序就不需要变化。这大大降低了客户程序与实现细节的耦合度,如图2-8所示。

图2-8 面向对象软件设计模式——依赖抽象

这样一来,无论低层组件怎样变化,只要抽象组件不发生改变,高层组件就不会发生变化,实现了客户程序与实现细节的解耦。

我们再回过头看看刚刚设计的曹操使用八星宝刀刺杀董卓的过程,想想有哪些地方存在不妥呢?对了,我们修改了MainApp主应用程序内容,这说明客户程序和具体工厂之间还存在着一定的耦合性。我们在设计中假设了两种曹操刺杀董卓的情况,其实,曹操只有一个,我们只需要在Caocao类中返回BaxingSword实例对象就行了,这样,客户程序MainApp就不需要进行任何修改了。

在工厂方法模式中,存在这样几个角色:

(1)抽象工厂;

(2)抽象产品;

(3)具体工厂;

(4)具体产品。

我们可以这样理解工厂方法设计模式:

(1)抽象工厂生产抽象产品(ISwordFactory生产AbstractSword),具体工厂生产具体产品(Caocao类生产QixingSword);

(2)抽象对具体(具体类实现抽象类,如Caocao类实现ISwordFactory;QixingSword继承AbstractSword)。

掌握以上这样的几个设计原则,我们在软件设计中就可以得心应手地使用工厂方法模式了。

2.6 使用场合

(1)当子类型可能会有很多,以后需要不断增添不同的子类实现时;

(2)当一个系统尚在框架设计阶段,还不知道将来需要实例化哪些具体类时;

(3)系统设计之初不需要具体对象的概念(或者说没有具体对象的概念)。

以上几种情况比较适合工厂方法设计模式。通过子类来创造对象,工厂方法模式在此过程中负责将客户端从具体类型中解耦,客户端只需要知道他们所使用对象的抽象类型就可以了,而不必关心具体的对象类型,具体的对象类型由工厂子类负责创建。

工厂方法设计模式如图2-9所示。

图2-9 工厂方法设计模式

工厂方法模式通过继承的方式实现应用程序的解耦,让子类决定实例化哪一个具体类型。如果在软件系统设计中,你发现可能会有很多的具体产品产生,但是目前又处于一种不确定的因素,这时使用工厂方法模式应该是最好的选择。这样,你就可以使用上面的工厂方法模式类图实现你的软件系统,让你的系统更加灵活、易于扩展!

在进行软件系统设计的时候,我们需要用战略的眼光来看待问题,所谓战略眼光就是需要把眼光放长远,不要只局限于目前的系统结构,要考虑到以后的若干情况,如子类型是不是会有很多、如何对系统进行扩展等。对于一个系统分析师或者一个软件设计开发人员来说,一定要把握一个原则,那就是把软件系统做成一个易扩展的、能够适应变化的系统,因为所有的软件系统都不是一成不变的,如何让我们的软件系统适应需求的变化,是我们在进行软件设计时需要思考的问题。

扩展:Java SDK中的工厂方法模式

工厂方法设计模式在很多场合都有应用,使用得非常广泛,如在Java SDK中就存在工厂方法设计模式的影子。例如:java.util.List、java.util.LinkedList、java.util.ArrayList就应用了工厂方法设计模式。

java.util.List作为一个抽象接口工厂,其中定义了一个iterator()接口方法,该方法返回一个迭代器接口Iterator(抽象产品),用于循环遍历List中的各个对象。子类java.util.LinkedList实现了java.util.List接口方法iterator(),返回具体的迭代器类型AbstractList$ListItr(具体产品),ListItr类作为AbstractList的内部私有类使用。同样,java.util.ArrayList也是java.util.List类的一个子类,实现了其iterator()接口方法,返回具体的迭代器AbstractList$Itr(具体产品), Itr类也是作为AbstractList的内部私有类使用,如图2-10所示。

图2-10 java.util.List工厂方法模式

2.7 本章要点

1.工厂方法模式

定义一个创建产品对象的工厂接口,让子类决定实例化哪一种实例对象,也就是将实际创建实例对象的工作推迟到子类当中,核心工厂类不再负责具体产品的创建。

2.设计原则

(1)“开-闭”原则:一个软件实体应对扩展开放,对修改关闭。我们在设计软件模块的时候应该使这个模块可以在不被修改的前提下被扩展。

(2) 依赖倒置原则:不论工厂还是产品都应该依赖于抽象,而不是具体的实现类。

3.工厂方法模式的使用场合

(1)当子类型可能会有很多,以后需要不断增添不同的子类实现时;

(2)当一个系统尚在框架设计阶段,还不知道将来需要实例化哪些具体类时;

(3)系统设计之初不需要具体对象的概念(或者说没有具体对象的概念)。