设计模式必知必会系列终章(代码片段)

小杰312 小杰312     2022-10-21     436

关键词:

目录

装饰器模式

工厂方法模式

抽象工厂模式

​编辑

适配器模式

代理模式


装饰器模式

官方定义:   动态地给⼀个对象增加⼀些额外的职责。就增加功能而言,装饰器模式比生成子类更为灵活。 —— 《设计模式》GoF

通俗解释:  装饰器是为了给对象增加额外职责而产生的, 有点粉刷的意思, 房子已经存在了, 在房子的表面加上一层粉刷.      (而且它的优势是相较于继承而言的, 相比直接继承, 装饰器更加灵活, 耦合度更低)                     

应对与  ”过度的采取类继承扩展对象的功能“ 的情况

继承为类型引入的静态特质,使得这种扩展方式缺乏灵活性;并且随着子类的增多(扩展功能的增多),各种子类的组合(扩展功能的组合)会导致子类数量的膨胀。

(继承下来就是实实在在的一份负担, 代码的膨胀, 类的膨胀.)  但是很多时候其实我们没有必要去继承, 完全可以使用组合的方式来根据需求动态的实现对象功能的扩展, 以解决子类数量膨胀的问题. 使得功能扩展所带来的影响,代价最小,没必要说来一个新的功能扩展就新创建一个类。

这个类图其实很奇怪, 我刚看见的时候就在想,为啥又是继承, 又是组合的, 继承是为了复用之前的框架, 接口, 裸机 你只有有了之前的房子,才能粉刷装饰吧, 继承体现的是最初的裸机, 接口, 组合完成动态的修饰, 装饰。  

这个组合还是组合抽象类本身,绝对看的让人很迷,组合的虽然是抽象类本身, 但是抽象类是一个接口,它可以代表它的一切派生类, 这样便使得这个组合对象可扩展性很强.

模式要点:

  1. 通过采用组合而非继承的手法,Decorator模式实现了在运行时动态扩展对象功能的能力,而且可以根据需要扩展多个功能。避免了使用继承带来的“灵活性差”和“多子类衍生问题”。
  2. Decorator类在接口上表现为is-a Component的继承关系,即Decorator类继承了Component类所具有的接口。但在实现上又表现为has-a Component的组合关系,即Decorator类又使用了另外一个Component类。
  3. Decorator模式的目的并非解决“多子类衍生的多继承”问题,Decorator模式应用的要点在于解决“主体类在多个方向上的扩展功能”——是为“装饰”的含义。

记忆技巧:  继承复用接口,组合复用实现细节,动态组合

代码场景: 工资计算, 基本员工的工资是天数*100, 经理员工是基本工资 + 加成add. 老板更是在经理员工的基础上增加10倍加成

实现代码如下:

#include <iostream>
using namespace std;

//此处我没有再写componet类了, 想写的兄弟可以添加喔
class Context 
public:
	int day;//天数
	int add;//加成
;

//分发奖金: 普通员工仅仅只是day * 100  
//经理  还有加成
//老总等等加成更加牛逼, 在经理的基础上再10倍加成

//基础员工工资
//DecoratorClass装饰器基类
class BaseWocker 
public:
	BaseWocker(BaseWocker* _base) : base(_base) 

	
	virtual int CalculateSlary(Context& ctx) 
		//计算基本工资
		return ctx.day * 100;
	
	virtual ~BaseWocker() 
protected:
	BaseWocker* base;
;

//继承复用接口
//BaseWocker继承下来的是裸机, 最初框架, 接口
//组合扩展细节. 运算.
class Manager : BaseWocker 
public:
	Manager(BaseWocker* _base = nullptr) : BaseWocker(_base) 

	

	virtual int CalculateSalary(Context& ctx) 
		int basesalary = base->CalculateSlary(ctx);
		//...在组合对象的基础上进行扩展运算
		return basesalary + ctx.add;
	
;

//Decorator
class Boss : public BaseWocker 
public:
	Boss(BaseWocker* _base) : BaseWocker(_base) 

	

	virtual int CalculateSalary(Context& ctx) 
		int basesalary = base->CalculateSlary(ctx);
		//...在组合对象的基础上进行扩展运算
		return (basesalary + (ctx.add * 10));
	
