设计模式(2) - 创建型模式

news2024/11/20 11:41:15

创建型模式指的是 创建对象 或是 获取实例 的方式。

1、工厂模式

平时写一些简单的代码可能会直接用 new 创建出一个对象,但是实际在阅读一些功能比较多、规模比较庞大的工程时,可能会发现有多个类继承于同一个基类的情况,它们拥有同样的接口但是实现了不同的功能。它们可能是可以互相替代的两套系统(例如 Android Media 中的 ACodec 和 CCodec),也有可能是多个不同功能的工具(例如 MediaExtractor 中不同的 extractor),创建这类对象时往往都用到了工厂模式

这里的工厂模式我觉得是一个广义的概念,指的是在创建有具有相同特征(相同基类)的对象时不要硬编码(hard code),这样会在工程庞大之后变得难以维护,我们需要一种动态的创建方法,可以根据条件灵活创建具体的类。这里的创建方法并不仅仅指的是如下三种具体的工厂设计模式,也有可能是一个条件判断函数。

工厂模式的实现主要依赖的是多态的特性。

1.1、简单工厂模式

我们可以认为具有相同特征(相同基类)的对象可以由一个工厂生产,根据不同的要求,工厂可以生产不同的对象实例,这就是简单工厂模式。

以简单计算器为例,有加法和减法等等计算器类型,这时候我们就可以创建一个计算器工厂,根据传进的符号创建对应的计算器实例:

class Calculator {
public:
	virtual int calculate(int a, int b) = 0;
};

class AddCalculator : public Calculator {
public:
	int calculate(int a, int b) {
		return a + b;
	}
};

class SubCalculator : public Calculator {
public:
	int calculate(int a, int b) {
		return a - b;
	}
};

class CalculatorFactory {
public:
	static Calculator* createCalculator(const char c) {
		Calculator* cal = NULL;
		switch (c)
		{
		case '+':
			cal = new AddCalculator;
			break;
		case '-':
			cal = new SubCalculator;
			break;
		default:
			break;
		}
		return cal;
	}
};

int main() {
	Calculator* addCal = CalculatorFactory::createCalculator('+');
	printf("5 + 5 = %d\n", addCal->calculate(5, 5));
	Calculator* subCal = CalculatorFactory::createCalculator('-');
	printf("5 - 5 = %d\n", subCal->calculate(5, 5));
	delete addCal;
	delete subCal;
	return 0;
}

请添加图片描述

用简单工厂模式有什么好处呢?

  • 只要给工厂指定计算器类型,它就可以为我们创建出对应实例,创建实例的接口被统一,后期如果要修改对象类型,只需要修改传给工厂的参数即可;
  • 后期如何要增加新的计算器类型,我们只要修改工厂即可,使用方式仍旧可以保持统一。

1.2、工厂方法模式

简单工厂需要指定创建的实例类型,指定这个动作也可以看作为hard code的一种,有没有办法让工厂为我们自动选择创建的实例类型呢?工厂方法模式可以帮助我们完成自动选择的动作。

我们对上面简单计算器例子做一些修改:

class Calculator {
public:
	virtual int calculate(int a, int b) = 0;
};

class AddCalculator : public Calculator {
public:
	int calculate(int a, int b) {
		return a + b;
	}
};

class SubCalculator : public Calculator {
public:
	int calculate(int a, int b) {
		return a - b;
	}
};

class CalFactory {
public:
	virtual Calculator* createCalculator() = 0;
};

class AddCalFactory : public CalFactory {
public:
	Calculator* createCalculator() {
		return new AddCalculator;
	}
};

class SubCalFactory : public CalFactory {
public:
	Calculator* createCalculator() {
		return new SubCalculator;
	}
};

int main() {

	CalFactory *addFactory = new AddCalFactory;
	Calculator* addCal = addFactory->createCalculator();
	printf("5 + 5 = %d\n", addCal->calculate(5, 5));
	CalFactory *subFactory = new SubCalFactory;
	Calculator* subCal = subFactory->createCalculator();
	printf("5 - 5 = %d\n", subCal->calculate(5, 5));
	delete addCal;
	delete subCal;
	delete addFactory;
	delete subFactory;
	return 0;
}

