0

0

设计模式之简单工厂,工厂方法和抽象工厂

絕刀狂花

絕刀狂花

发布时间:2025-07-22 08:36:29

|

667人浏览过

|

来源于php中文网

原创

在面向对象编程中,通常通过继承和虚函数来提供抽象能力,多态性使得程序在运行时,调用者只需处理父类类型,而无需关注具体的子类类型。例如,在一个游戏中,活动对象为动物,如老虎和猫,它们会对主角进行攻击。实现可能如下所示:

设计模式之简单工厂,工厂方法和抽象工厂

代码如下:

class Animal {
public:
    virtual void Attack() = 0;
};

class Tiger : public Animal { public: virtual void Attack() { std::cout << "Tiger Attack!" << std::endl; } };

class Cat : public Animal { public: virtual void Attack() { std::cout << "Cat Attack!" << std::endl; } };

如果外部有一个方法,不需要关心具体的对象类型,只需使用基类Animal的指针来操作对象,通过多态机制可以实现对具体对象类型的方法调用:

void Attack(Animal pAnimal) {
pAnimal->Attack();
}

调用方式如下(忽略内存释放问题):

Animal pAnimal = new Tiger;
Attack(pAnimal);
pAnimal = new Cat;
Attack(pAnimal);

输出结果为:

Tiger Attack!
Cat Attack!

上述多态示例表明,在程序实现时,只需操作Animal类并了解其方法即可操作继承自Animal的对象。也就是说,继承自AnimalCatTiger不需要暴露给使用者,可以隐藏CatTiger的创建过程,不需要被调用者所关心。此时,可以通过模块化和工厂设计模式来实现。

例如,将Animal, Cat, Tiger的实现放入一个动态链接库模块中,并且只向调用者暴露Animal的头文件。而工厂方法设计模式,可以使用一个工厂方法创建具体的对象,返回时只返回基类Animal的指针。本文将从简单工厂开始讨论,然后介绍工厂方法和抽象工厂。

简单工厂是大家最常见的一种实现方法,如下所示:

设计模式之简单工厂,工厂方法和抽象工厂

代码如下:

class AnimalFactory {
public:
enum class AnimalType {
TIGER,
CAT
};

static Animal* CreateAnimal(const AnimalType type) {
    if (AnimalType::TIGER == type) {
        return new Tiger;
    }
    else if (AnimalType::CAT == type) {
        return new Cat;
    }
    return nullptr;
}

};

调用方式如下:

Animal pAnimal = AnimalFactory::CreateAnimal(AnimalFactory::AnimalType::TIGER);
Attack(pAnimal);
pAnimal = AnimalFactory::CreateAnimal(AnimalFactory::AnimalType::CAT);
Attack(pAnimal);

然而,这种方式违背了软件开发的开闭原则(Open-Closed Principle, OCP)。如果需要添加一个名为Dog的对象,则需要在CreateAnimal方法中修改分支判断逻辑。简单来说,这种扩展方式破坏了原有逻辑,扩展可能会影响软件的稳定性。

有些人可能不喜欢这种代码方式,但也有人认为这种书写方式比较简单。从个人工程实践经验来看,软件开发设计原则具有很好的指导意义,但并不是所有代码都必须完全符合这些原则。个人的理解如下:

在工程实践中,有时为了让代码完全遵循软件开发设计原则,反而会带来负担,例如过度设计问题。一方面,有些代码模块可能几年都不会被修改扩展;另一方面,当逻辑实现比较简单时,过度设计也会使代码维护者阅读代码更加费劲。毕竟大多数人只是普通的程序员。往往可扩展的代码编写时间更长,但程序员头上的压力还有软件开发时间。对于一般的程序员来说,在规定时间内高质量完成需求是首要任务,此时可能不会完全考虑软件开发设计原则。如果要符合开闭原则,可以实现工厂方法模式,让我们一起来看看。