;

如下是大佬写的一份代码: 大家也可以赏析一下:

//业务操作, Component抽象基类
class Stream

public:
    virtual char Read(int number)=0;
    virtual void Seek(int position)=0;
    virtual void Write(char data)=0;
    
    virtual ~Stream()
;

//主体类具体的Component类
class FileStream: public Stream
public:
    virtual char Read(int number)
        //读文件流
    
    virtual void Seek(int position)
        //定位文件流
    
    virtual void Write(char data)
        //写文件流
    

;

class NetworkStream :public Stream
public:
    virtual char Read(int number)
        //读网络流
    
    virtual void Seek(int position)
        //定位网络流
    
    virtual void Write(char data)
        //写网络流
    
    
;

class MemoryStream :public Stream
public:
    virtual char Read(int number)
        //读内存流
    
    virtual void Seek(int position)
        //定位内存流
    
    virtual void Write(char data)
        //写内存流
    
    
;

//扩展操作
//扩展操作采取的是装饰器类, has a 的特征
DecoratorStream: public Stream
protected:
    Stream* stream;//...
    
    DecoratorStream(Stream * stm):stream(stm)
    
    
    
;

//具体的decorator类
class CryptoStream: public DecoratorStream 
 

public:
    CryptoStream(Stream* stm):DecoratorStream(stm)
    
    
    
    
    virtual char Read(int number)
       
        //额外的加密操作...
        stream->Read(number);//读文件流
    
    virtual void Seek(int position)
        //额外的加密操作...
        stream::Seek(position);//定位文件流
        //额外的加密操作...
    
    virtual void Write(byte data)
        //额外的加密操作...
        stream::Write(data);//写文件流
        //额外的加密操作...
    
;



class BufferedStream : public DecoratorStream
    
    Stream* stream;//...
    
public:
    BufferedStream(Stream* stm):DecoratorStream(stm)
        
    
    //...
;

工厂方法模式

官方定义: 定义一个用于创建对象的接口,让子类决定实例化哪一个类。Factory Method使得一个类的实例化延迟(目的:解耦,手段:虚函数)到子类。——《设计模式》GoF

通俗解释:简单来说就是在工厂基类中定义一个抽象接口,  将整个接口的实例化延迟到一个具体的子类工厂(具体工厂),解耦合.                     ----    之后对于工厂的依赖都是依赖稳定的工厂基类

这个类图感觉不算特别完善, 最好可以在Factory下面在继承具体的Factory感觉比较好.

实现代码如下:

//抽象产品类
class Operator 
public:
	Operator(int l, int r) 
		: lhs(l)
		, rhs(r) 
	

	//抽象操作
	virtual int Operation() = 0; 
	virtual ~Operator() 
protected:
	int lhs;
	int rhs;
;

//具体产品类
class AddOperator : public Operator
public:
	AddOperator(int l, int r)
		: Operator(l, r) 

	
	virtual int Operation() 
		return lhs + rhs;
	
;

class SubOperator : public Operator

public:
	SubOperator(int l, int r)
		: Operator(l, r) 

	
	virtual int Operation() 
		return lhs - rhs;
	
;

class MulOperator : public Operator

public:
	MulOperator(int l, int r)
		: Operator(l, r) 

	
	virtual int Operation() 
		return lhs * rhs;
	
;


class DivOperator : public Operator
public:
	DivOperator(int l, int r)
		: Operator(l, r) 

	
	virtual int Operation() 
		if (rhs == 0) 
			cerr << "zero div" << endl;
			return 0;
		
		return lhs / rhs;
	
;


//工厂基类
class OperatorFactory 
public:
	//创建对象的接口, 实例化延迟到子类, 工厂方法FactoryMethod
	virtual Operator* CreateMethod(int, int) = 0;
;

//具体工厂
class AddOperatorFactory : public OperatorFactory 
public:
	virtual Operator* CreateMethod(int lhs, int rhs) override 
		return new AddOperator(lhs, rhs);
	
;

