面向对象设计的设计模式(一):简单工厂模式

继上一篇的面向对象设计的设计原则,本篇是面向对象设计系列的第二个部分:面向对象设计的设计模式。

介绍设计模式最著名的一本书莫属《设计模式 可复用面向对象软件的基础》这本书,书中共介绍了23个设计模式。而这些设计模式分为三大类,每个类别都包含几个设计模式:

  • 创建型设计模式:侧重于对象的创建。
  • 结构型设计模式:侧重于接口的设计和系统的结构。
  • 行为型设计模式:侧重于类或对象的行为。

本系列对设计模式的讲解按照上面分类排列的顺序进行。而且从多维度来理解一件事物有助于更深刻地理解它,因此每个设计模式我都会从以下这几点来讲解:

  • 定义
  • 使用场景
  • 成员与类图
  • 代码示例
  • 优点
  • 缺点
  • iOS SDK 和 JDK中的应用

最后一项:“iOS SDK 和 JDK中的应用”讲解的是该设计模式在Objective-C和java语言(JDK)中的应用。

本篇是面向对象设计中面向对象设计的第一篇文章,讲解的是简单工厂模式。

注意:简单工厂模式不是 GoF总结出来的23种设计模式之一,不存在于《设计模式 可复用面向对象软件的基础》这本书中。

简单工厂模式

定义

简单工厂模式(Simple Factory Pattern):专门定义一个类(工厂类)来负责创建其他类的实例。可以根据创建方法的参数来返回不同类的实例,被创建的实例通常都具有共同的父类。

简单工厂模式又称为静态工厂方法(Static Factory Method)模式,它属于类创建型模式。

适用场景

如果我们希望将一些为数不多的类似的对象的创建和他们的创建细节分离开,也不需要知道对象的具体类型,可以使用简单工厂模式。

举个形象点的例子:在前端开发中,常常会使用外观各式各样的按钮:比如有的按钮有圆角,有的按钮有阴影,有的按钮有边框,有的按钮无边框等等。但是因为同一种样式的按钮可以出现在项目的很多地方,所以如果在每个地方都把创建按钮的逻辑写一遍的话显然是会造成代码的重复(而且由于业务的原因有的按钮的创建逻辑能比较复杂,代码量大)。

那么为了避免重复代码的产生,我们可以将这些创建按钮的逻辑都放在一个“工厂”里面,让这个工厂来根据你的需求(传入的参数)来创建对应的按钮并返回给你。这样一来,同样类型的按钮在多个地方使用的时候,就可以只给这个工厂传入其对应的参数并拿到返回的按钮即可。

下面来看一下简单工厂模式的成员和类图。

成员与类图

成员

简单工厂模式的结构比较简单,一共只有三个成员:

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

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

模式类图

简单工厂模式类图

从类图中可以看出,工厂类提供一个静态方法:通过传入的字符串来制造其所对应的产品。

代码示例

场景概述

举一个店铺售卖不同品牌手机的例子:店铺,即客户端类向手机工厂购进手机售卖。

场景分析

该场景可以使用简单工厂的角色来设计:

  • 抽象产品:Phone,是所有具体产品类的父类,提供一个公共接口packaging表示手机的装箱并送到店铺。
  • 具体产品:不同品牌的手机,iPhone手机类(IPhone),小米手机类(MIPhone),华为手机类(HWPhone)。
  • 工厂:PhoneFactory根据不同的参数来创建不同的手机。
  • 客户端类:店铺类Store负责售卖手机。

代码实现

抽象产品类Phone

//================== Phone.h ==================
@interface Phone : NSObject

//package to store
- (void)packaging;

@end

具体产品类 IPhone

//================== IPhone.h ==================
@interface IPhone : Phone

@end


//================== IPhone.m ==================
@implementation IPhone

- (void)packaging{
    NSLog(@"IPhone has been packaged");
}

@end

具体产品类 MIPhone

//================== MIPhone.h ==================
@interface MIPhone : Phone

@end



//================== MIPhone.m ==================
@implementation MIPhone