XmxCms企业网站管理系统2.0
XmxCms企业网站管理系统2.0

原本这个程序只是本人两年前初学时练手的,最近拿出来进行了修改,所以叫XmxCms 企业网站管理系统2.0 开发环境:WinXP + VS2008 + SQLServer 2008 + Access开发语言:C#本程序采用 三层架构 + 抽象工厂设计模式 + Linq 实现,目前只做了Access 和 SQL Server ,默认数据库为Access,要更换数据库只需修改web.config 即可

下载

工厂方法的主要特点是通过继承一个AnimalFactory来实现具体的工厂,例如CatFactory主要负责生产Cat,而TigerFactory主要负责生产Tiger,其类图如下:

设计模式之简单工厂,工厂方法和抽象工厂

代码如下:

class AnimalFactory {
public:
virtual Animal CreateAnimal() = 0;
};

class CatFactory : public AnimalFactory { public: virtual Animal* CreateAnimal() { return new Cat; } };

class TigerFactory : public AnimalFactory { public: virtual Animal CreateAnimal() { return new Tiger; } };

调用方式如下:

AnimalFactory pFactory = new TigerFactory;
Animal* pAnimal = pFactory->CreateAnimal();
Attack(pAnimal);
pFactory = new CatFactory;
pAnimal = pFactory->CreateAnimal();
Attack(pAnimal);

可以发现,工厂方法模式如果需要扩展一个新的动物类型,也可以对应扩展一个新的工厂,例如增加一个新的DogFactory继承AnimalFactory来生产Dog,从而符合开闭原则,更加安全地进行扩展。然而,这种方式也可以看出,每增加一个新的动物类型就需要新增一个Factory。个人对这种模式的理解如下:

当动物类型的创建过程并不繁琐时,采用这种方式相较于简单工厂而言会更加繁琐;但当初始化过程较多时,使用工厂方法模式扩展会显得更加清晰。这种设计模式的本身实现是取消了条件判断的逻辑,但实际上是将这个条件判断任务交给了使用者去判断选择哪个工厂。

对于新手来说,可能不太容易理解抽象工厂模式,容易将其与工厂方法模式混淆。工厂方法模式中的每个工厂生产一个动物角色,而在抽象工厂中生产一类动物角色的抽象。一个动物角色比较好理解,就是我们上面的CatFactory生产Cat角色,而TigerFactory生产Tiger角色。一类动物角色,我们可以理解为在游戏中,这些动物角色的攻击性分为普通模式和困难模式。

首先,我们将Tiger分为SimpleModeTigerHardModeTigerCat分为SimpleModeCatHardModeCat。然后,抽象工厂提供CreateAnimal接口,继承的SimpleModeFactory可以生产简单模式的Cat角色和Tiger角色,继承的HardModeFactory可以生产困难模式的Cat角色和Tiger角色。

设计模式之简单工厂,工厂方法和抽象工厂

实现的代码如下所示:

#include 

class Animal { public: virtual void Attack() = 0; };

class AbstractTiger : public Animal { public: virtual void Attack() = 0; };

class SimpleModeTiger : public AbstractTiger { public: virtual void Attack() { std::cout << "SimpleModeTiger Attack!" << std::endl; } };

class HardModeTiger : public AbstractTiger { public: virtual void Attack() { std::cout << "HardModeTiger Attack!" << std::endl; } };

class AbstractCat : public Animal { public: virtual void Attack() = 0; };

class SimpleModeCat : public AbstractCat { public: virtual void Attack() { std::cout << "SimpleModeCat Attack!" << std::endl; } };

class HardModeCat : public AbstractCat { public: virtual void Attack() { std::cout << "HardModeCat Attack!" << std::endl; } };

class AbstractAnimalFactory { public: virtual Animal* CreateAnimal(AnimalType type) = 0; };