请添加图片描述
例子中每一个 Calculator 类都有其对应的 CalFactory 工厂,想要创建需要的类型只要实例化对应的工厂就可以了。

我们在网上其他博客中看到的工厂方法模式一般到这里就结束了,这里就会有疑问了,自动选择是如何体现的呢?工厂方法模式相比简单工厂优点在哪里呢?

接下来以 Android MediaPlayerFactory 为例看一下工厂方法模式在实战中是如何使用的:

MediaPlayerFactory 中有一个 getPlayerType 方法,它会遍历每一个具体的 IFactory,调用它们的 scoreFactory 方法得到最优分数,得到最优工厂类型;然后再调用MediaPlayerFactory createPlayer 方法,用自动选择的工厂创建出实例(当然分数选择和实例化对象可以放到一个方法中)。

请添加图片描述

1.3、抽象工厂模式

抽象工厂和工厂方法类似,这里只做简单举例:

class Pencil {
public:
	Pencil(const char* factory) : mFactory(factory) {}
	virtual void write() = 0;
protected:
	const char* mFactory;
};

class RedPencil : public Pencil {
public:
	RedPencil(const char* factory) : Pencil(factory) {}
	void write() {
		printf("%s", mFactory);
		printf(" Red Pencil write\n");
	}
};

class BlackPencil : public Pencil {
public:
	BlackPencil(const char* factory) : Pencil(factory) {}
	void write() {
		printf("%s", mFactory);
		printf(" Black Pencil write\n");
	}
};

class Pen {
public:
	Pen(const char* factory) : mFactory(factory) {}
	virtual void write() = 0;
protected:
	const char* mFactory;
};

class RedPen : public Pen {
public:
	RedPen(const char* factory) : Pen(factory) {}
	void write() {
		printf("%s", mFactory);
		printf(" Red Pen write\n");
	}
};

class BlackPen : public Pen {
public:
	BlackPen(const char* factory) : Pen(factory) {}
	void write() {
		printf("%s", mFactory);
		printf(" Black Pen write\n");
	}
};


class Factory {
public:
	virtual Pencil* createPencil(int catgory) = 0;
	virtual Pen* createPen(int catgory) = 0;
};

class AppleFactory : public Factory {
public:
	virtual Pencil* createPencil(int catgory) {
		Pencil* p = NULL;
		switch (catgory)
		{
		case 1:
			p = new RedPencil("AppleFactory");
			break;
		case 2:
			p = new BlackPencil("AppleFactory");
			break;
		default:
			break;
		}
		return p;
	}
	virtual Pen* createPen(int catgory) {
		Pen* p = NULL;
		switch (catgory)
		{
		case 1:
			p = new RedPen("AppleFactory");
			break;
		case 2:
			p = new BlackPen("AppleFactory");
			break;
		default:
			break;
		}
		return p;
	}
};

class OrangeFactory : public Factory {
public:
	virtual Pencil* createPencil(int catgory) {
		Pencil* p = NULL;
		switch (catgory)
		{
		case 1:
			p = new RedPencil("OrangeFactory");
			break;
		case 2:
			p = new BlackPencil("OrangeFactory");
			break;
		default:
			break;
		}
		return p;
	}
	virtual Pen* createPen(int catgory) {
		Pen* p = NULL;
		switch (catgory)
		{
		case 1:
			p = new RedPen("OrangeFactory");
			break;
		case 2:
			p = new BlackPen("OrangeFactory");
			break;
		default:
			break;
		}
		return p;
	}
};

static Factory* createFactory(const char* factoryName) {
	Factory* factory = NULL;
	if (!memcmp(factoryName, "Apple", 5))
	{
		factory = new AppleFactory;
	}
	else if (!memcmp(factoryName, "Orange", 6)) {
		factory = new OrangeFactory;
	}
	return factory;
}

int main() {
	Factory* AppFac = createFactory("Apple");
	Pen* AppPen = AppFac->createPen(1);
	AppPen->write();
	Pencil* AppPencil = AppFac->createPencil(1);
	AppPencil->write();

	Factory* OraFac = createFactory("Orange");
	Pen* OraPen = OraFac->createPen(2);
	OraPen->write();
	Pencil* OraPencil = OraFac->createPencil(2);
	OraPencil->write();

	delete AppFac;
	delete AppPen;
	delete AppPencil;
	delete OraFac;
	delete OraPen;
	delete OraPencil;
}