class SubOperatorFactory : public OperatorFactory 
public:
	virtual Operator* CreateMethod(int lhs, int rhs) 
		return new SubOperator(lhs, rhs);
	
;

class MulOperatorFactory : public OperatorFactory 
public:
	virtual Operator* CreateMethod(int lhs, int rhs) 
		return new MulOperator(lhs, rhs);
	
;

class DivOperatorFactory : public OperatorFactory 
public:
	virtual Operator* CreateMethod(int lhs, int rhs) 
		return new DivOperator(lhs, rhs);
	
;


int main() 

	int l;
	int r;
	char op;

	while (1) 
		cout << "请输入lhs, rhs: " << endl;
		cin >> l >> r;
		cout << "请输入操作方法op: " << endl;
		cin >> op;
		OperatorFactory* factory = nullptr;
		//创建工厂
		switch (op) 
			case '+': 
				factory = new AddOperatorFactory();
			 break;
			case '-': 
				factory = new SubOperatorFactory();
			 break;
			case '*': 
				factory = new MulOperatorFactory();
			 break;
			case '/': 
				factory = new DivOperatorFactory();
			 break;
		
		//生产产品运行Operaction
		cout << factory->CreateMethod(l, r)->Operation() << endl;
	
	getchar();
	return 0;

 要点:

  1. Factory Method模式用于隔离类对象的使用者和具体类型之间的耦合关系。面对一个经常变化的具体类型,紧耦合关系(new)会导致软件的脆弱。
  2. Factory Method模式通过面向对象的手法,将所要创建的具体对象工作延迟到子类,从而实现一种扩展(而非更改)的策略,较好地解决了这种紧耦合关系。
  3. Factory Method模式解决“单个对象”的需求变化。缺点在于要求创建方法/参数相同。

使用场景:

解决创建过程比较复杂,希望对外隐藏这些细节;

  1. 比如连接池,线程池;
  2. 隐藏对象真实类型;
  3. 对象创建会有很多参数来决定如何创建;
  4. 创建对象有复杂的依赖关系;

本质记忆技巧:  延迟到子类进行创建实现

抽象工厂模式

提供一个接口,让该接口负责创建一系列“相关或者相互依赖的对象”,无需指定它们具体的类。——《设计模式》GoF

通俗解释:本质上来讲, 无论是抽象工厂模式, 还是简工厂方法模式,均是属于对象创建模式. 抽象工厂模式相对于工厂方法而言核心差别在于它是负责相关联的一系列的产品, 对象的创建. 不再是单个产品的创建, 而是系列产品的创建, 两者差别仅此而已.

一个抽象工厂下面的两个具体工厂生产的两种产品是搭配产品。好比如说:瓶身与瓶盖两个产品的加工厂. 产品相互依赖,配套.  可以进行扩展, 比如说瓶子也有种类,矿泉水瓶,可口可乐瓶子,芬达瓶子等等. 这个瓶身瓶盖一定是配套生产的.       (对应一系列相关,相互依赖的对象)

//产品抽象类A
class IProductA 
public:
	//对于产品的操作
	virtual void Operation() = 0;
	virtual ~IProductA() 
;

//产品抽象类B
class IProductB 
public:
	//对于产品的操作
	virtual void Operation() = 0;
	virtual ~IProductB() 
;

//抽象工厂 A 瓶身, B 瓶盖
class IFactory 
public:
	virtual IProductA* FactoryMethodA() = 0;//创建产品A
	virtual IProductB* FactoryMethodB() = 0;//创建产品B
	virtual ~IFactory() 
;


//具体的产品
class PingSheng : public IProductA 
public:
	virtual void Operation() 
		cout << "我是瓶身" << endl;
	
;

class PingGai : public IProductB 
public:
	virtual void Operation() 
		cout << "我是瓶盖" << endl;
	
	
;

//具体的工厂
class PingZiFactory : public IFactory 
	virtual IProductA* FactoryMethodA() 
		return new PingSheng();
	
	virtual IProductB* FactoryMethodB() 
		return new PingGai();
	
;

记忆技巧:  配套产品生产

适配器模式

