概述
创建型模式,就是用来创建对象的模式,它抽象了对象的创建过程。本系列文章介绍了Gof设计模式中的5种创建型模式,另外对简单工厂模式也进行了介绍,下面通过一个表格来说明它们之间的关系。
名称 | GoF的定义 | 功能描述 |
Singleton | 保证一个类仅有一个实例,并提供一个该实例的全局访问点。 | 解决的是类实例化个数的问题,严格控制实体对象的数量 |
Simple Factory | 非23种Gof模式,没有Gof定义(个人定义:专门定义一个类来负责创建其他类的实例,被创建的实例常常具有共同的父类。) | 解决的是“某个对象”的创建工作,由于需求的变化,这个对象的具体实现常常面临着剧烈的变化,但是这个对象拥有的接口相对稳定。简单工厂实现了客户端和对象创建的解耦。 |
Factory Method | 定义创建对象的接口,让子类决定实例化哪一个类。工厂方法使得一个类的实例化延迟到其子类。 | 功能和简单工厂一样,它具有简单工厂的优点,并规避了简单工厂的缺点(违反“开放-关闭原则”) |
Abstract Factory | 提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。 | 抽象工厂解决了一个系列易变对象的创建问题 |
Builder | 将一个复杂对象的构建与它的表现分离,使得同样的构建过程可以创建不同的表现 | 应对项目中一些复杂对象的创建工作。所谓“复杂对象”,是指对象中还含有其它的子对象。 |
Prototype | 使用原型实例指定创建对象的种类,并通过复制这个原型创建新的对象 | 解决了某些结构复杂的对象的创建工作。由于需求的变化,这些对象经常面临着剧烈的变化,但是它们却拥有比较稳定一致的接口,原型模式通过原型(可以理解为一个特殊的工厂类)来克隆易变对象 |
为什么需要创建型模式
所有的创建型模式都有两个永恒的主题:第一,它们都将系统使用哪些具体类的信息封装起来;第二,它们隐藏了这些类的实例是如何被创建和组织的。外界对于这些对象只知道它们共同的接口,而不清楚其具体的实现细节。正因如此,创建型模式在创建什么(what)、由谁(who)创建、以及何时(when)创建这些方面,都为软件设计者提供了尽可能大的灵活性。下面通过应用场景来说明。
假设现在要开发一个游戏,游戏中会用到一个现代风格房屋的对象,我们一般会使用如下代码来创建:
ModernRoom room = [[ModernRoom alloc] init];
这样一个现代风格的房屋对象就创建好了。但现在由于客户需求的变化,客户方要求创建一个古典风格的房屋,那么我们就需要将上面的代码修改为:
ClassicalRoom room = [[ClassicalRoom alloc] init];
从代码看也没什么问题,但大家可以试想一下:在我们的程序中可能有很多地方使用了这样风格的创建代码,这里仅仅是假设房屋的风格变化,就需要修改程序中所有的房屋风格创建代码,那么其他的话,大家可以想象一下修改的工作量。现在有了创建型模式,我们对对象创建过程采用工厂方法模式封装,把对象的创建放在一个工厂方法中,客户端调用的代码可能如下:
// 这个地方的@" ModernRoomFactory "可以写到配置文件中 id<Factory> factory = [[[NSClassFromString(@"ModernRoomFactory") alloc] init] autorelease]; id<IRoom> chart = [factory createRoom]; // 其他操作......
当房屋的风格变化时,我们只需要修改配置文件中@" ModernRoomFactory ",代码不做任何改动就可以了。这就是我们为什么需要创建型模式。创建者模式作用可以概括为如下两点:
- 封装创建逻辑,绝不仅仅是alloc一个对象那么简单。
- 封装创建逻辑变化,客户代码尽量不修改,或尽量少修改。
如何使用创建型模式
我们继续上面的游戏开发场景:假定在游戏中我们需要用到墙(Wall)、屋子(Room)、门(Door)等部件。同样是对象的创建问题,但是会根据所要解决的问题不同而使用不同的创建型模式。
假定一个屋子只允许有一个门存在,那么这就是一个使用Signleton模式的例子,确保只有一个Door类的实例被创建,解决的是对象创建个数的问题。
在游戏中需要创建墙、屋子的实例时,为了避免直接对构造器的调用而实例化类,也是应对这些类可能有不同表现的问题,这时我们会使用简单工厂模式或工厂方法模式,每一个部件都有它自己的工厂类,解决的是“单个对象”的需求变化问题。
更进一步,在游戏场景中,不可能只有一种风格的墙或屋子,可能有现代风格的(Modern)、古典风格(Classical)等其他系列风格的部件。这时就是一系列对象的创建问题了,可以使用抽象工厂模式,解决的是“系列对象”的需求变化问题。
现在再考虑一下:我们知道一个屋子一般由4面墙、一扇窗户、一张门、一个天花板构成,但具体用什么风格的墙、窗户、门、天花板是不断变化的,这时我们可以用生成器模式,解决的是“对象部分”的需求变化问题。
如果在游戏中,需要大量的古典风格或现代风格的墙或屋子,这时我们可以通过拷贝一个已有的原型对象来生成新对象,这就是原型模式。通过克隆来解决“易变对象”的创建问题。
究竟选用哪一种模式,取决于很多的因素。使用Abstract Factory、Prototype或Builder的设计比使用Factory Method的设计更加灵活,但是也更加复杂,尤其Abstract Factory需要庞大的工厂类来支持。通常,设计以使用Factory Method开始,当需要更大的灵活性时,设计便会向其他设计模式演化,了解多个设计模式,可以让你有更多的选择余地。