前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >面向对象设计的设计模式(三):抽象工厂模式

面向对象设计的设计模式(三):抽象工厂模式

作者头像
用户2932962
发布于 2019-01-07 09:24:37
发布于 2019-01-07 09:24:37
1K00
代码可运行
举报
文章被收录于专栏:程序员维他命程序员维他命
运行总次数:0
代码可运行

定义

抽象工厂模式(Abstract Factory Pattern):提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。

适用场景

有时候我们需要一个工厂可以提供多个产品对象,而不是单一的产品对象。比如系统中有多于一个的产品族,而每次只使用其中某一产品族,属于同一个产品族的产品将在一起使用。

在这里说一下产品族和产品等级结构的概念:

  • 产品族:同一工厂生产的不同产品
  • 产品等级结构:同一类型产品的不同实现

用一张图来帮助理解:

在上图中:

  • 纵向的,不同形状,相同色系的图形属于同一产品组的产品,而同一产品族的产品对应的是同一个工厂;
  • 横向的,同一形状,不同色系的图形属于统一产品等级结构的产品,而统一产品等级结构的产品对应的是同一个工厂方法。

下面再举一个例子帮助大家理解:

我们将小米,华为,苹果公司比作抽象工厂方法里的工厂:这三个工厂都有自己生产的手机,平板和电脑。 那么小米手机,小米平板,小米电脑就属于小米这个工厂的产品族;同样适用于华为工厂和苹果工厂。 而小米手机,华为手机,苹果手机则属于同一产品等级结构:手机的产品等级结构;平板和电脑也是如此。

结合这个例子对上面的图做一个修改可以更形象地理解抽象工厂方法的设计:

上面的关于产品族和产品等级结构的说法参考了慕课网实战课程:java设计模式精讲 Debug 方式+内存分析的6-1节。

成员与类图

成员

抽象工厂模式的成员和工厂方法模式的成员是一样的,只不过抽象工厂方法里的工厂是面向产品族的。

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

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

模式类图

抽象工厂模式类图

  • 抽象工厂模式与工厂方法模式最大的区别在于,工厂方法模式针对的是一个产品等级结构,而抽象工厂模式则需要面对多个产品等级结构
  • 增加新的具体工厂和产品族很方便,无须修改已有系统,符合“开闭原则”。

代码示例

场景概述

由于抽象工厂方法里的工厂是面向产品族的,所以为了贴合抽象工厂方法的特点,我们将上面的场景做一下调整:在上面两个例子中,商店只卖手机。在这个例子中我们让商店也卖电脑:分别是苹果电脑,小米电脑,华为电脑。

场景分析

如果我们还是套用上面介绍过的工厂方法模式来实现该场景的话,则需要创建三个电脑产品对应的工厂:苹果电脑工厂,小米电脑工厂,华为电脑工厂。这就导致类的个数直线上升,以后如果还增加其他的产品,还需要添加其对应的工厂类,这显然是不够优雅的。

仔细看一下这六个产品的特点,我们可以把这它们划分在三个产品族里面:

  1. 苹果产品族:苹果手机,苹果电脑
  2. 小米产品族:小米手机,小米电脑
  3. 华为产品族:华为手机,华为电脑

而抽象方法恰恰是面向产品族设计的,因此该场景适合使用的是抽象工厂方法。下面结合代码来看一下该如何设计。

代码实现

首先引入电脑的基类和各个品牌的电脑类:

电脑基类:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//================== Computer.h ==================
@interface Computer : NSObject

//package to store
- (void)packaging;

@end



//================== Computer.m ==================
@implementation Computer

- (void)packaging{
    //implemented by subclass
}

@end

苹果电脑类 MacBookComputer

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//================== MacBookComputer.h ==================
@interface MacBookComputer : Computer

@end



//================== MacBookComputer.m ==================
@implementation MacBookComputer

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

@end

小米电脑类 MIComputer

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//================== MIComputer.h ==================
@interface MIComputer : Computer

@end



//================== MIComputer.m ==================
@implementation MIComputer

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

@end

