面向对象设计的设计模式(二):工厂方法模式

继上一篇简单工厂模式,本篇讲解的是创建型设计模式中的工厂方法模式。

定义

工厂方法模式(Factory Method Pattern)又称为工厂模式,工厂父类负责定义创建产品对象的公共接口,而工厂子类则负责生成具体的产品对象,即通过不同的工厂子类来创建不同的产品对象。

适用场景

工厂方法模式的适用场景与简单工厂类似,都是创建数据和行为比较类似的对象。但是和简单工厂不同的是:在工厂方法模式中,因为创建对象的责任移交给了抽象工厂的子类,因此客户端需要知道其所需产品所对应的工厂子类,而不是简单工厂中的参数。

下面我们看一下工厂方法模式的成员和类图。

成员与类图

成员

工厂方法模式包含四个成员:

  1. 抽象工厂(Abstract Factory):抽象工厂负责声明具体工厂的创建产品的接口。
  2. 具体工厂(Concrete Factory):具体工厂负责创建产品。
  3. 抽象产品(Abstract Product):抽象产品是工厂所创建的所有产品对象的父类,负责声明所有产品实例所共有的公共接口。
  4. 具体产品(Concrete Product):具体产品是工厂所创建的所有产品对象类,它以自己的方式来实现其共同父类声明的接口。

下面通过类图来看一下各个成员之间的关系:

模式类图

工厂方法模式类图

从类图中我们可以看到:抽象工厂负责定义具体工厂必须实现的接口,而创建产品对象的任务则交给具体工厂,由特定的子工厂来创建其对应的产品。

这使得工厂方法模式可以允许系统在不修改原有工厂的情况下引进新产品:只需要创建新产品类和其所对应的工厂类即可。

代码示例

场景概述

同样也是模拟上面的简单工厂例子中的场景(手机商店卖手机),但是由于这次是由工厂方法模式来实现的,因此在代码设计上会有变化。

场景分析

与简单工厂模式不同的是:简单工厂模式里面只有一个工厂,而工厂方法模式里面有一个抽象工厂和继承于它的具体工厂。

因此同样的三个品牌的手机,我们可以通过三个不同的具体工厂:苹果手机工厂(IPhoneFactory),小米手机工厂 (MIPhoneFactory),华为手机工厂(HWPhoneFactory)来生产。而这些具体工厂类都会继承于抽象手机工厂类:PhoneFactory,它来声明生产手机的接口。

下面我们用代码来具体来看一下工厂类(抽象工厂和具体工厂)的设计:

代码实现

首先我们声明一个抽象工厂类 PhoneFactory

//================== PhoneFactory.h ==================
#import "Phone.h"

@interface PhoneFactory : NSObject

+ (Phone *)createPhone;

@end


//================== PhoneFactory.m ==================
@implementation PhoneFactory

+ (Phone *)createPhone{
    //implemented by subclass
    return nil;
}

@end

抽象工厂类给具体工厂提供了生产手机的接口,因此不同的具体工厂可以按照自己的方式来生产手机。下面看一下具体工厂:

苹果手机工厂 IPhoneFactory

//================== IPhoneFactory.h ==================
@interface IPhoneFactory : PhoneFactory
@end


//================== IPhoneFactory.m ==================
#import "IPhone.h"

@implementation IPhoneFactory

+ (Phone *)createPhone{

    IPhone *iphone = [[IPhone alloc] init];
    NSLog(@"iPhone has been created");
    return iphone;
}

@end

小米手机工厂 MIPhoneFactory

//================== MIPhoneFactory.h ==================
@interface MPhoneFactory : PhoneFactory

@end



//================== MIPhoneFactory.m ==================
#import "MiPhone.h"

@implementation MPhoneFactory

+ (Phone *)createPhone{

    MiPhone *miPhone = [[MiPhone alloc] init];
    NSLog(@"MIPhone has been created");
    return miPhone;
}

@end

华为手机工厂 HWPhoneFactory:

//================== HWPhoneFactory.h ==================
@interface HWPhoneFactory : PhoneFactory

@end



//================== HWPhoneFactory.m ==================
#import "HWPhone.h"

@implementation HWPhoneFactory

+ (Phone *)createPhone{

    HWPhone *hwPhone = [[HWPhone alloc] init];
    NSLog(@"HWPhone has been created");
    return hwPhone;
}

@end

以上就是声明的抽象工厂类和具体工厂类。因为生产手机的责任分配给了各个具体工厂类,因此客户端只需要委托所需手机所对应的工厂就可以获得其生产的手机了。

因为抽象产品类Phone和三个具体产品类(IPhoneMIPhoneHWPhone)和简单工厂模式中介绍的例子中的一样,因此这里就不再重复介绍了。

下面我们用代码模拟一下该场景:

//================== Using by client ==================


//A phone store
Store *phoneStore = [[Store alloc] init];

//phoneStore wants to sell iphone
Phone *iphone = [IPhoneFactory  createPhone];
[iphone packaging];
[phoneStore sellPhone:iphone];


//phoneStore wants to sell MIPhone
Phone *miPhone = [MPhoneFactory createPhone];
[miPhone packaging];
[phoneStore sellPhone:miPhone];

//phoneStore wants to sell HWPhone
Phone *hwPhone = [HWPhoneFactory createPhone];
[hwPhone packaging];
[phoneStore sellPhone:hwPhone];

由上面的代码可以看出:客户端phoneStore只需委托iPhone,MIPhone,HWPhone对应的工厂即可获得对应的手机了。

而且以后如果增加其他牌子的手机,例如魅族手机,就可以声明一个魅族手机类和魅族手机的工厂类并实现createPhone这个方法即可,而不需要改动原有已经声明好的各个手机类和具体工厂类。

下面我们看一下该例子对应的 UML类图,可以更直观地看一下各个成员之间的关系:

代码对应的类图

工厂方法模式代码示例类图

优点

  • 用户只需要关心其所需产品对应的具体工厂是哪一个即可,不需要关心产品的创建细节,也不需要知道具体产品类的类名。
  • 当系统中加入新产品时,不需要修改抽象工厂和抽象产品提供的接口,也无须修改客户端和其他的具体工厂和具体产品,而只要添加一个具体工厂和与其对应的具体产品就可以了,符合了开闭原则(这一点与简单工厂模式不同)。

缺点

  • 当系统中加入新产品时,除了需要提供新的产品类之外,还要提供与其对应的具体工厂类。因此系统中类的个数将成对增加,增加了系统的复杂度。

iOS SDK 和 JDK 中的应用

  • 暂未发现iOS SDK中使用工厂方法的例子,有知道的小伙伴欢迎留言。
  • 在JDK中,Collection接口声明了iterator()方法,该方法返回结果的抽象类是IteratorArrayList就实现了这个接口;,而ArrayList对应的具体产品是Itr

原文发布于微信公众号 - 程序员Gank(programmer_gank)

原文发表时间:2019-06-21

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

扫码关注云+社区

领取腾讯云代金券