- (void)packaging{
    NSLog(@"MIPhone has been packaged");
}

@end

具体产品类:HWPhone:

//================== HWPhone.h ==================
@interface HWPhone : Phone

@end



//================== HWPhone.m ==================
@implementation HWPhone

- (void)packaging{
    NSLog(@"HUAWEI Phone has been packaged");
}

@end

以上是抽象产品类以及它的三个子类:苹果手机类,小米手机类和华为手机类。 下面看一下工厂类 PhoneFactory

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

+ (Phone *)createPhoneWithTag:(NSString *)tag;

@end


//================== PhoneFactory.m ==================
#import "IPhone.h"
#import "MIPhone.h"
#import "HWPhone.h"

@implementation PhoneFactory

+ (Phone *)createPhoneWithTag:(NSString *)tag{

    if ([tag isEqualToString:@"i"]) {

        IPhone *iphone = [[IPhone alloc] init];
        return iphone;

    }else if ([tag isEqualToString:@"MI"]){

        MIPhone *miPhone = [[MIPhone alloc] init];
        return miPhone;

    }else if ([tag isEqualToString:@"HW"]){

        HWPhone *hwPhone = [[HWPhone alloc] init];
        return hwPhone;

    }else{

        return nil;
    }
}

@end

工厂类向外部(客户端)提供了一个创造手机的接口createPhoneWithTag:,根据传入参数的不同可以返回不同的具体产品类。因此客户端只需要知道它所需要的产品所对应的参数即可获得对应的产品了

在本例中,我们声明了店铺类 Store为客户端类:

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

@interface Store : NSObject

- (void)sellPhone:(Phone *)phone;

@end


//================== Store.m ==================
@implementation Store

- (void)sellPhone:(Phone *)phone{
    NSLog(@"Store begins to sell phone:%@",[phone class]);
}

@end

客户端类声明了一个售卖手机的接口sellPhone:。表示它可以售卖作为参数所传入的手机。

最后我们用代码模拟一下这个实际场景:

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


//1. A phone store  wants to sell iPhone
Store *phoneStore = [[Store alloc] init];

//2. create phone
Phone *iPhone = [PhoneFactory  createPhoneWithTag:@"i"];

//3. package phone to store
[iphone packaging];

//4. store sells phone after receving it
[phoneStore sellPhone:iphone];

上面代码的解读:

  1. 最开始实例化一个商店,商店打算卖苹果手机
  2. 商店委托工厂给他制作一台iPhone手机,传入对应的字段i
  3. 手机生产好以后打包送到商店
  4. 商店售卖手机

在这里我们需要注意的是:商店从工厂拿到手机不需要了解手机制作的过程,只需要知道它要工厂做的是手机(只知道Phone类即可),和需要给工厂类传入它所需手机所对应的参数即可(这里的iPhone手机对应的参数就是i)。

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

代码对应的类图

简单工厂模式代码示例类图

优点

  • 客户端只需要给工厂类传入一个正确的(约定好的)参数,就可以获取你所需要的对象,而不需要知道其创建细节,一定程度上减少系统的耦合。
  • 客户端无须知道所创建的具体产品类的类名,只需要知道具体产品类所对应的参数即可,减少开发者的记忆成本。

缺点

  • 如果业务上添加新产品的话,就需要修改工厂类原有的判断逻辑,这其实是违背了开闭原则的。
  • 在产品类型较多时,有可能造成工厂逻辑过于复杂。所以简单工厂模式比较适合产品种类比较少而且增多的概率很低的情况。

iOS SDK 和 JDK中的应用

  • Objective-C中的类簇就是简单工厂设计模式的一个应用。如果给NSNumber的工厂方法传入不同类型的数据,则会返回不同数据所对应的NSNumber的子类。
  • JDK中的Calendar类中的私有的createCalendar(TimeZone zone, Locale aLocale)方法通过不同的入参来返回不同类型的Calendar子类的实例。

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

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

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

发表于

我来说两句

0 条评论
登录 后参与评论

扫码关注云+社区

领取腾讯云代金券