华为电脑类 MateBookComputer

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//================== MateBookComputer.h ==================
@interface MateBookComputer : Computer

@end



//================== MateBookComputer.m ==================
@implementation MateBookComputer

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

@end

引入电脑相关产品类以后,我们需要重新设计工厂类。因为抽象工厂方法模式的工厂是面向产品族的,所以抽象工厂方法模式里的工厂所创建的是同一产品族的产品。下面我们看一下抽象工厂方法模式的工厂该如何设计:

首先创建所有工厂都需要集成的抽象工厂,它声明了生产同一产品族的所有产品的接口:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//================== Factory.h ==================
#import "Phone.h"
#import "Computer.h"

@interface Factory : NSObject

+ (Phone *)createPhone;

+ (Computer *)createComputer;

@end



//================== Factory.m ==================
@implementation Factory

+ (Phone *)createPhone{

    //implemented by subclass
    return nil;
}

+ (Computer *)createComputer{

    //implemented by subclass
    return nil;
}

@end

接着,根据不同的产品族,我们创建不同的具体工厂:

首先是苹果产品族工厂 AppleFactory

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//================== AppleFactory.h ==================
@interface AppleFactory : Factory

@end



//================== AppleFactory.m ==================
#import "IPhone.h"
#import "MacBookComputer.h"

@implementation AppleFactory