定义: 将一个类的接口转换为用户需要的另一个接口。Adapter使得原本由于接口不兼容不能在一起工作的那些类可以一起工作。 ——《设计模式》GoF

通俗解释:简单来说就是适配接口, 本来一个类的接口是和用户需要的接口不统一的, 但是现在需要使用这个类的一些功能, 于是可以抽象出来一个中间的适配器类, 进行组合这个需要使用的类对象,并且适配接口,进而实现接口不兼容的类功能, 接口也可以使用.

说到适配器模式,我还想说所有学习C++的大家,只要学了STL, 容器适配器学的还不错的,仿写过stack,queue两个容器适配器的实现的,你们都已经使用了适配器模式了. 只是当时还不知道自己用的是适配器模式. 想想好像确实就是适配新的接口.       

push_back-----> push   pop_back ----> pop   针对stack而言

场景引入, 简单实现一下适配器模式

//目标接口, 新接口.
class ITarget 
public:
	virtual void Request() = 0;
;

//需要复用的老旧接口
class Adaptee 
public:
	virtual void RequestOne() = 0;
	virtual void RequestTwo() = 0;
;

//具体的旧类型, 老旧的库, 需要适配复用其中的Request
class Old : public Adaptee 
public:
	virtual void RequestOne() 
		cout << "处理需求1" << endl;
	

	virtual void RequestTwo() 
		cout << "处理需求2" << endl;
	
;

//接口的不一致,需要产生一个Adapter接口适配器类, 适配接口
//适配ITarget目标类接口

//继承目标类适配接口
class Adapter : public ITarget 
protected:
	Adaptee* adaptee;//组合复用老旧接口
public:
	virtual void Request() 
		adaptee->RequestOne();
		adaptee->RequestTwo();
	
;

class Client 
private:
	ITarget* target;
public:
	void Click() // 客户点击, 目标执行需求
		target->Request();
	
;

 要点总结:

  1. Adapter模式主要应用于 "希望复用一些现成类, 但是接口又与复用环境不一致的情况", 在遗留代码复用, 类库迁移方面非常有用
  2. 适配的方式使用的是对象组合的方式, 更加符合松耦合的设计原则
  3. 原来的接口是稳定的,新的外来的需求是变化的,那么可以通过继承原来的接口,让原来的接口继续保持稳定,在子类通过组合的方式来扩展功能。     继承原来的接口(用户需要使用的接口, 复用其他类对象的接口,子类中组合扩展)

记忆技巧:  继承使得接口转换匹配, 组合复用已有类功能

代理模式

官方定义:为其他对象提供⼀种代理以控制(隔离, 使用接口)对这对象的访问。

——《设计模式》GoF

通俗解释:代理完成职责,代理一个具体类完成职责(接口职责, 相同的接口), 常见的代理, 婚庆代理, 代理商.

代理层出现的原因,背景,优势.

在有些系统中,为了某些对象的纯粹性,只进行了功能相关封装(稳定点),后期添加了其他功能 需要对该对象进行额外操作(变化点),为了隔离变化点(也就是不直接在稳定点进行修改,这样 会让稳定点也变得不稳定),可以抽象一层代理层

首先解释一下纯粹性:纯粹性指的是接口中仅仅只是实现功能,而不需要进行权限判断,引用计数等等其他的额外操作

代理类是一个中介层, 并不做具体的业务细节实现。而是介于客户类和具体的委托代理类之间的中间层. 作用在于对委托代理类使用前后的一些处理扩展, 让真正的RealSubject类保持纯粹  真正的业务功能其实还是委托代理类实现的。代理类是做中间处理和扩展的.        (对已有对象的额外操作, 放在代理层)

简单实现:

class ISubject 
public:
	virtual void Handle() = 0;
	virtual ~ISubject() 
;


//真实的Subject对象
//是一个很纯粹的类. 很稳定.
class RealSubject : public ISubject 
public:
	virtual void Handle() 
		//纯粹的功能实现
	
;