2、建造者模式

class Builder;
class Computer {
protected:
	friend class Builder;
	void setBrand(std::string brand)	{ mBrand = brand; }
	void setCpu(std::string cpu)		{ mCpu = cpu; }
	void setGpu(std::string gpu)		{ mGpu = gpu; }
	void setPrice(int price)			{ mPrice = price; }
public:
	void print() {
		printf("%s \t %s \t %s \t %d\n", mBrand.c_str(), mCpu.c_str(), mGpu.c_str(), mPrice);
	}
private:
	std::string mBrand;
	std::string mCpu;
	std::string mGpu;
	int			mPrice;
};

class GrapeComputer : public Computer {
public:
	GrapeComputer() {
		setBrand("Grape");
		setGpu("RTX4080");
	}
};

class OrangeComputer : public Computer {
public:
	OrangeComputer() {
		setBrand("Orange");
	}
};

class Director;
class Builder {
protected:
	friend class Director;
	virtual void buildCpu(std::string cpu) { mComputer->setCpu(cpu); };
	virtual void buildGpu(std::string gpu) { mComputer->setGpu(gpu); };
	virtual void buildPrice(int price) { mComputer->setPrice(price); };
public:
	virtual Computer* build() {
		return mComputer;
	}
protected:
	Computer *mComputer;
};

class GrapeBuilder : public Builder
{
public:
	GrapeBuilder() {
		mComputer = new GrapeComputer();
	}
	virtual void buildGpu(std::string gpu) override { };
};

class OrangeBuilder : public Builder
{
public:
	OrangeBuilder() {
		mComputer = new OrangeComputer();
	}
};


class Director {
public:
	Director(Builder *builder) { mBuilder = builder; }
	Computer * construct(std::string cpu, std::string gpu, int price) {
		mBuilder->buildCpu(cpu);
		mBuilder->buildGpu(gpu);
		mBuilder->buildPrice(price);
		return mBuilder->build();
	}
private:
	Builder *mBuilder;
};

int main() {
	Builder *grapeBuilder = new GrapeBuilder;
	Director *directorA = new Director(grapeBuilder);
	Computer *grapeComputer = directorA->construct("core i7 13700k", "standard", 10999);
	grapeComputer->print();
	delete grapeBuilder;
	delete directorA;
	delete grapeComputer;

	Builder *orangeBuilder = new OrangeBuilder;
	Director *directorB = new Director(orangeBuilder);
	Computer *orangeComputer = directorB->construct("core i5 12400f", "RTX3060", 5999);
	orangeComputer->print();
	delete orangeBuilder;
	delete directorB;
	delete orangeComputer;
}

以上是我阅读网上内容后自己编写的示例,示例主要由 Director 和 Builder 两部分组成。我的理解 Director 就是调用者,不同的 Builder 用于处理不同事务/创建不同的对象。引用网上的分析:

在客户端代码中,无须关心产品对象的具体组装过程,只需确定具体建造者的类型即可,建造者模式将复杂对象的构建与对象的表现分离开来,这样使得同样的构建过程可以创建出不同的表现。

我没有在实战中看到过完全契合以上示例的建造者模式使用,只在
Android MediaCodecList.cpp中看到有类似的使用方法。

实例化 MediaCodecList 时会先调用 GetBuilders 获取 Codec2InfoBuilder 和 OmxInfoBuilder 实例,接着遍历调用他们的 buildMediaCodecList 方法,将所有的信息写到 MediaCodecListWriter 中。在这个示例中虽然没有具体的 Director,也没有返回具体的对象,只有不同的 Builder,但是我觉得和建造者模式的思想是一致的。

3、单例模式

单例模式比较简单,在我们不想重复构建一个对象的时候可以使用单例模式,例如 DataSourceFactory.cpp 和上面提到的 MediaCodecList.cpp 就是典型的单例模式:

struct MediaCodecList : public BnMediaCodecList {
	static sp<IMediaCodecList> getInstance();
private:
    static sp<IMediaCodecList> sCodecList;
    MediaCodecList(std::vector<MediaCodecListBuilderBase*> builders);
    MediaCodecList(const MediaCodecList&) = delete;
    MediaCodecList& operator=(const MediaCodecList&) = delete;
}

目前碰到的单例模式使用场景如下:

  • 加载并存储信息可供查询,使用单例模式可以避免多次信息加载(MediaCodecList)
  • 提供一系列方法可供使用,使用单例模式可以减少对象创建的开销(DataSourceFactory)。

唯一有一点要注意的,在获取对象实例时需要用锁保护,避免有同时调用getInstance 同时创建到对象的情况出现:

sp<DataSourceFactory> DataSourceFactory::getInstance() {
    Mutex::Autolock l(sInstanceLock);
    if (!sInstance) {
        sInstance = new DataSourceFactory();
    }
    return sInstance;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1011012.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

删除安装Google Chrome浏览器时捆绑安装的Google 文档、表格、幻灯片、Gmail、Google 云端硬盘、YouTube网址链接(Mac)

删除安装Google Chrome浏览器时捆绑安装的Google 文档、表格、幻灯片、Gmail、Google 云端硬盘、YouTube网址链接(Mac) Mac mini操作系统&#xff0c;安装完 Google Chrome 浏览器以后&#xff0c;单击 启动台 桌面左下角的“显示应用程序”&#xff0c;我们发现捆绑安装了 Goo…

ArcGis10.8安装教程!

1、找到arcgis10.8中文安装包和Crack破解文件夹 2、运行"ArcGIS.exe"程序&#xff0c;进入安装向导&#xff1b;默认路径点下一步 3、注意&#xff0c;需要Python 2.7、Numpy、Matplotlib的支持 4、建议取消此处的勾选&#xff0c;开始进行安装 5、安装完成 6、…

【npm】npm私有库的使用-绑定

注册npm账户 输入基本信息 验证 收一次性验证码 登录 本地绑定 全局绑定了其他的私有库 若要在专门发包的项目中&#xff0c;发包到自己的私有库&#xff0c;需要在项目文件夹中创建一个.npmrc文件 创建文件 可以直接在项目目录下输入touch .npmrc创建文件 文件内容 regi…

C++之保存编译全部中间文件(二百一十五)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 人生格言&#xff1a; 人生…

JavaScript学习笔记03

JavaScript笔记03 流程控制 if 判断 和 Java 中if语句的使用方法相同。例&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title</title><script>"use strict"…

OPCAE扫盲

目录 1 基本概念 1.1 服务器/客户端 1.2 区域 1.3 报警/条件 1.4 事件 2 条件概念 2.1 子条件 2.2 OPCConditions属性 2.3 Condition质量 2.4 OPCSubConditions属性 2.5 Condition定义 2.6 严重性 2.7 Condition启用/禁用 2.8 Area启用/禁用 2.9 Condition状态集…

域控操作一:更换域用户桌面背景

1,创建背景图片文件夹并设置共享文件夹&#xff1a; 创建文件夹&#xff0c;将图片放进去&#xff0c;设置共享&#xff0c;权限改为Everyone 2&#xff0c;打开域控服务器设置组策略 在需要的组织单位OU内创建GPO设置名字为统一桌面背景 用户配置–管理模板–桌面–桌面 Act…

期权怎样的加仓才是合理的加仓?

期权加仓的手法是期权投资中常见的一种操作的手段,一般是在行情有大涨趋势的时候,投资者通过追加仓位来扩大收益和缩小持仓成本的策略&#xff0c;下文为大家介绍期权怎样的加仓才是合理的加仓&#xff1f;本文来自&#xff1a;期权酱 一、期权交易怎么加仓最合适&#xff1f;期…

[H5动画制作系列 ] Text及Button 的基础原理Demo

准备工作: 舞台上方是个动态文本框,名称为:myText,舞台下方是一个按钮元件(myButton)的实例,名称是:myButton1,当点击按钮时,能够在文本框上和控制台(console)输出:当前帧号以及全局i的变量值。建立两个图层,一个图层布局按钮和文本框,另一个图层专门部署代码。 操作步骤: 步…

WebGL 正确处理对象前后的关系——隐藏面消除(深度测试)/ 深度冲突

目录 前言 验证WebGL处理对象前后关系的规则——后绘制的图形覆盖先绘制的图形 隐藏面消除&#xff08;深度测试&#xff09; 开启隐藏面消除功能&#xff0c;需要遵循以下两步&#xff1a; 1.开启隐藏面消除功能。 gl.enable&#xff08;&#xff09;函数规范 2.在绘制…

计算机毕业设计 基于SSM的电影推荐系统的设计与实现 Java实战项目 附源码+文档+视频讲解

博主介绍&#xff1a;✌从事软件开发10年之余&#xff0c;专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精…

zabbix 钉钉微信企微告警(动作操作消息内容模板)

一、环境配置 1、配置zabbix服务端 2、配置监控主机&监控项&监控模板 zabbix配置安装_this page is used to test the proper operation of _疯飙的蜗牛的博客-CSDN博客 二、触发器 触发器的本质就是一个条件判断&#xff0c;对于不同的监控数据来说&#xff0c;我…

小剧场短剧影视小程序源码分享,搭建自己的短剧小程序

拥有一个属于自己的短剧小程序&#xff0c;是现代人追求创作梦想和与观众互动的新方式。近年来&#xff0c;小剧场短剧影视小程序的兴起为广大创作者提供了展示才华和与观众互动的平台。如果你也渴望搭建一个自己的短剧小程序&#xff0c;那么你来对地方了&#xff01;在本文中…

一个Python终端增强开源库

迷途小书童 读完需要 4分钟 速读仅需 2 分钟 1 简介 rich 是由 Will McGugan 开发的一个开源库&#xff0c;旨在提供更好的终端文本渲染和样式处理能力。它提供了丰富的文本格式化选项&#xff0c;包括颜色、粗体、斜体、下划线、对齐等。rich 不仅适用于命令行界面的美化&…

MYSQL 窗体汇总函数

如果我们想要汇总当天数据&#xff0c;当月数据&#xff0c;当年数据的。如果不懂窗体函数&#xff0c;可能会比较费劲&#xff0c;那小编就说了&#xff0c;我用java处理同样可以达到效果啊。可问题是。明明有现成的函数&#xff0c;为啥要用java处理&#xff0c;当然同时&…

【zlm】 webrtc源码讲解

目录 前端WEB 服务器收到请求 服务端的处理 播放 拉流 参考文章 前端WEB 服务器收到请求 POST /index/api/webrtc?applive&streamtest&typeplay HTTP/1.1 HttpSession::onRecvHeaderHttpSession::Handle_Req_POSTHttpSession::Handle_Req_POSTif (totalConte…

iPhone苹果15手机怎么看是国行还是美版或港版的苹果iPhone15手机?

iPhone苹果手机15机型区域版本识别代码 CH代码为国行 LL代码为美版 ZP代码为港版 iPhone苹果15手机怎么看是国行还是美版或港版的苹果iPhone15手机&#xff1f; 1、打开苹果iPhone15手机桌面上的「设置」&#xff1b; 2、在iPhone苹果15手机设置内找到「通用」并点击打开&…

MySQL修改时间添加时间自动更新

第一种: database.php设置 false改为true;然后看是使用的什么框架 如果是tp5需要数据库是create_time和update_time字段 laravel的话,需要的是created_at和updated_at字段 如果想自定义的话,就在model文件里加上 protected $createTime create_at;// 默认的字段为create_t…

腾讯mini项目-【指标监控服务重构】2023-08-01

今日已办 合并 Traefik 和 Profile 的 Trace 对 Traceparent Header 理解有误 Trace Context (w3.org) 故需要解析 TraceHeader 才能获取trace_id、parent_id func (profileCtx *ProfileContext) UnpackKafkaMessage(ctx context.Context) (needBreak bool, tpsStatus strin…

浅谈C++|模板篇

一.模板模板概念 模板就是建立通用的模具&#xff0c;大大提高复用性 模板的特点&#xff1a; 1.模板不可以直接使用&#xff0c;它只是一个框架 2.模板的通用并不是万能的 C另一种编程思想称为泛型编程&#xff0c;主要利用的技术就是模板。 C提供两种模板机制:函数模板和类模…