设计模式 ---- 适配器模式&桥梁模式&代理模式
适配器模式
适配器模式和装饰器模式在代码结构上有一些相似之处,都可能通过继承目标接口,并持有另一个对象来完成包装(将新的接口翻译为业务逻辑能够理解的老接口)
装饰器模式是为了在不改变原有接口的情况下,为对象增加新的职责或功(接口基本不变,增加职责)
适配器模式则是为了让原本接口不兼容的对象能够被现有代码使用,将一个已有接口转换成客户端所需要的接口(功能目的通常不变,改变接口),这样不需要重写新的方法
比如现有游戏数据保存类:
class Save{
public:
virtual void uploadData(int userID) = 0;
}
class SaveData : public Save{
public:
void uploadData(int userID) override{
std::cout << "原版本地保存数据" << std::endl;
}
}
现在厂商觉得本地保存数据不安全,引进了云存储
有
class CloudUpload{
public:
void cluodUploadFun(int id){
std::cout << "这是云服务存储的接口,用户ID: " << id << std::endl;
}
}
那么为了然云存储适配现有接口就可以有适配器
class cloudUploadAdapter : public Save{
private:
CloudUpload* cloudUpload;
public:
cloudUploadAdapter(CloudeUpload* cloud) : cloudUpload(cloud){}
void uploadData(int userID){
std::cout << "这是适配器模式接口" << std::endl;
cloudUpload -> cludUploadFun();
}
}
这样在主业务种就有
CloudUpload cloudUploadIns;
cloudUploadAdapter adapter(&cloudUploadIns);
adapter.uploadData(123456);
这样将cloudUploadIns.cludUploadFun()变成了我们熟悉的adapter.uploadDate(123456)
从客户端的角度来看,它根本不需要知道 CloudeUpload 的存在。客户端仍然按照原来的 Save 接口调用 uploadData(),而适配器负责在内部将这个调用转换成云存储所提供的接口。
所以适配器的核心就是:让一个原本接口不兼容的对象,能够以客户端熟悉的接口被使用。
桥梁模式
了解桥梁模式的要点是"组合”,比如现有一个类,实际现有的作用其实是有两种类型组合起来的,比如
//铁狗牌
class IronIDTag{
private:
int tagID;
std::string IDTagMaterial = "iron";
public:
IDInfoTag(int ID): tagID(ID){}
void ShowInfo(){
//Play voice messages for strangers and so on...
}
}
// 著恶化号的成品类
class ChineseRuralDog{
private:
std::string name;
IronIDTag idTag;
pubilc:
ChineseRuralDog(int ID):idTag(ID){}
viod ShowInfoForStranger(){
idTag.ShowInfo();
}
}
在主业务种
ChineseRuralDog myDog(12);
myDog.ShowInfoForStranger();
但是如果我需要增加dog的种类和不同材质的身份牌呢,按照上面的写法,dog和IDtag其实是高度耦合的,为了降低耦合度,可以将两个类分别,抽象出来,然后通过dog 拥有身份牌 来进行桥接
//狗牌基类
class IDTag{
private:
std::string IDTagMaterial;
virtiual void ShowInfo() = 0;
};
//狗的基类
class Dog{
private:
std::string name;
IDTag* tag;
public;
Dog(std::string name, IDTag* tag)
: name(name), tag(tag)
{
}
virtual void ShowInfoForStranger() = 0;
}
在具体使用的时候
class IronIDTag : public IDTag
{
public:
void ShowInfo() override
{
std::cout << "这是铁制狗牌" << std::endl;
}
};
class ChineseRuralDog : public Dog
{
public:
ChineseRuralDog(std::string name, IDTag* tag)
: Dog(name, tag)
{
}
void ShowInfoForStranger() override
{
std::cout << "我是中华田园犬:" << name << std::endl;
tag->ShowInfo();
}
};
```C++
IronIDTag ironTag;
ChineseRuralDog dog("大黄", &ironTag);
dog.ShowInfoForStranger();
后期如果需要增加狗的种类,和身份牌的种类就不需要增加对应的牌和狗的具体类就好,而不需要写多个已经组合号的成品类
桥梁模式解决的不是“两个对象需要组合”这么简单,而是当一个类中存在两个或者多个独立变化的维度时,将这些维度拆分成独立的继承体系,再通过组合将它们连接起来,从而避免随着两个维度的组合产生大量子类
代理模式
代理模式:为真实对象提供一个代理对象,客户端不直接访问真实对象,而是通过代理对象间接访问真实对象,由代理对象控制或增强对真实对象的访问
和适配器模式的解决问题不一样,适配器模式只是将不同的接口翻译成现有的接口,从而达到不修改老代码的目的,代理模式是控制客户端(即调用代理的逻辑)对实际对象的访问权限,是否可以访问,如何访问,访问哪些内容...,也可以理解为,代理其实就是一个伪装成实际对象的中间层,客户端想要访问对象,其实访问的是一个伪装成对象的代理
及比如你想找明星要签名
你实际找到的是 经纪人,经纪人确定你是否是黑粉,在找到明星本人拿到签名,经纪人 在将签名给你(当然这里排除特殊情况,例如直接叨扰他人线下直接找人)
例子加载图片,现在已知加载图片需要很长时间,不希望每次显示图片都去加载一遍
class Image
{
public:
Image()
{
loadImg();
}
void Display()
{
cout << "显示图片" << endl;
}
private:
void loadImg()
{
cout << "加载图片" << endl;
}
};
直接访问
Image image;
image.Display();
如果图片太大,每次有需求都要经过这些步骤就太慢了
增加一个代理
class Image
{
public:
virtual void Display() = 0;
};
class RealImage : public Image
{
public:
Image()
{
LoadImage();
}
void Display() override
{
cout << "显示图片" << endl;
}
private:
void LoadImage()
{
cout << "加载图片" << endl;
}
};
class ImageProxy : public Image
{
private:
RealImage* image = nullptr;
public:
void Display() override
{
if (image == nullptr)
{
image = new RealImage();
}
image->Display();
}
};
有可能会有人问,直接在主程序里写要给if判断就好了啊,没必要增加一个代理,这和普通的装饰者模式有什么区别
这是因为,装饰者模式是对象没有的功能,将需要的功能一层层加上去,代理模式是将现象有的功能剥离出来,使功能详细过程对其透明(看不见)
如果你后续想要对display加上其他操作,为了不修改主程序,可以直在代理种操作,客户端不关心
但是如果使用装饰者,需要修改主程序(代理模式有点像简单工厂--生产 )(代理模式 -- 封装透明)(装饰者 -- 加功能)
浙公网安备 33010602011771号