//代理类. 扩展变化
class Proxy1 : public  ISubject 
public:
	Proxy1(ISubject* subject) : _subject(subject) 
	

	//实现功能扩展.

	virtual void Handle() 
		//在访问RealSubject之前的一些操作

		//if (是否不可以访问) return;
		_subject->Handle();

		//访问玩之后的一些操作
		++count;//++访问计数等等
	
private:
	ISubject* _subject;
	static int count;
;

int Proxy1::count = 0;


//代理模式2扩展处理
//在分布式系统中出现的,skynet actor等

// 在分布式系统当中  skynet actor
class Proxy2 : public ISubject 
public:
	virtual void Handle() 
		// 在访问 RealSubject 之前做一些处理

		// 发送到数据到远端  网络处理  同步非阻塞 ntyco c协程
		//IResult * val = rpc->call("RealSubject", "Handle");

		// 在访问 RealSubject 之后做一些处理
	
private:
	/*void callback(IResult * val) 
	// 在访问 RealSubject 之后做一些处理
	*/
;

模式要点:

  1. 远程代理(隐藏⼀个对象存在不同的地址空间的事实),虚代理(延迟加载lazyload),保护代理(在代理前后做额外操作,权限管理,引用计数等)
  2. 在分布式系统中,actor模型(skynet)等常用的设计模式

本质:控制对象访问, 代理RealSubject, 处理对RealSubject访问前后的扩展需求. 权限验证, 引用计数等

  1. 好了各位老铁们,设计模式到此结束了.感谢各位兄弟们从一到现在的陪伴
  2. 设计模式的学习确实是有点虚无的.  特别是我们这些没啥工作经验的,学完之后根本不知道咋用到实际项目中实现重构项目的设计,持续重构形成自己的设计模式。 我们也没有经历过没有任何设计原则的Bad Smell Code的维护,所以体会是没有实践过的前辈们深入的
  3. 但是我觉得提早了解和具备遵循设计原则这样的大思想是我们需要具备的,不一定强行套入设计模式,可以先是我们的代码遵循设计原则。    遵循设计原则的代码往往更容易重构和维护
  4. IT技术路漫长久远。。                 ----  感谢各位大佬的支持,让我们继续加油,工作的前辈升职加薪,学习的友友学业有成,争取保研

h5系列之history(必知必会)(代码片段)

H5系列之History(必知必会)目录概念兼容性属性方法H5方法概念理解HistoryApi的使用方式目的是为了解决哪些问题作用:ajax获取数据时,可以改变历史记录,从而可以使用浏览器的后退和前进。【】规范地址:http://www.w3.org/TR/html5... 查看详情

架构师必知必会系列系统架构设计需要知道的5大精要(5systemdesignfundamentals)...(代码片段)

无论是在大厂还是初创公司,技术产品经理(TPM)都需要具备系统设计的基础知识。从历史上看,系统设计基础知识通常是软件工程师在面试时的要求,而TPM不受此期望的约束。然而,现在趋势正在发生变化。作为TP... 查看详情

springmvc--必知必会(代码片段)

  SpringMVC基于模型--视图--控制器(Model-View-Controller,MVC)模式实现,属于SpringFrameWork的后续产品,已经融合在SpringWebFlow里面。它通过一套注解,让一个简单的Java类成为处理请求的控制器,而无需实现任何接口。同时它还支持... 查看详情

android单例模式必知必会(代码片段)

...atile关键字一、概念        单例模式是运用最广泛的设计模式之一,在应用这个模式时,单例模式的类必须保证只有一 查看详情

scala必知必会(代码片段)

文章目录入门概述安装JavaVSScalaval和var基本数据类型lazy在Scala中的应用开发工具IDEAMaven函数方法定义默认参数命名参数可变参数条件语句循环语句面向对象概述类的定义和使用抽象类伴生类和伴生对象case和trait集合数组ListSetMapOpt... 查看详情

mysql学习--mysql必知必会(代码片段)

?上图为数据库操作分类:??下面的操作參考(mysql必知必会)创建数据库运行脚本建表:mysql>createdatabasemytest;QueryOK,1rowaffected(0.07sec)mysql>showdatabases;+--------------------+|Database|+--------------------+|infor 查看详情

必知必会-使用kafka之前要掌握的知识(代码片段)