+ (Phone *)createPhone{

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

+ (Computer *)createComputer{

    MacBookComputer *macbook = [[MacBookComputer alloc] init];
    NSLog(@"Macbook has been created");
    return macbook;
}

@end

接着是小米产品族工厂 MIFactory

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//================== MIFactory.h ==================
@interface MIFactory : Factory

@end



//================== MIFactory.m ==================
#import "MIPhone.h"
#import "MIComputer.h"

@implementation MIFactory

+ (Phone *)createPhone{

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

+ (Computer *)createComputer{

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

@end

最后是华为产品族工厂 HWFactory

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//================== HWFactory.h ==================
@interface HWFactory : Factory

@end



//================== HWFactory.m ==================
#import "HWPhone.h"
#import "MateBookComputer.h"

@implementation HWFactory

+ (Phone *)createPhone{

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

+ (Computer *)createComputer{

    MateBookComputer *hwComputer = [[MateBookComputer alloc] init];
    NSLog(@"HWComputer has been created");
    return hwComputer;
}

@end

以上就是工厂类的设计。这样设计好之后,客户端如果需要哪一产品族的某个产品的话,只需要找到对应产品族工厂后,调用生产该产品的接口即可。假如需要苹果电脑,只需要委托苹果工厂来制造苹果电脑即可;如果需要小米手机,只需要委托小米工厂制造小米手机即可。

下面用代码来模拟一下这个场景:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//================== Using by client ==================


Store *store = [[Store alloc] init];

//Store wants to sell MacBook
Computer *macBook = [AppleFactory createComputer];
[macBook packaging];

[store sellComputer:macBook];


//Store wants to sell MIPhone
Phone *miPhone = [MIFactory createPhone];
[miPhone packaging];

[store sellPhone:miPhone];


//Store wants to sell MateBook
Computer *mateBook = [HWFactory createComputer];
[mateBook packaging];

[store sellComputer:mateBook];

上面的代码就是模拟了商店售卖苹果电脑,小米手机,华为电脑的场景。而今后如果该商店引入了新品牌的产品,比如联想手机,联想电脑,那么我们只需要新增联想手机类,联想电脑类,联想工厂类即可。

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

代码对应的类图

抽象工厂模式代码示例类图

由于三个工厂的产品总数过多,因此在这里只体现了苹果工厂和小米工厂的产品。

优点

  • 具体产品在应用层代码隔离,不需要关心产品细节。只需要知道自己需要的产品是属于哪个工厂的即可 当一个产品族中的多个对象被设计成一起工作时,它能够保证客户端始终只使用同一个产品族中的对象。这对一些需要根据当前环境来决定其行为的软件系统来说,是一种非常实用的设计模式。

缺点

  • 规定了所有可能被创建的产品集合,产品族中扩展新的产品困难,需要修改抽象工厂的接口。
  • 新增产品等级比较困难
  • 产品等级固定,而产品族不固定,扩展性强的场景。

iOS SDK 和 JDK 中的应用

  • 暂未发现iOS SDK中使用抽象工厂方法的例子是NSNumber
  • JDK中有一个数据库连接的接口Connection。在这个接口里面有createStatement()prepareStatement(String sql)。这两个接口都是获取的统一产品族的对象,比如MySql和PostgreSQL产品族,具体返回的是哪个产品族对象,取决于所连接的数据库类型。

OK,到现在三个工厂模式已经讲完了。在继续讲解下面三个设计模式之前,先简单回顾一下上面讲解的三个工厂模式:

大体上看,简单工厂模式,工厂方法模式和抽象工厂模式的复杂程度是逐渐升高的。

  • 简单工厂模式使用不同的入参来让同一个工厂生产出不同的产品。
  • 工厂方法模式和抽象工厂模式都需要有特定的工厂类来生产对应的产品;而工厂方法模式里的工厂是面向同一产品等级的产品;而抽象工厂方法模式里的工厂是面向同一产品族的产品的。

在实际开发过程中,我们需要根据业务场景的复杂程度的不同来采用最适合的工厂模式

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2018-11-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 程序员维他命 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
【译】9个强大的JavaScript技巧
但是,如果站点无法在用户的Internet Explorer 11浏览器中运行,他们不会在乎我的优化代码。
Jimmy_is_jimmy
2019/12/16
3910
【译】9个强大的JavaScript技巧
【译】9个强大的JavaScript技巧
但是,如果站点无法在用户的Internet Explorer 11浏览器中运行,他们不会在乎我的优化代码。
Jimmy_is_jimmy
2020/10/15
4180
有个开发者总结这 15 优雅的 JavaScript 个技巧
JavaScript 有很多很酷的特性,大多数初学者和中级开发人员都不知道。今天分享一些,我经常在项目中使用一些技巧。
前端小智@大迁世界
2022/06/15
7320
有个开发者总结这 15 优雅的 JavaScript 个技巧
你应该知道的13个有用的JavaScript数组技巧
数组是Javascript最常见的概念之一,它为我们提供了处理数据的许多可能性。您可以在编程开始之初就了解它,在本文中,我想向您展示一些您可能不知道并且可能非常有用的技巧。有助于编码!让我们开始吧。
winty
2020/02/21
6110
12个非常有用的JavaScript技巧
在这篇文章中,我将分享12个非常有用的JavaScript技巧。这些技巧可以帮助你减少并优化代码。  1) 使用!!将变量转换成布尔类型 有时,我们需要检查一些变量是否存在,或者它是否具有有效值,从而将它们的值视为true。对于做这样的检查,你可以使用||(双重否定运算符),它能自动将任何类型的数据转换为布尔值,只有这些变量才会返回false:0,null,"",undefined或NaN,其他的都返回true。我们来看看这个简单的例子:  Js代码  function Account(cash) { 
hbbliyong
2018/03/06
7540
13个超实用的JavaScript数组操作技巧
英文原文:https://www.blog.duomly.com/13-useful-javascript-array-tips-and-tricks-you-should-know/
JowayYoung
2020/09/10
5470
12个JavaScript技巧
在这篇文章中将给大家分享12个有关于JavaScript的小技巧。这些小技巧可能在你的实际工作中或许能帮助你解决一些问题。 使用!!操作符转换布尔值 有时候我们需要对一个变量查检其是否存在或者检查值是否有一个有效值,如果存在就返回true值。为了做这样的验证,我们可以使用!!操作符来实现是非常的方便与简单。对于变量可以使用!!variable做检测,只要变量的值为:0、null、" "、undefined或者NaN都将返回的是false,反之返回的是true。比如下面的示例: function Accoun
hbbliyong
2018/03/06
6740
33个有用的 JavaScript 小技巧
作为开发人员,我体验到学习是一个持续的过程。随着每年都有新技术的出现与技术升级,我们都需要相应地进行技术更新,并且在工作和项目中进行充分利用它。
前端达人
2021/07/16
1.2K0
12 个 JS 技巧
在这篇文章中,将分享 12 个非常有用的 JavaScript 技巧,可以帮助你写出简洁且高性能的代码。
grain先森
2019/04/29
9880
12 个 JS 技巧
12个有用的JavaScript数组技巧
数组是Javascript最常见的概念之一,它为我们提供了处理数据的许多可能性,熟悉数组的一些常用操作是很有必要的。
IT工作者
2022/02/14
3030
12个非常有用的JavaScript技巧
在这篇文章中,我将分享12个非常有用的JavaScript技巧。这些技巧可以帮助你减少并优化代码。 1) 使用!!将变量转换成布尔类型 有时,我们需要检查一些变量是否存在,或者它是否具有有效值,从而将它们的值视为true。对于做这样的检查,你可以使用||(双重否定运算符),它能自动将任何类型的数据转换为布尔值,只有这些变量才会返回false:0,null,"",undefined或NaN,其他的都返回true。我们来看看这个简单的例子: function Account(cash) { this
CSDN技术头条
2018/02/12
7840
【JS】210-11个教程中不常被提及的JavaScript小技巧
有一些在日常工作中经常出现却容易被忽略的JavaScript小技巧,作者从中选取了11个教程中不常被提及的案例,供大家一起学习!
pingan8787
2019/07/23
4810
使用JavaScript的一些小技巧
https://www.w3cplus.com/javascript/javascript-tips.html
@超人
2021/02/26
1.7K0
使用JavaScript的一些小技巧
JavaScript 数组 API 全解析
在编程世界中,数组是指元素的集合。数组将数据作为元素进行存储,并在需要时将其取出。
若川
2021/07/19
2.4K0
JavaScript 数组 API 全解析
【Java 进阶篇】JavaScript Array数组详解
当我们编写JavaScript代码时,经常需要处理一组数据。JavaScript中的数组(Array)是一种用于存储多个值的数据结构,它提供了许多方法和功能,使我们能够方便地操作这些数据。在本篇博客中,我们将详细探讨JavaScript数组,包括如何创建、操作、遍历和使用数组。
繁依Fanyi
2023/10/19
2550
你不得不看的81条JavaScript编码小技巧,吐血整理,建议收藏
对于不包含大逻辑的 if-else 条件,可以使用下面的快捷写法。我们可以简单地使用三元运算符来实现这种简化。
CherishTheYouth
2021/08/31
2.2K0
JavaScript 又出新特性了?来看看这篇就明白了
https://juejin.im/post/5ca2e1935188254416288eb2
崔庆才
2019/05/06
1.6K0
《JavaScript权威指南》——JavaScript核心
前言 这本由David Flanagan著作,并由淘宝前端团队译的《JavaScript权威指南》,也就是我们俗称的“犀牛书”,算是JS界公认的“圣经”了。本书较厚(有1004页),读起来颇费功夫,但作为JavaScript(下文简称:JS)相关从业者,我还是鼎力推荐,一定要读完这本经久不息,好评如潮的JS“圣经”(如果您有耐心的读完,觉得还不错的,博客最后附有购买本书的优惠券,可自行领取)。 说完本书重要性,下面重点介绍一下本书作者写书的逻辑性,简单来说本书分为四部分,第一部分:JS核心;第二部分:客户端
磊哥
2018/05/08
1.2K0
分享一些对你有帮助的JavaScript技巧
JavaScript也不例外。对于JavaScript,我们经常会发现,为了一个相似的结果,我们可以用多种方式来做事情,这有时会让人感到困惑。 有些用法比其他替代方法更好。
程序员海军
2021/10/11
1.4K0
分享一些对你有帮助的JavaScript技巧
分享7个实用的 JavaScript 方法技巧
在本文中,我收集了一些关于JavaScript 技巧和窍门,我相信它们可以让你成为更好的 JavaScript 开发人员。没有特别的顺序,这里一共收集整理了7个 JavaScript 技巧!
前端达人
2021/07/16
8970
推荐阅读
相关推荐
【译】9个强大的JavaScript技巧
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档