一. 装饰模式
装饰模式:动态地给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更为灵活
装饰模式是为已有功能添加更多功能的一种方式。当系统需要新功能的时候,是向旧的类中添加新的代码。这些新加的代码通常装饰了原有类的核心职责或主要行为。
这样把类中的装饰功能从类中搬出,有效的把类的核心职责和装饰功能区分开,简化了原有的类
我们来模拟一个例子:星巴克咖啡订单项目需求
- 咖啡有不同种类:Espresso(意大利浓咖啡)、LongBlack(美式咖啡)、Decaf(无因咖啡)
- 咖啡中可以加入不同调料:Milk(牛奶)、Butter(黄油)、Salt(盐)
- 客户可以单点咖啡,也可以在咖啡中加入调料,使用面向对象来计算不同种类咖啡的费用
#include <cstdio>
#include <string>
#include <type_traits>
class Drink{
private:
std::string drink_name;
double price;
public:
virtual ~Drink() = default;
protected:
void setDrinkName(const std::string &name){
this->drink_name = name;
}
void setDrinkPrice(double price){
this->price = price;
}
public:
std::string getDrinkName(void) const{
return this->drink_name;
}
double getDrinkPrice(void) const{
return this->price;
}
virtual void show(void) const = 0;
virtual double cost(void) const = 0;
};
class Coffee : public Drink{
public:
double cost(void) const override{
return this->getDrinkPrice();
}
void show(void) const override{
printf("%s coffee\n", this->getDrinkName().c_str());
}
};
class Espresso : public Coffee{
public:
Espresso(){
this->setDrinkName("Espresso");
this->setDrinkPrice(40);
}
};
class LongBlack : public Coffee{
public:
LongBlack(){
this->setDrinkName("LongBlack");
this->setDrinkPrice(45);
}
};
class Decorator : public Drink{
private:
Drink &drink;
protected:
Decorator(Drink &drink)
:drink(drink){}
public:
double cost(void) const override{
return this->getDrinkPrice() + this->drink.cost();
}
void show(void) const override{
printf("%s ", this->getDrinkName().c_str());
this->drink.show();
}
};
class Milk : public Decorator{
public:
Milk(Drink &drink)
:Decorator(drink){
this->setDrinkName("Milk");
this->setDrinkPrice(5);
}
};
class Butter : public Decorator{
public:
Butter(Drink &drink)
:Decorator(drink){
this->setDrinkName("Butter");
this->setDrinkPrice(3);
}
};
int main(void){
LongBlack long_black;
Milk milk(long_black);
Butter butter(milk);
butter.show();
printf("Money: %lf\n", butter.cost());
return 0;
}
二. 代理模式
代理模式:为其它对象提供一种代理以控制对这个对象的访问
代理模式通常用在以下场合
-
远程代理(本地执行远程服务),适用于服务对象位于远程服务器上的情形。这样, 代理通过网络传递客户端请求, 负责处理所有与网络相关的复杂细节。
-
虚拟代理(延迟初始化),如果你有一个偶尔使用的重量级服务对象, 一直保持该对象运行会消耗系统资源时, 可使用代理模式。这样,你无需在程序启动时就创建该对象, 可将对象的初始化延迟到真正有需要的时候。
-
保护代理(访问控制),如果你只希望特定客户端使用服务对象, 这里的对象可以是操作系统中非常重要的部分, 而客户端则是各种已启动的程序 (包括恶意程序), 此时可使用代理模式。这样,代理可仅在客户端满足要求时将请求传递给服务对象。
-
智能引用,可在没有客户端使用某个重量级对象时立即销毁该对象。 代理会将所有获取了指向服务对象或其结果的客户端记录在案。 代理会时不时地遍历各个客户端, 检查它们是否仍在运行。 如果相应的客户端列表为空, 代理就会销毁该服务对象, 释放底层系统资源。代理还可以记录客户端是否修改了服务对象。 其他客户端还可以复用未修改的对象。
我们来模拟一个例子:在生活中,我们经常要找人帮忙,比如收快递
#include <cstdio>
#include <string>
class ReceiveParcel{
public:
virtual ~ReceiveParcel() = default;
public:
virtual void receive(const std::string &parcel_content) const = 0;
};
class ReceiveSelf : public ReceiveParcel{
private:
std::string name;
std::string phone;
public:
ReceiveSelf(const std::string &name, const std::string &phone)
:name(name), phone(phone){}
public:
std::string getName(void) const{
return this->name;
}
std::string getPhone(void) const{
return this->phone;
}
void receive(const std::string &parcel_content) const override{
printf("Name: %s, Phone: %s, %s has been received.\n", this->name.c_str(), this->phone.c_str(), parcel_content.c_str());
}
};
class ReceiveProxy : public ReceiveParcel{
private:
ReceiveSelf &receive_self;
public:
ReceiveProxy(ReceiveSelf &receive_self)
:receive_self(receive_self){}
public:
void receive(const std::string &parcel_content) const override{
printf("I am %s's friend, I'll receive the %s, phone is %s.\n", receive_self.getName().c_str(), parcel_content.c_str(), receive_self.getPhone().c_str());
}
};
int main(void){
ReceiveSelf receive_self("Alice", "15866667777");
receive_self.receive("perfume");
ReceiveProxy receive_proxy(receive_self);
receive_proxy.receive("perfume");
return 0;
}
作业
你在和朋友一起玩 猜数字(Bulls and Cows)游戏,该游戏规则如下:
写出一个秘密数字,并请朋友猜这个数字是多少。朋友每猜测一次,你就会给他一个包含下述信息的提示:
猜测数字中有多少位属于数字和确切位置都猜对了(称为 “Bulls”,公牛), 有多少位属于数字猜对了但是位置不对(称为 “Cows”,奶牛)。也就是说,这次猜测中有多少位非公牛数字可以通过重新排列转换成公牛数字。
给你一个秘密数字 secret 和朋友猜测的数字 guess ,请你返回对朋友这次猜测的提示。
提示的格式为 “xAyB” ,x 是公牛个数, y 是奶牛个数,A 表示公牛,B 表示奶牛。
请注意秘密数字和朋友猜测的数字都可能含有重复数字。
示例 1 输入:secret = “1807”, guess = “7810”
输出:“1A3B”
示例 2 输入:secret = “1123”, guess = “0111”
输出:“1A1B”