必知必会系列之kafka前记kafka特性kafka实现顺序写高速读概念介绍分区和分组队列还是分发消费方式API前记消息队列是分布式系统架构中不可或缺的基础组件,它主要负责服务间的消息通信和数据传输。市面上有很多的开源消... 查看详情

大数据必知必会|hive架构设计和原理(代码片段)

前言        大家好,我是梦想家Alex。在上一篇文章简单介绍HDFS,MapReduce,Yarn的架构思想和原理,收获和反响还不错,那本篇内容,我们继续,本篇文章,我来为大家介绍Hive架构思想和设计原... 查看详情

大数据必知必会|hive架构设计和原理(代码片段)

前言        大家好,我是梦想家Alex。在上一篇文章简单介绍HDFS,MapReduce,Yarn的架构思想和原理,收获和反响还不错,那本篇内容,我们继续,本篇文章,我来为大家介绍Hive架构思想和设计原... 查看详情

hive必知必会(代码片段)

hive: 基于hadoop,数据仓库软件,用作OLAPOLAP:onlineanalyzeprocess 在线分析处理OLTP:onlinetransactionprocess在线事务处理 事务: ACID A:atomic 原子性 C:consistent 一致性 I:isolation 隔离性 D:durability 持久性 1读未提交   脏读 //事务... 查看详情

正则表达式必知必会(代码片段)

基本概念正则表达式描述了一种字符串匹配的文字模式,由普通字符(例如字符a到z)以及特殊字符(称为元字符)组成,将该模式与所搜索的字符串进行匹配。通俗的讲,正则表达式相当于定义了一个模板,从某个字符串中按... 查看详情

必知必会的设计原则——依赖倒置原则(代码片段)

概述开放封闭原则是面向对象的设计的中级目标,而依赖倒置原则是实现开放封闭原则的基础。如果开放封闭原则是设计大楼的蓝田,那么依赖倒置原则就是大楼的钢铁架构。高层模块(调用者)不应该依赖于低层模块(被调用... 查看详情

mysql必知必会(代码片段)

姊妹篇——Hive必知必会(数据仓库):https://hiszm.blog.csdn.net/article/details/119907136文章目录第一章:数据库基础基本概念什么是SQL第二章:MySQL简介第三章:了解数据库和表第四章:检索数据SELECT语句第五章:... 查看详情

crypto必知必会(代码片段)

crypto必知必会最近参加了个ctf比赛,在i春秋,南邮方面刷了一些crypto密码学题目,从中也增长了不少知识,在此关于常见的密码学知识做个小总结!Base编码Base编码中用的比较多的是base64,首先就说一下Base64编码方式将字符串以... 查看详情

读书笔记sql必知必会(代码片段)

章节标题页数进度完成时间1了解SQL1~9100%2022-04-08 2检索数据SELECT10~22100%2022-04-103排序检索数据ORDERBY23~30100%2022-04-114过滤数据WHERE31~38100%2022-04-115高级数据过滤(组合WHERE,NOT,IN)39~49100%2022-04-166用 查看详情

大数据必知必会:hadoop单机环境安装(代码片段)

(大数据必知必会:Hadoop(1)单机环境安装)安装前准备操作系统准备本次安装采用的操作系统是Ubuntu20.04。更新一下软件包列表。sudoapt-getupdate安装Java8+使用命令安装Java8。sudoapt-getinstall-yopenjdk-8-jdk配置环境变量。vi~/.bashrcexportJAVA... 查看详情

8000字概括精髓,pandas必知必会50例(代码片段)

本篇我们继续前面pandas系列教程的探讨,今天小编会介绍pandas库当中一些非常基础的方法与函数,希望大家看了之后会有所收获,另外呢,大家要是希望小编写什么样子类型的文章,也可以在评论区留言,... 查看详情

必知必会的设计原则——开放封闭原则(代码片段)

概述开放封闭原则是面向对象所有原则的核心。对功能扩展开放,面向修改代码封闭。需求改变时,在小改变软件实体源代码(类、接口、方法等)的前提下通过扩展功能使其满足新的需求。需求描述不同需求的用户去银行办理... 查看详情