class SimpleModeAnimalFactory : public AbstractAnimalFactory { public: virtual Animal* CreateAnimal(AnimalType type) { if (type == AnimalType::TIGER) { return new SimpleModeTiger; } else if (type == AnimalType::CAT) { return new SimpleModeCat; } return nullptr; } };

class HardModeAnimalFactory : public AbstractAnimalFactory { public: virtual Animal* CreateAnimal(AnimalType type) { if (type == AnimalType::TIGER) { return new HardModeTiger; } else if (type == AnimalType::CAT) { return new HardModeCat; } return nullptr; } };

int main() { // 简单模式 AbstractAnimalFactory pFactory = new SimpleModeAnimalFactory; Animal pAnimal = pFactory->CreateAnimal(AnimalType::TIGER); Attack(pAnimal); pAnimal = pFactory->CreateAnimal(AnimalType::CAT); Attack(pAnimal);

// 困难模式
pFactory = new HardModeAnimalFactory;
pAnimal = pFactory-youjiankuohaophpcnCreateAnimal(AnimalType::TIGER);
Attack(pAnimal);
pAnimal = pFactory-youjiankuohaophpcnCreateAnimal(AnimalType::CAT);
Attack(pAnimal);

}

为了让结构简单,便于读者理解抽象工厂模式以及与工厂方法模式的区别,上述SimpleModeFactoryHardModeFactory使用的是简单工厂的形式实现的。如果需要符合开闭原则,可以再对SimpleModeFactoryHardModeFactory进行工厂方法的实现。可以想象,如果抽象工厂再加上工厂方法,这个类的结构会变得非常庞大,而且这还仅仅是两个游戏角色的描述。因此,笔者认为在进行工程实践时,尽量避免过度设计,有时反而不利于代码阅读,修改也未必简单。需要权衡好工程和理论的平衡点。

参考:

相关专题

更多
go语言 面向对象
go语言 面向对象

本专题整合了go语言面向对象相关内容,阅读专题下面的文章了解更多详细内容。

54

2025.09.05

java面向对象
java面向对象

本专题整合了java面向对象相关内容,阅读专题下面的文章了解更多详细内容。

46

2025.11.27

java多态详细介绍
java多态详细介绍

本专题整合了java多态相关内容,阅读专题下面的文章了解更多详细内容。

14

2025.11.27

java多态详细介绍
java多态详细介绍

本专题整合了java多态相关内容,阅读专题下面的文章了解更多详细内容。

14

2025.11.27

java多态详细介绍
java多态详细介绍

本专题整合了java多态相关内容,阅读专题下面的文章了解更多详细内容。

14

2025.11.27

硬盘接口类型介绍
硬盘接口类型介绍

硬盘接口类型有IDE、SATA、SCSI、Fibre Channel、USB、eSATA、mSATA、PCIe等等。详细介绍:1、IDE接口是一种并行接口,主要用于连接硬盘和光驱等设备,它主要有两种类型:ATA和ATAPI,IDE接口已经逐渐被SATA接口;2、SATA接口是一种串行接口,相较于IDE接口,它具有更高的传输速度、更低的功耗和更小的体积;3、SCSI接口等等。

984

2023.10.19

PHP接口编写教程
PHP接口编写教程

本专题整合了PHP接口编写教程,阅读专题下面的文章了解更多详细内容。

41

2025.10.17

http与https有哪些区别
http与https有哪些区别

http与https的区别:1、协议安全性;2、连接方式;3、证书管理;4、连接状态;5、端口号;6、资源消耗;7、兼容性。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

1556

2024.08.16

虚拟号码教程汇总
虚拟号码教程汇总

本专题整合了虚拟号码接收验证码相关教程,阅读下面的文章了解更多详细操作。

25

2025.12.25

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
PostgreSQL 教程
PostgreSQL 教程

共48课时 | 6.1万人学习

好课诞生记
好课诞生记

共20课时 | 5.9万人学习

swift开发文档
swift开发文档

共33课时 | 17.7万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号