设计模式|常用23种
设计模式
一:模板模式
在固定步骤确定的情况下,通过多态机制在多个子类中对每个步骤的细节进行差异化实现,这就是模板方法模式能够达到的效果。
说人话:完成某件事,步骤确定,但是每一步可以使用不同的方法。通过利用多态机制,在父类中定义步骤,在子类中实现步骤的具体过程,从而保证父类稳定,子类变化。
例如去餐馆吃饭的具体步骤为:点菜 -> 食用 -> 结账。其中点菜可以点粤菜,鲁菜,白菜等多种具体行为。食用可以使用筷子,使用叉子,使用手等具体行为。结账可以使用现金,使用微信,霸王餐等具体行为。
模板方法 Gof 的定义是:在一个方法里定义算法的骨架,将一些步骤延迟到其子类。
例一: 餐厅吃饭:
- 步骤:点菜,吃,结账,抽象成模板函数,父类实现。并且在父类中写上步骤对应的虚函数。
- 具体行为:子类实现餐厅吃饭的各个步骤的具体行为。
- 调用:父类指针指向子类对象,指针调用模板函数,根据多态性,在模板中调用的虚函数会调用子类重写后的同名虚函数。从而实现这个功能。
- 总结:利用多态性,父类中定义整个过程,子类中具体实现。过程调用父类的函数,具体实现调用子类的函数。
class Customer {
public:
void dining() {//模板方法,定义了实现活动的步骤
order();
eat();
paying();
}
virtual ~Customer(){}
private:
virtual void order() = 0;//每个步骤的具体实现使用虚函数,延迟到子类实现
virtual void eat() = 0;//多态机制,会调用子类的函数
virtual void paying() = 0;
void bay() {
cout << "nice, bay!" << endl;
}
};
class Chinese : public Customer {
private:
virtual void order() {//步骤的具体实现
cout << "中国人使用微信点餐" << endl;
}
virtual void eat() {//步骤的具体实现
cout << "中国人使用筷子吃饭" << endl;
}
virtual void paying() {//步骤的具体实现
cout << "中国人使用微信付钱" << endl;
}
};
class American : public Customer {
private:
virtual void order() {//步骤的具体实现
cout << "美国人使用菜单点餐" << endl;
}
virtual void eat() {//步骤的具体实现
cout << "美国人使用叉子吃饭" << endl;
}
virtual void paying() {//步骤的具体实现
cout << "美国人使用信用卡付钱" << endl;
}
};
class Hindu : public Customer {
private:
virtual void order() {//步骤的具体实现
cout << "印度人使用手势点餐" << endl;
}
virtual void eat() {//步骤的具体实现
cout << "印度人使用手指吃饭" << endl;
}
virtual void paying() {//步骤的具体实现
cout << "印度人使用卢布付钱" << endl;
}
};
int main()
{
Customer * chinese = new Chinese();//父类指针指向子类对象
Customer* american = new American();
Customer* hindu = new Hindu();
chinese->dining();//多态调用
american->dining();
hindu->dining();
}
例二:
战斗:影响敌人,影响自己,播放特效。
- 步骤:影响敌人,影响自己,播放特效,抽象成模板函数,父类实现。并且在父类中写上各个行为的虚函数。
- 具体行为:不同子类实的分别实现具体行为。
- 调用:父类指针指向子类对象,指针调用模板函数,根据多态性,在模板中调用的虚函数会调用子类重写后的同名虚函数。从而实现这个功能。
- 总结:利用多态性,父类中定义整个过程,子类中具体实现。过程调用父类的函数,具体实现调用子类的函数。
namespace _nmsp2
{
//战斗者父类
class Fighter
{
public:
Fighter(int life, int magic, int attack) :m_life(life), m_magic(magic), m_attack(attack) {}
virtual ~Fighter() {} //做父类时析构函数应该为虚函数
//对主角自身会产生影响,对敌人会产生影响。
//分析:对敌人产生影响,有函数effect_enemy。对主角自身产生影响,有函数effect_self。播放技能play_effect函数。
void JN_Burn() //技能“燃烧”
{
effect_enemy(); //对敌人产生的影响
effect_self(); //对主角自身产生的影响
play_effect(); //播放技能“燃烧”的技能特效
}
private:
virtual void effect_enemy() {} //函数体为空,表示啥也不做,如果要求必须在子类中重新实现该虚函数,则可以将该函数写成纯虚函数。
virtual void effect_self() {}
void play_effect()
{
cout << "播放技能\"燃烧\"的技能特效给玩家看" << endl; //所有主角播放的技能特效都相同,因此不用写成一个虚函数并在子类中实现技能特效的播放。
}
protected: //可能被子类访问,所以用protected修饰
//角色属性
int m_life; //生命值
int m_magic; //魔法值
int m_attack; //攻击力
};
//-------------------------
//“战士”类,父类为Fighter
class F_Warrior :public Fighter
{
public:
F_Warrior(int life, int magic, int attack) :Fighter(life,magic,attack) {}
private:
//对敌人产生的影响
virtual void effect_enemy()
{
cout << "战士主角_让所有敌人每人失去500点生命,相关逻辑代码这里略......" << endl;
}
//对主角自身产生的影响
virtual void effect_self()
{
cout << "战士主角_自身失去300点生命值" << endl;
m_life -= 300;
}
};
//-------------------------
//“法师”类,父类为Fighter
class F_Mage :public Fighter
{
public:
F_Mage(int life, int magic, int attack) :Fighter(life, magic, attack) {}
private:
//对敌人产生的影响
virtual void effect_enemy()
{
cout << "法师主角_让所有敌人每人失去650点生命,相关逻辑代码这里略......" << endl;
}
//对主角自身产生的影响
virtual void effect_self()
{
cout << "法师主角_自身失去100点魔法值" << endl;
m_magic -= 100;
}
};
}
模板方法模式通常适用于以下场景。
- 算法的整体步骤很固定,但其中个别部分易变时,这时候可以使用模板方法模式,将容易变的部分抽象出来,供子类实现。
- 当多个子类存在公共的行为时,可以将其提取出来并集中到一个公共父类中以避免代码重复。首先,要识别现有代码中的不同之处,并且将不同之处分离为新的操作。最后,用一个调用这些新的操作的模板方法来替换这些不同的代码。
- 当需要控制子类的扩展时,模板方法只在特定点调用钩子操作,这样就只允许在这些点进行扩展。
优点:
- 模板方法模式是通过把不变的行为挪到一个统一的父类,从而达到去除子类中重复代码的目的、
- 子类实现模板父类的某些细节,有助于模板父类的扩展
- 通过一个父类调用子类实现的操作,通过子类扩展增加新的行为,符合“开放-封闭原则”
缺点:
- 按照设计习惯,抽象类负责声明最抽象、最一般的事物属性和方法,实现类负责完成具体的事务属性和方法,但是模板方式正好相反,子类执行的结果影响了父类的结果,会增加代码阅读的难度
适用场景:
- 多个子类有共有的方法,并且逻辑基本相同
- 重要、复杂的算法,可以把核心算法设计为模板方法,周边的相关细节功能则由各个子类实现
- 重构时,模板方法是一个经常使用的方法,把相同的代码抽取到父类中,然后通过构造函数约束其行为
二:简单工厂模式
简单工厂模式的实现思路:使用工厂类可以实现创建对象的代码 与 各个 具体类对象要实现的逻辑代码隔离。 说人话:一个类的某个功能(例如计算)根据不同情况(例如加减乘除),有不同的代码。这时可以把公共部分写成一个父类,然后实现父类的多个子类,每个子类实现一种情况。当需要修改或者增加某种新情况时,只需要修改对应的子类代码或者增加一个子类。
然后增加一个工厂类,根据不同的情况,在工厂类中创建子类,并返回。
例如:计算类有两个成员:数字A,数字B,有四个功能:加、减、乘、除。可以先写一个父类,里面有成员变量,虚函数计算结果。然后分别写加法类,减法类,乘法类,除法类并实现具体的计算类。再写一个工厂类,里面有一个函数,根据输入,返回具体的计算类型。然后使用。
class Operation {
public:
double getA(){
return numA;
}
double getB(){
return numB;
}
void setA(double num) {
numA = num;
}
void setB(double num) {
numB = num;
}
virtual double getRes() {
return 0;
}
virtual ~Operation() {};
private:
double numA = 0;
double numB = 0;
};
class OperationAdd :public Operation {
public:
double getRes() {
return getA() + getB();
}
};
class OperationSub :public Operation {
public:
double getRes() {
return getA() - getB();
}
};
class OperationMul :public Operation {
public:
double getRes() {
return getA() * getB();
}
};
class OperationDiv :public Operation {
public:
double getRes() {
return getA() / getB();
}
};
class OperationFactor {
public:
Operation* createrOpreate(char oper) {
switch (oper)
{
case '+':
return new OperationAdd();
break;
case '-':
return new OperationSub();
break;
case '*':
return new OperationMul();
break;
case '/':
return new OperationDiv();
break;
default:
break;
}
}
};
int main()
{
OperationFactor operFactor;
Operation* operAdd = operFactor.createrOpreate('+');
Operation* operSub = operFactor.createrOpreate('-');
operAdd->setA(100);
operAdd->setB(200);
operSub->setA(100);
operSub->setB(200);
cout << operAdd->getRes() << endl;
cout << operSub->getRes() << endl;
delete operAdd;
delete operSub;
}
存在一个缺点:需要具体的输入来指导工厂类生产出具体的对象。
三:工厂方法模式
简单工厂模式中,工厂类中必须使用客户端给出待生产产品的代号来进行逻辑判断,定返回哪个产品。除去了客户端与具体的产品之间的依赖。但是在增加新的产品时,需要修改工厂类,工厂类不符合开闭原则。
为了解除简单工厂类的耦合,根据依赖倒置原则,让模块依赖于接口。**把工厂类抽象出一个接口,这个接口有个方法,就是创建抽象产品的方法。然后所有的要生产的具体类的工厂,去实现这个接口。**这样一个简单工厂模式就变成了一个抽象工厂接口和多个具体的生产对象的工厂类。如果要增加新的产品,只需要增加对应的产品类和对应的具体工厂即可,无需修改原有的工厂类。对扩展开放,对修改封闭,符合开闭原则。
但是,对于客户端而言,需要知道具体的生产产品的类名,才能使用。相当于把简单工厂类中的逻辑判断,移植到了客户端。
class Operation {
public:
double getA() {
return numA;
}
double getB() {
return numB;
}
void setA(double num) {
numA = num;
}
void setB(double num) {
numB = num;
}
virtual double getRes() {
return 0;
}
virtual ~Operation() {};
private:
double numA = 0;
double numB = 0;
};
class OperationAdd :public Operation {
public:
double getRes() {
return getA() + getB();
}
};
class OperationSub :public Operation {
public:
double getRes() {
return getA() - getB();
}
};
class Factor {
public:
virtual Operation* createrOpreate() = 0;
virtual ~Factor() {};
};
class AddFactor :public Factor {
public:
Operation* createrOpreate() {
return new OperationAdd();
}
};
class SubFactor :public Factor {
public:
Operation* createrOpreate(){
return new OperationSub();
}
};
int main()
{
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
Factor* addFactor = new AddFactor();
Operation* operAdd = addFactor->createrOpreate();
Factor* subFactor = new SubFactor();
Operation* operSub = subFactor->createrOpreate();
operAdd->setA(100);
operAdd->setB(200);
operSub->setA(100);
operSub->setB(200);
cout << operAdd->getRes() << endl;
cout << operSub->getRes() << endl;
delete operAdd;
delete operSub;
delete addFactor;
delete subFactor;
}
四:抽象工厂模式
五:策略模式
定义了算法家族,分别封装起来,让他们之间可以互相替换,此模式让算法的改变,不影响使用算法的客户。
说人话:比如说对象的某个行为,在不同场景中有不同的实现方式,这样就可以将这些实现方式定义成一组策略类,每个实现类对应一个策略,在不同的场景就使用不同的实现类,并且可以自由切换策略。
策略父类定义算法接口,策略子类实现不同的算法。环境类包含一个策略父类的成员变量,一个调用策略父类的函数的成员函数,运行时通过多态性调用子类的具体算法。
使用时,创建一个环境类,并传入策略子类,通过环境类的成员函数,利用多态性调用子类的具体算法。
例如:出动物园,可以选择骑自行车,坐公共汽车,坐火车,坐飞机等。每个策略都可以得到相同的结果(到了动物园),但是它们使用了不同的资源(不同的车)。
class Travel {
public:
virtual void travel() = 0;
virtual ~Travel() {}
};
class TravelByCar : public Travel {
public:
virtual void travel() override {
cout << "坐汽车";
}
};
class TravelByBicycle : public Travel {
public:
virtual void travel() override {
cout << "骑自行车" ;
}
};
class TravelByTrain : public Travel {
public:
virtual void travel() override {
cout << "坐汽车";
}
};
class TravelContext {
public:
TravelContext(Travel* ptr):travel(ptr){}
TravelContext(string s):travel(nullptr){
if (s == "byCar") {
this->travel = new TravelByCar;
}
if (s == "byBicyle") {
this->travel = new TravelByBicycle;
}
}
void traveling() {
this->travel->travel();
}
virtual ~TravelContext() {
delete this->travel;
}
private:
Travel* travel = nullptr;
};
class People {
public:
void goToZoo() {
tc->traveling();
cout << "去动物园" << endl;
}
void setTravelContext(TravelContext* tc) {
this->tc = tc;
}
~People() {
delete tc;
}
private:
TravelContext* tc;
};
int main()
{
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
People Tom;
Tom.setTravelContext(new TravelContext(new TravelByBicycle));
Tom.goToZoo();
People Tim;
Tim.setTravelContext(new TravelContext(new TravelByCar));
Tim.goToZoo();
}
六:装饰模式
“装饰”设计模式的定义(实现意图):动态的给一个对象添加一些额外的职责。就增加功能来说,该模式相比生成子类更加灵活。
说人话:一个对象(比如人),他可以由多种组件(人体,外套,裤子,鞋子)中的若干部分构成。
- 首先定义一个类(人),然后由装饰类派生出多个具体的组件。
- 然后由人这个类派生一个装饰类。装饰类中有一个指向父类人的指针,有一个成员函数可以给这个指针赋值。然后重载父类(人这个类)的具体的函数。
- 最后由装饰类派生一系列的具体装饰类型,每个派生出来的类重载了装饰类重载的父类的具体函数(也就是人这个类的具体函数),这个函数中同事有自己的具体逻辑代码,又调用父类该函数的代码。
调用形式:(多想,说的很乱)
- 首先生成一个具体的使用对象(人这个对象),然后生成一系列的具体的装饰类,并通过装饰类中的函数,依次装饰各个装饰类对象。
- 当各个部分装饰完成,使用最后装饰的那一部分调用具体的函数,首先会调用这个装饰部分函数中的具体代码,然后调用直接父类对应的函数,在父类的这个函数中,通过指针调用这个对象中包含的指针对应的函数。这样就能把各个装饰部分都调用一遍,最后调用的是具体类(人这个类)的具体函数。
例一:
穿着各个衣服的人:
- 人类,生成人体,
- 有一个函数show,输出姓名。
- 装饰类:继承人类
- 有一个指向人类的指针变量。
- 有一个为指针赋值的函数。
- 有一个重载函数show,调用指针指向对象的show函数。
- 具体的装饰类:继承装饰类
- 有一个重载的show函数,函数中有自己的逻辑代码,并调用父类的show函数。
使用:
- 创建一个人对象。
- 创建各个衣服
- 层层装饰
- 最后装饰的部分调用show函数。
class Person {
public:
Person() {};
Person(string name) : name(name) {}
virtual void show() {
cout << "装扮的" << name << endl;
}
virtual ~Person() {};
private:
string name;
};
class Clothes:public Person {
public:
void decorate(Person* component) {
this->component = component;
}
virtual void show() {
component->show();
}
virtual ~Clothes() {}
private:
Person* component;
};
class TShirts : public Clothes {
public:
virtual void show() override {
cout << " T 恤 ";
Clothes::show();
}
};
class BellBottoms : public Clothes {
public:
virtual void show() override {
cout << " 喇叭裤 ";
Clothes::show();
}
};
class Slipper : public Clothes {
public:
virtual void show() override {
cout << " 拖鞋 ";
Clothes::show();
}
};
int main()
{
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
Person* Tim = new Person("Tim");
Clothes* tshirt = new TShirts();
Clothes* bellBottoms = new BellBottoms();
Clothes* slipper = new Slipper();
tshirt->decorate(Tim);
bellBottoms->decorate(tshirt);
slipper->decorate(bellBottoms);
slipper->show();
delete Tim;
delete bellBottoms;
delete tshirt;
delete slipper;
}
例二:
绘制有各个小功能的列表框。装饰模式包含的四种角色:
- a:Control(抽象构件):draw,让调用者以一致的方式处理未被修饰的对象以及经过修饰之后的对象,实现客户端的透明操作。
- b:ListCtrl(具体构件):实现抽象构件定义的接口,此后,装饰器就可以给该构件增加额外的方法(职责);
- c:Decorator(抽象装饰器类):
- d:BorderDec、VerScrollBarDec、HorScrollBarDesc(具体装饰器类):增加了一些新方法,然后通过对draw接口的扩展,达到最终的修饰目的。
//抽象饮料类
class Beverage
{
public:
virtual int getprice() = 0; //获取价格
public:
virtual ~Beverage() {}
};
//水果饮料类
class FruitBeverage : public Beverage
{
public:
virtual int getprice()
{
return 10; //一杯单纯的水果饮料,售价为10元
}
};
//抽象的装饰器类
class Decorator :public Beverage
{
public:
Decorator(Beverage* tmpbvg) :m_pbvg(tmpbvg) {} //构造函数
virtual int getprice()
{
return m_pbvg->getprice();
}
private:
Beverage* m_pbvg;
};
//具体的“砂糖”装饰器类
class SugarDec :public Decorator
{
public:
SugarDec(Beverage* tmpbvg) :Decorator(tmpbvg) {} //构造函数
virtual int getprice()
{
return Decorator::getprice() + 1; //额外加多1元,要调用父类的getprice方法以把以往的价格增加进来
}
};
//具体的“牛奶”装饰器类
class MilkDesc :public Decorator
{
public:
MilkDesc(Beverage* tmpbvg) :Decorator(tmpbvg) {} //构造函数
virtual int getprice()
{
return Decorator::getprice() + 2; //额外加多2元,要调用父类的getprice方法以把以往的价格增加进来
}
};
//具体的“珍珠”装饰器类
class BubbleDesc :public Decorator
{
public:
BubbleDesc(Beverage* tmpbvg) :Decorator(tmpbvg) {} //构造函数
virtual int getprice()
{
return Decorator::getprice() + 2; //额外加多2元,要调用父类的getprice方法以把以往的价格增加进来
}
};
int main()
{
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
//创建一杯单纯的水果饮料,价格10元:
Beverage* pfruit = new FruitBeverage();
//向饮料中增加珍珠,价格多加了2元
Decorator* pfruit_addbubb = new BubbleDesc(pfruit);
//再向饮料中增加砂糖,价格又加多了1元
Decorator* pfruit_addbubb_addsugar = new SugarDec(pfruit_addbubb);
//输出最终的价格
cout << "加了珍珠又加了砂糖的水果饮料最终价格是:" << pfruit_addbubb_addsugar->getprice() << "元人民币" << endl;
//释放资源
delete pfruit_addbubb_addsugar;
delete pfruit_addbubb;
delete pfruit;
return 0;
}
七: 代理模式
为其他对象提供一种代理,以控制对这个对象的访问
说人话:
class Customer {
public:
virtual void buyCar() = 0;
virtual ~Customer() {}
};
class Layfolk : public Customer {
public:
void buyCar() {
cout << "普通人掏钱买车" << endl;
}
};
class BuyCarProxy : Customer {
public:
BuyCarProxy(){
if (!customer) {
customer = new Layfolk();
}
}
void buyCar() {
check();
customer->buyCar();
}
~BuyCarProxy() {
if (customer) delete customer;
}
private:
void check() {
cout << "中介检查车况" << endl;
}
Customer* customer = nullptr;
};
int main()
{
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
BuyCarProxy* cs = new BuyCarProxy();
cs->buyCar();
delete cs;
}
class WebSet {
public:
virtual void visit() = 0;
virtual ~WebSet() {}
};
class Google : public WebSet {
public:
void visit() {
cout << "google 被访问" << endl;
}
};
class GoogleProxy : public WebSet {
public:
GoogleProxy() {
ws = new Google;
}
void visit() {
proxy();
ws->visit();
}
~GoogleProxy() {
delete ws;
}
private:
WebSet* ws = nullptr;
void proxy() {
cout << "进入代理模式" << endl;
}
};
class Person {
public:
void setProxy(WebSet* wb) {
this->gp = wb;
}
void visit() {
cout << "准备访问网站" << endl;
gp->visit();
}
private:
WebSet* gp = nullptr;
};
int main()
{
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
GoogleProxy* googleProxy = new GoogleProxy();
Person* Tim = new Person;
Tim->setProxy(googleProxy);
Tim->visit();
delete googleProxy;
delete Tim;
}
八:原型模式
浅拷贝与深拷贝
- 浅拷贝:值的拷贝。拷贝后,值相同。如果值时引用或者指针,则指向相同的内存。
- 深拷贝:完完整整的赋值出一个一模一样的对象。如果对象中有指针,引用等,也进行对应内容的拷贝。赋值完成后,这两个对象相互独立。
通过一个对象(原型对象)克隆出多个一模一样的对象。
一般在初始化的信息不发生变化的时候,克隆(原型模式)是最好的方法。即隐藏了创建对象的细节,又大大提高了性能。
定义:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。简单说来就是通过克隆来创建新的对象实例。 原型模式的两种角色:
- Prototype(抽象原型类):Monster类。
- ConcretePrototype(具体原型类):M_Undead、M_Element、M_Mechanic类。
如果对象内部数据比较复杂多变并且在创建对象的时候希望保持对象的当前状态,那么用原型模式显然比用工厂方法模式更合适。
比如果实现游戏里的怪物的分裂,就可以用原型模式。
class Monster {
public:
Monster(int life) :life(life) {}
virtual Monster* clone() = 0;
virtual ~Monster() {}
private:
int life;
};
class UndeadMonster :public Monster {
public:
UndeadMonster(int life) : Monster(life) {
cout << "创建了一个亡灵类怪物" << endl;
}
UndeadMonster(const UndeadMonster& tempobj) : Monster(tempobj) {
cout << "自我克隆出一个相同个的亡灵怪物" << endl;
}
Monster* clone() override {
return new UndeadMonster(*this);
}
};
int main()
{
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
Monster* undeadMonster = new UndeadMonster(200);
Monster* undeadMonsterClone = undeadMonster->clone();
delete undeadMonster;
delete undeadMonsterClone;
}
工厂方法模式和原型模式在创建对象时的异同点:
- 都不需要程序员知道所创建对象所属的类名。
- 工厂方法模式中的createMonster仍旧属于根据类名来生成新对象。
- 原型模式中的clone是根据现有对象来生成新对象。
可以把原型模式看成是一种特殊的工厂方法模式。
原型模式优缺点:
- 如果创建的新对象内部数据比较复杂且多变,原型模式创建对象的效率可能会高很多。
- 原型模式不存在额外的等级结构——原型模式不需要额外的工厂类。
- clone接口的实现方法有多种。
- 有些情况下,产品类中存在一个克隆方法也会给开发提供一些明显便利。
九:外观模式
为系统的一组接口提供了一个一致界面,此模式顶一个了一个高层接口,高层接口使得整个子系统更加容易的使用。
说人话:有多个类,客户端如果直接操作,需要多个类的创建,函数调用等,过程复杂,也容易出错。提供一个高层的接口,这个高层接口来把被操作的的各个类按照需要的功能进行统一调用。客户端只需要使用高层接口即可。
例如:一个人买卖股票。
- 多个股票了,分别包含买入和卖出函数。
- 外观类:包含各个股票的成员变量,有若干个函数,每个函数对应若干个股票的操作。、
class Stock1 {
public:
void sell() {
cout << "股票1被卖出" << endl;
}
void buy() {
cout << "股票1被买入" << endl;
}
};
class Stock2 {
public:
void sell() {
cout << "股票2被卖出" << endl;
}
void buy() {
cout << "股票2被买入" << endl;
}
};
class Stock3 {
public:
void sell() {
cout << "股票4被卖出" << endl;
}
void buy() {
cout << "股票4被买入" << endl;
}
};
class Fund {
public:
Fund(Stock1* st1, Stock2* st2, Stock3* st3) :st1(st1), st2(st2), st3(st3) {}
void buy() {
st1->buy();
st2->buy();
st3->buy();
}
void sell() {
st1->sell();
st2->sell();
st3->sell();
}
private:
Stock1* st1;
Stock2* st2;
Stock3* st3;
};
int main()
{
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
Stock1* st1 = new Stock1();
Stock2* st2 = new Stock2();
Stock3* st3 = new Stock3();
Fund* fund = new Fund(st1, st2, st3);
fund->buy();
fund->sell();
delete st1;
delete st2;
delete st3;
delete fund;
}
外观模式与代理模式区别:
- 代理模式是为了解决客户不能和其他类沟通的问题产生的,用代理多沟通桥梁,连通连个不能沟通的类。
- 外观模式中,客户能和其他类沟通,但是比较繁琐。所以用外观类来管理其他类,减少客户端的复杂度。
十:建造者模式
又叫生成器模式。
将一个复杂的对象构建与它的表示分离,是的同样的构建过程可创建不同 表示。
说人话:当创建一个比较复杂的对象的时候,为了避免创建不同对象时发生错误,可以使用这个模式。
- 需要创建比较复杂的类的对象。
- 生成一个创建类。
- 一个指向创建对象的指针。
- 这个类中有一系列函数,负责完成复杂对象的各个部分构建。
- 返回创建完成对象的函数。
- 创建类的一系列子类,一个子类负责创建一种形式的对象:
- 每个子类重写了创建类对复杂对象的各个部分创建的函数。
- 指导类:
- 一个用来指导创建的函数,接收创建类,在函数中完成各个部分的创建。
class Person {//复杂的类
public:
void buildHead(string size) {
cout << "构造了" << size << "头" << endl;
}
void buildBody(string size) {
cout << "构造了" << size << "身" << endl;
}
void buildLegs(string size) {
cout << "构造了" << size << "腿" << endl;
}
};
class PersonBuilder {//创建者类
public:
//各个部件的创建
virtual void buildPerson() = 0;
virtual void buildHead() = 0;
virtual void buildBody() = 0;
virtual void buildLegs() = 0;
//返回创建完成的对象
virtual Person* getPerson() {
return person;
}
virtual ~PersonBuilder() {}
protected:
//指向待创建对象
Person* person = nullptr;
};
//具体的创建类
class FatPersonBuilder : public PersonBuilder {
public:
//重写了创建类各个部分,创建出特定的对象内容
void buildPerson() {
person = new Person();
}
void buildHead() {
person->buildHead("大");
}
void buildBody() {
person->buildBody("大");
}
void buildLegs() {
person->buildLegs("大");
}
};
class ThinPersonBuilder : public PersonBuilder {
public:
void buildPerson() {
person = new Person();
}
void buildHead() {
person->buildHead("小");
}
void buildBody() {
person->buildBody("小");
}
void buildLegs() {
person->buildLegs("小");
}
};
//指导类
class PersonDirector {
public:
void personConstruct(PersonBuilder* pb) {//接收创建类,负责具体的创建过程
pb->buildPerson();
pb->buildBody();
pb->buildHead();
pb->buildLegs();
}
};
int main()
{
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
//指导人创建
PersonDirector* dp = new PersonDirector();
//创造特定类型对象的创建类
PersonBuilder* pbFat = new FatPersonBuilder();
PersonBuilder* pbThin = new ThinPersonBuilder();
//指导类对象的创建函数接收特定类型的创建类的对象,并完成各个部分创建
dp->personConstruct(pbFat);
//得到特定类型的对象
Person* person = pbFat->getPerson();
dp->personConstruct(pbFat);
Person* person2 = pbFat->getPerson();
delete dp;
delete pbFat;
delete pbThin;
delete person;
delete person2;
}
十一:状态模式
当一个对象的内在状态改变时,允许改变他的行为,这个对象看起来像改变了他的类。
说人话:如果某个类的有不同的状态,处在不同的状态的时候,同一个行为表现出不同的结果,直接在类中进行状态判断,然后执行对应的函数,会出现大量的判断语句。并且如果增加新的状态,需要修改类本身,破坏了开闭原则。就可以使用状态模式。
例如:
人处在孩子,青年,老年不同状态时,同一个吃的动作,表现出不同行为,小孩吃奶,青年吃肉,老年喝粥。如果在人类里面完成吃这个函数,需要有三个判断,并且如果增加新的状态,就需要修改人这个类本身。不符合开闭原则。
可以这样做:
- 创建一个
PersonState
类,里面有一个吃的虚函数。 - 创建若干个具体的
PersonState
的子类,在子类中完成吃这个函数的具体行为。 - 创建一个
Perosn
类:- 有一个
PersonState
指针,表示人的当前状态 - 有一个
setState
函数,用来设置人的状态 - 一个吃函数,调用
PersonState
的吃函数完成吃动作。
- 有一个
//Person.h
#ifndef _ITEMSTRATEGY__
#define _ITEMSTRATEGY__
class PersonState;
class Person {
public:
void setState(PersonState* ps);
void eat();
~Person();
int age = 0;
private:
PersonState* personState;
};
#endif
//PersonState.h
#ifndef __RIGHTER__
#define __RIGHTER__
#include <iostream>
using namespace std;
class PersonState {
public:
virtual void eat(Person* person) = 0;
virtual ~PersonState() {}
};
class ChildState : public PersonState {
public:
void eat(Person* person) {
person->age++;
cout << "小孩吃奶" << person->age << endl;
}
};
class YouthState : public PersonState {
public:
void eat(Person* person) {
person->age++;
cout << "青年吃肉" << person->age << endl;
}
};
class OldState : public PersonState {
public:
void eat(Person* person) {
person->age++;
cout << "老年喝粥" << person->age << endl;
}
};
#endif
//Person.cpp
#include "Person.h"
#include"PersonState.h"
void Person::setState(PersonState* ps) {
if (personState) delete personState;
personState = ps;
}
void Person::eat() {
personState->eat(this);
}
Person::~Person() {
if (personState) delete personState;
}
//main.cpp
#include <iostream>
#include"Person.h"
#include"PersonState.h"
using namespace std;
int main()
{
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
Person* person = new Person();
person->setState(new ChildState());
person->eat();
person->setState(new YouthState());
person->eat();
person->setState(new OldState());
person->eat();
delete person;
}
如果要增加新的状态,比如生病状态,只需要生成一个PersonState
类的子类,然后重写吃函数即可。
十二:观察者模式
定义:定义了一种一对多的依赖关系,让多个观察对象同时监听某一个主体对象。当这个主体对象发生变化时,主体对象会通知所有观察者对象,使他们动态的更新自己。
说人话:就是有许多个类型相同的对象,他们的属性受到某个条件的控制,当条件改变的时候,所有对象都会动态的发生对应变化。
传统模式:定义一个观察类。一个订阅类,订阅类中保存了所有他的观察者。当订阅类改变时,通知所有定观察类的对象做出对应动作。订阅类依赖于具体的观察类(保存了所有观察者)。具体类存在依赖。
例一:
有二进制类,八进制类,十六进制类,当一个类对象的状态发生变化时,要通知到所有的对象。可以这样做:
- 一个抽象通知者类
- 一个保存所有需要通知对象的数组
- 一个添加通知对象的方法
- 一个删除通知对象的方法
- 一个通知所有对象的方法
- 一个具体的通知者类,继承通知者类
- 实现了添加通知对象的方法,将对象添加到数组
- 实现了删除通知对象的方法,将对象从数组中删除
- 实现了通知对象的方法,方法内调用待通知对象的对应函数(
update
)
- 一个抽象的观察者类
- 一个保存通知对象的指针,在构造函数中为该对象赋值,并且将该对象保存到通知这对象中。
- 一个保存当前状态的变量。
- 一个改变状态的函数,函数中调用他的通知者对象的通知方法。
- 一个更新函数,接到通知后,差生对应的变化。
- 多个具体的观察者类
- 实现了观察者类的的更新方法
class Observer;
class Subject {
public:
virtual void Attach(Observer *obs) = 0;
virtual void Detach(Observer* obs) = 0;
virtual void notify(string state) = 0;
virtual ~Subject() {}
protected:
list<Observer*> obsList;
};
class Observer {
public:
Observer(Subject* subject) : subject(subject) {
subject->Attach(this);
}
virtual void update(string state) = 0;
void setState(string state) {
this->state = state;
if(subject)
subject->notify(state);
}
virtual ~Observer() {}
string state;
public:
Subject* subject = nullptr;
};
class BinaryObserver : public Observer {
public:
BinaryObserver(Subject* subject) :Observer(subject) {}
void update(string state) {
cout << "BinaryObserver 收到了通知:" << state << endl;
}
};
class OctalObserver : public Observer {
public:
OctalObserver(Subject* subject) :Observer(subject) {}
void update(string state) {
cout << "OctalObserver 收到了通知:" << state << endl;
}
};
class HexaObserver : public Observer {
public:
HexaObserver(Subject* subject) :Observer(subject) {}
void update(string state) {
cout << "HexaObserver 收到了通知:" << state << endl;
}
};
class NotifySubject : public Subject {
public:
void Attach(Observer* obs) {
obsList.push_back(obs);
}
void Detach(Observer* obs) {
obsList.remove(obs);
obs->subject = nullptr;
}
void notify(string state) {
for (auto iter = obsList.begin(); iter != obsList.end(); iter++) {
(*iter)->update(state);
}
}
};
int main()
{
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
Subject* notifySubject = new NotifySubject();
Observer* binaryObserver = new BinaryObserver(notifySubject);
Observer* hexaObserver = new HexaObserver(notifySubject);
Observer* octalObserver = new OctalObserver(notifySubject);
binaryObserver->setState("binaryObserver 开始工作");
cout << "________" << endl;
notifySubject->Detach(hexaObserver);
hexaObserver->setState("hexaObserver 开始工作");
cout << "________" << endl;
octalObserver->setState("octalObserver 开始工作");
cout << "________" << endl;
delete notifySubject;
delete binaryObserver;
delete hexaObserver;
delete octalObserver;
}
十三:适配器模式
将一个类的接口转换成客户希望的接口。使得由于接口不兼容而不能一起使用工作的类可以在一起使用。
说人话:有一个已经存在的接口,但是不符合客户的习惯。可以生成一个客户习惯使用的适配器类,类里面有个已经存在的接口对象的成员,客户调用适配器类,适配器类调用对应的接口函数。
例子:
原本开发了播放器接口,实现了Mp3
播放器类,想实现播放Mp4
类。Mp4
类已经再之前实现了,但是和现在接口不兼容。就可以避免重复开发Mp4
类,而是实现播放器接口的一个适配器类,适配器类中有一个Mp4
的成员。客户调用接口,接口类调用Mp4
类。
- 播放器接口:
- 虚函数
play
:播放Mp3
。
- 虚函数
Mp4
类:- 函数
show
:播放MP4。
- 函数
- 适配器类继承播放器类:
- 成员变量:
Mp4
对象指针。 - 实现
play
函数:调用Mp4
指针向对象的show
函数完成功能。
- 成员变量:
Mp3
类继承播放器接口类:- 成员变量:指向适配器对象的指针。
- 实现了
play
函数:如果自己不能播放,调用适配器对象播放。
这样 Mp3
类对象就能播放Mp4
了。
class MediaPlayer {
public:
virtual void play(string audioType) = 0;
virtual ~MediaPlayer() {}
};
class MP4Player {
public:
void show(string audioType) {
if (audioType == "mp4") {
cout << "播放mp4" << endl;
}
else
cout << audioType << "格式无法播放" << endl;
}
};
class MediaAdapter :public MediaPlayer {
public:
void play(string audioType) {
if (audioType == "mp4") {
if (!mp4Player)
mp4Player = new MP4Player();
mp4Player->show(audioType);
}
else
cout << audioType << "格式无法播放" << endl;
}
~MediaAdapter() {
if(mp4Player)
delete mp4Player;
}
private:
MP4Player* mp4Player;
};
class MP3Player :public MediaPlayer {
public:
void play(string audioType) {
if (audioType == "mp3") {
cout << "播放mp3" << endl;
}
else {
if(!mediaAdapter)
mediaAdapter = new MediaAdapter();
mediaAdapter->play(audioType);
}
}
~MP3Player() {
if(mediaAdapter)
delete mediaAdapter;
}
private:
MediaPlayer* mediaAdapter;
};
int main()
{
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
MediaPlayer* mp3 = new MP3Player();
mp3->play("mp3");
mp3->play("mp4");
mp3->play("avi");
delete mp3;
}
十四:备忘录模式
在不破坏封装性封装性的前提下,将类对象的状态(成员变量)保存在对象之外。这样以后可以将对象恢复到原先保存的状态。
说人话:相当于对象的存档。把对象的当前属性保存下来,如果有需要,可以用来恢复。
例子:有一个猫类,需要有存档,恢复存档功能。
- 备忘录类:
- 年龄属性:存档年龄
- 体重属性:存档体重
- 构造函数:构造时,设置好存档属性
- 得到年龄函数:返回存档年龄
- 得到体重函数:返回存档体重
- 猫类:
- 年龄属性,重量属性。
- 构造函数:构造时,给定初始年龄和体重。
- 存档函数:返回一个备忘录对象。
- 恢复函数:根据传入的备忘录,恢复状态
- 长大函数:外界改变猫状态的函数。
- 管理者类:
- 指向备忘录的指针:保存备忘录的地址
- 设置备忘录函数:将传入的备忘录地址保存到备忘录指针中
- 获取备忘录函数:返回备忘录的地址
class CatMemento {
public:
CatMemento(int age, int weight) :age(age), weight(weight) {}
int getAge() { return age; }
int getWeight() { return weight; }
private:
int age;
int weight;
};
class Cat {
public:
Cat(int age = 0, int weight = 0) : age(age), weight(weight) {}
CatMemento* creatMenmento() {
return new CatMemento(age, weight);
}
void recoverState(CatMemento* cm) {
this->age = cm->getAge();
this->weight = cm->getWeight();
}
void grow() {
age++;
weight++;
}
void show() {
cout << "猫咪年龄" << age << " 体重" << weight << endl;
}
private:
int age;
int weight;
};
class CatCaretaker {
public:
void setCatMemento(CatMemento* cm) {
this->cm = cm;
}
CatMemento* getCatMemento() {
return this->cm;
}
~CatCaretaker() {
if (cm) delete cm;
}
private:
CatMemento* cm;
};
int main()
{
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
Cat* cat = new Cat(1, 1);
cat->show();
CatCaretaker* catCaretaker = new CatCaretaker();
catCaretaker->setCatMemento(cat->creatMenmento());
cat->grow();
cat->show();
cat->recoverState(catCaretaker->getCatMemento());
cat->show();
delete cat;
delete catCaretaker;
}
十五:组合模式
将对象组合成树形结构以表示"部分-整体"的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。
说人话:相当于多叉树的递归遍历。每个节点有一个遍历函数,遍历以该节点为跟的所有子节点。
例如:对于公司的人这个类。有一个下属属性,保存了指向他下属的指针集合。有一个展示函数,输出他和他的所有下属。
- 人类:
- 保存下属指针的集合。
- 展示函数:递归输出自己和所有直接间接下属。
class Tree {
public:
Tree(string name) :name(name) {}
virtual void add(Tree* wk) = 0;
virtual void remove(Tree* wk) = 0;
virtual ~Tree() {};
void display(int depth) {
for (int i = 0; i < depth; i++) cout << "-";
cout << name << " 有" << child.size() << "个下属:"<< endl;
for (auto iter = child.begin(); iter != child.end(); iter++) {
(*iter)->display(depth + 1);
}
}
protected:
string name;
list<Tree*> child;
};
class Worker : public Tree {
public:
Worker(string name) :Tree(name) {}
void add(Tree* wk) {
child.push_back(wk);
}
void remove(Tree* wk) {
child.remove(wk);
}
};
int main()
{
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
Worker* boss = new Worker("Boss");
Worker* ministerA = new Worker("ministerA");
Worker* ministerB = new Worker("ministerB");
Worker* ministerC = new Worker("ministerC");
Worker* employeeA1 = new Worker("employeeA");
Worker* employeeA2 = new Worker("employeeA2");
Worker* employeeB1 = new Worker("employeeB1");
boss->add(ministerA);
boss->add(ministerB);
boss->add(ministerC);
ministerA->add(employeeA1);
ministerA->add(employeeA2);
ministerB->add(employeeB1);
boss->display(1);
delete boss;
delete ministerA;
delete ministerB;
delete ministerC;
delete employeeA1;
delete employeeA2;
delete employeeB1;
}
十六:迭代器模式
提供一种方法顺序访问一个聚合对象中各个元素, 而又无须暴露该对象的内部表示。
说人话:有一个集合,想要提供一种标准的方法,把在元素之间游走的责任交给迭代器,而不是聚合对象。在不管集合内部是如何实现的情况下,实现对结合的遍历。
例子:
- 抽象的迭代器接口类:迭代器具有的通用函数
- 一个让迭代器指向首元素的虚函数
- 一个让迭代器指向下一个元素的虚函数
- 一个判断是否遍历完的虚函数
- 一个指向抽象容器的指针
- 一个记录当前位置的变量
- 具体的迭代器类:继承迭代器接口
- 实现了接口的虚函数
- 一些自己的函数
- 一个抽象的容器接口类:容器的通用函数
- 一个创建迭代器的虚函数:返回一个指向抽象迭代器的指针
- 一个返回元素个数的虚函数
- 一个取得某个位置上元素引用的虚函数
- 具体的容器类:继承容器接口
- 实现了容器接口的虚函数。
- 一些自己的函数
template<typename T>
class Iterator {
public:
Iterator(Aggregate<T>* agg) :ma(agg) {}
virtual void first() = 0;
virtual void next() = 0;
virtual bool isDone() = 0;
virtual T& currentItem() = 0;
protected:
Aggregate<T>* ma;
int current = 0;
};
template<typename T>
class MyItreator :public Iterator<T> {
public:
MyItreator(Aggregate<T>* agg) :Iterator<T>(agg) {}
void first() {
this->current = 0;
}
void next() {
this->current++;
}
bool isDone() {
return this->current >= this->ma->size() ? 1 : 0;
}
T& currentItem() {
return this->ma->get(this->current);
}
};
template<typename T >
class Aggregate {
public:
virtual MyItreator<T> createrIterater() = 0;
virtual int size() = 0;
virtual T& get(int x) = 0;
~Aggregate() {}
};
template<typename T>
class MyAggregate :public Aggregate<T> {
public:
MyItreator<T> createrIterater() {
return MyItreator<T>(this);
}
void add(T obj) {
items.push_back(obj);
}
T& get(int x) {
return items[x];
}
int size() {
return items.size();
}
private:
vector<T> items;
};
int main()
{
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
MyAggregate<int> myAggregate;
myAggregate.add(1);
myAggregate.add(2);
myAggregate.add(3);
auto iter = myAggregate.createrIterater();
while (!iter.isDone()) {
cout << iter.currentItem()++ << endl;
iter.next();
}
}
十七:桥接模式
桥接模式即将抽象部分与它的实现部分分离开来,使他们都可以独立变化。实现系统可能有多个角度分类,每一种角度都可能变化,那么把这种多角度分类给分离出来让他们独立变化,减少他们之间耦合。
说人话:就是尽量少使用继承模式,多实用聚合模式。降低了类与类之间的耦合。
例如:要生成带颜色的形状,直接的想法是:写一个形状的抽象类,然后派生出各个形状,再派生出各个带颜色的形状。这样形状和颜色是紧耦合。如果要增加一种颜色,则有多少个具体的形状类,就要增加多少个这种颜色的子类。如果要增加一种形状,则有多少种颜色,就要为这种形状生成多个子类。
仔细思考可以发现:各个形状的颜色类别是一样的。同样的颜色对应了所有的形状,所以可以将形状和颜色分开。形状有颜色,所以形状类中有颜色属性。具体的带颜色的形状,由形状和颜色组合生成,而不是继承生成。
例子:
- 颜色接口类:
- 返回当前颜色的函数。
- 保存颜色的成员变量
- 具体颜色类:
- 在构造函数中为颜色属性赋值
- 形状接口类:
- 设置形状颜色的函数
- 画出形状的函数
- 保存形状的变量
- 保存颜色的指针
- 具体形状类:
- 在构造函数中为形状属性赋值
class Color {
public:
virtual string getColor() {
return color;
}
protected:
string color;
};
class Yellow : public Color {
public:
Yellow() {
color = "Yellow";
}
};
class White : public Color {
public:
White() {
color = "White";
}
};
class Shape {
public:
void setColor(Color* color) {
this->color = color;
}
virtual void draw() {
cout << color->getColor() << "颜色的" << this->name << endl;
}
virtual ~Shape() {}
protected:
Color* color;
string name;
};
class Rectangle : public Shape {
public:
Rectangle() {
this->name = "长方形";
}
};
class Circle : public Shape {
public:
Circle() {
this->name = "圆形";
}
};
int main()
{
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
Color* white = new White();
Color* yellow = new Yellow();
Shape* circle = new Circle();
Shape* rectangle = new Rectangle();
circle->setColor(white);
circle->draw();
circle->setColor(yellow);
circle->draw();
rectangle->setColor(white);
rectangle->draw();
rectangle->setColor(yellow);
rectangle->draw();
delete white;
delete yellow;
delete circle;
delete rectangle;
}
十八:命令模式
将一个请求封装为一个对象,从而是你可用不同的请求对客户进行参数化;对请求排队或记录日志,以及支持可撤销操作。
说人话:客户端如果要完成某个命令,不直接调用完成命令的对象。而是中间加入一个命令类,一个下令类。客户端调用下令类,下令类调用完成命令的命令。
命令类负责完成具体的命令。中包含具体的完成命令的对象,包含一个完成该需求的函数,函数中通过调用完成命令的对象的对应函数完成命令。
下令类负责保持命令,并且完成命令。有一个容器,保存了一系列的命令对象。有可以在容器中添加命令对象的函数,有从容器中删除命令对象的函数,有一个执行容器中所有命令的函数,这个函数通过调用命令对象的完成命令的函数完成这个命令。
例子:烤串。
- 烧烤师傅类:具体命令的完成者。
- 一系列的完成命令的函数。
- 命令类:负责完成某个具体的命令。
- 指向完成命令对象的指针。
- 执行命令函数虚函数。
- 具体的命令类:
- 在构造函数中为指向命令执行对象的指针赋值。
- 重写执行命令函数:调用命令执行对象的完成该命令的函数。
- 服务员类:
- 一个保存命令的对象的数组。
- 一个在数组中增加命令的函数。
- 一个在数组中删除命令的函数
- 一个执行命令数组中所有命令的函数:调用命令对象的执行命令函数完成。
class Barbecuer {
public:
void bakeMutton() {
cout << "烤羊如串" << endl;
}
void bakeBeef() {
cout << "烤牛肉串" << endl;
}
};
class Command {
public:
Command(Barbecuer* bb) :receiver(bb) {}
virtual void excuteCommand() = 0;
virtual ~Command() {}
protected:
Barbecuer* receiver;
};
class BakeMuttonCommand :public Command {
public:
BakeMuttonCommand(Barbecuer* receiver) :Command(receiver) {}
void excuteCommand() {
receiver->bakeMutton();
}
};
class BakeBeefCommand :public Command {
public:
BakeBeefCommand(Barbecuer* receiver) :Command(receiver) {}
void excuteCommand() {
receiver->bakeBeef();
}
};
class Waiter {
public:
void setOrder(Command* command) {
orders.push_back(command);
}
void cancelOrder(Command* command) {
orders.erase(remove(orders.begin(), orders.end(), command), orders.end());
}
void Notifyf() {
for (auto e : orders) {
e->excuteCommand();
}
orders.clear();
}
private:
vector<Command*> orders;
};
int main()
{
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
Waiter* waiter = new Waiter();
Barbecuer* barbecuer = new Barbecuer();
Command* makeBeef = new BakeBeefCommand(barbecuer);
Command* makeBeef1 = new BakeBeefCommand(barbecuer);
Command* makeMutton = new BakeMuttonCommand(barbecuer);
waiter->setOrder(makeBeef);
waiter->setOrder(makeBeef1);
waiter->setOrder(makeMutton);
//waiter->cancelOrder(makeBeef1);
waiter->Notifyf();
delete makeBeef;
delete makeBeef1;
delete makeMutton;
delete waiter;
}
十九:职责链模式
使多个对象都有处理问题的机会,避免请求的发送者和接收者之间的耦合关系。将对象连接成一条链,并沿着这条链传递请求,直到有个对象处请求位置。
说人话:有一系列的处请求的类,把这些类穿成一个链表,当有问题时,把问题传给链表中的某一个节点,这样做,这个节点和这个节点的后继都有机会处理该令。
例子:公司中各级领导处理问题。
- 公司成员接口类:
- 一个指向公司成员对象后继的指针。
- 一个设置后继的函数
- 一个根据问题等级处理问题的虚函数。
- 一系列的具体处理问题的公司人员类。
- 重写处理问题函数:如果在自己能力范畴内,则处理问题,如果超出能力范畴,将问题传递给他的后继,他的后继续处理该问题。
class Member {
public:
void setNext(Member* m) {
this->nextMember = m;
}
virtual void handelRequest(int level) = 0;
virtual ~Member() {
}
protected:
Member* nextMember = nullptr;
};
class Boss:public Member {
public:
void handelRequest(int level) {
if (level <= 1 && level >= 0) {
cout << "Boss 处理了请求: 优先级为" << level << endl;
}
else {
cout << "请求超出公司范围!!!!" << endl;
}
}
};
class Director :public Member {
public:
void handelRequest(int level) {
if (level <= 5 && level >= 2) {
cout << "Director 处理了: 优先级为" << level << endl;
}
else {
nextMember->handelRequest(level);
}
}
};
class Leader :public Member {
public:
void handelRequest(int level) {
if (level >= 5) {
cout << "Leader 处理了: 优先级为" << level << endl;
}
else {
nextMember->handelRequest(level);
}
}
};
int main()
{
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
Member* boss = new Boss();
Member* director = new Director();
Member* leader = new Leader();
director->setNext(boss);
leader->setNext(director);
vector<int> request = { -2,-1, 0 ,1,2,3,4,5,6,7,8,9,10 };
for (auto e : request) {
leader->handelRequest(e);
}
delete boss;
delete director;
delete leader;
}
二十:中介者模式
用一个中介对象来封装一系列的对象交互,中介者使个对象不需要显示的相互引用,而使其耦合松散,而且可以独立变化他们之间的交互。
说人话:把两个对象对象之间的直接交互改为通过中介者简介交互,从而使得各个对象之间不用相互的引用。
例如:人和鸡通过翻译进行对话。
- 动物抽象类
- 指向翻译者的指针成员
- 保存姓名的成员
- 构造函数中为指向翻译者的指针赋值
- 发送消息的虚函数
- 接收消息的虚函数
- 翻译者类
- 保存待沟通的成员
- 设置待沟通成员的函数。
- 发送消息函数:进行翻译,然后给待接收消息的成员发送消息。
- 人类和鸡类:
- 构造函数中初始化父类
- 重写父类的发送消息函数,通过翻译者发型消息。
class Translate;
class Animal {
public:
Animal(Translate* tlt) : translate(tlt) {}
virtual void senMessage(string message) = 0;
virtual void getMessage(string message) = 0;
protected:
Translate* translate;
string name;
};
class Translate {
public:
void sendMessage(string message, Animal* animal) {
if (message == "mememem") message = "来吃米";
else message = "来拿蛋";
if (animal == role1) role2->getMessage(message);
else role1->getMessage(message);
}
void setRole(Animal* a1, Animal* a2) {
role1 = a1;
role2 = a2;
}
virtual ~Translate() {}
protected:
Animal* role1;
Animal* role2;
};
class Person : public Animal {
public:
Person(Translate* tlt) : Animal(tlt) {}
void senMessage(string message) {
translate->sendMessage(message, this);
}
void getMessage(string message) {
cout << "人收到了消息:" << message << endl;
}
};
class Chicken : public Animal {
public:
Chicken(Translate* tlt) : Animal(tlt) {}
void senMessage(string message) {
translate->sendMessage(message, this);
}
void getMessage(string message) {
cout << "鸡收到了消息:" << message << endl;
}
};
int main()
{
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
Translate* translate = new Translate();
Person* person = new Person(translate);
Chicken* chicken = new Chicken(translate);
translate->setRole(person, chicken);
person->senMessage("mememem");
chicken->senMessage("gegege");
delete translate;
delete person;
delete chicken;
}
二一:享元模式
运用共享技术来有效地支持大量细粒度对象的复用。它通过共享已经存在的对象来大幅度减少需要创建的对象数量、避免大量相似类的开销,从而提高系统资源的利用率。
说人话:在有大量对象时,可能会造成内存溢出。可以把对象的相同部分抽离出来,如果有相同业务请求,直接返回在内存中已经存在的对象。
例子:画出大量的不同位置,不同半径,不同颜色的圆。如果用一个对象表示一个圆,会占用大量内存空间,可以利用享元模式,创建一个工厂类,工厂类中保存不同特征的圆,需要时直接返回。
- 图形类:
- 画出图形的抽象函数
- 圆类:
- 圆心,半径,颜色这些参数的成员变量。
- 重写父类的画图函数。
- 工厂类:
- 保存不同颜色圆的哈希表(通过颜色分类)
- 返回圆指针的成员函数:现在哈希表中取颜色对应的圆,如果取不到,就新建并保存在表中。最后返回。
class Shape {
public:
virtual void draw() = 0;
virtual ~Shape() {}
};
class Circle : public Shape {
public:
Circle(string color) :color(color) {}
void setX(int x) { this->x = x; }
void setY(int y) { this->y = y; }
void setRadius(int radius) { this->radius = radius; }
void draw()override {
cout << x << "," << y << "出画 " << color << " 颜色的圆" << this << endl;
}
private:
int x;
int y;
string color;
int radius;
};
class CircleFactor {
public:
static Circle* getCircle(string color) {
Circle* circle = (Circle*)circleMap[color];
if (!circle) {
circle = new Circle(color);
circleMap[color] = circle;
}
return circle;
}
static unordered_map<string, Shape*> circleMap;
};
unordered_map<string, Shape*> CircleFactor::circleMap;
int main()
{
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
string color[] = { "green", "blue", "black", "white" };
for (int i = 0; i < 20; i++) {
Circle* circle =(Circle*)CircleFactor::getCircle(color[i % 4]);
circle->setX(i);
circle->setY(20 - i);
circle->setRadius(i);
circle->draw();
}
cout << CircleFactor::circleMap.size() << endl;
}
二二: 解释器模式
给定一个语言,定义它的文法表示,并定义一个解释器,这个解释器使用该标识来解释语言中的句子。
说人话:
class Expression;
class Context {
public:
void add(Expression* exp, int val) {
map[exp] = val;
}
int loopUp(Expression* exp) {
return map[exp];
}
private:
unordered_map<Expression*, int> map;
};
class Expression {
public:
virtual int interpreter(Context* context) = 0;
virtual ~Expression() {}
};
class TerminalExpression : public Expression {
public:
string var;
TerminalExpression(string var) : var(var) {}
int interpreter(Context* context) override {
return context->loopUp(this);
}
};
class AddExperssion :public Expression {
public:
AddExperssion(Expression* e1, Expression* e2) : e1(e1), e2(e2) {}
int interpreter(Context* context) override {
return e1->interpreter(context) + e2->interpreter(context);
}
private:
Expression* e1;
Expression* e2;
};
class SubExperssion :public Expression {
public:
SubExperssion(Expression* e1, Expression* e2) : e1(e1), e2(e2) {}
int interpreter(Context* context) override {
return e1->interpreter(context) - e2->interpreter(context);
}
private:
Expression* e1;
Expression* e2;
};
int main()
{
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
Context* context = new Context();
Expression* a = new TerminalExpression("a");
Expression* b = new TerminalExpression("b");
Expression* c = new TerminalExpression("c");
context->add(a, 4);
context->add(b, 8);
context->add(c, 2);
AddExperssion* addExpression = new AddExperssion(a, b);
SubExperssion* subExpression = new SubExperssion(addExpression, c);
cout << subExpression->interpreter(context) << endl;
delete context;
delete a;
delete b;
delete c;
delete subExpression;
delete addExpression;
}
class Expression;
class Context {
public:
void add(Expression* exp) {
map[exp] = true;
}
bool loopUp(Expression* exp) {
return map[exp];
}
private:
unordered_map<Expression*, bool> map;
};
class Expression {
public:
virtual int interpreter(Context* context) = 0;
virtual ~Expression() {}
};
class TerminalExpression : public Expression {
public:
TerminalExpression(string date) : data(data) {}
int interpreter(Context* context) override {
return context->loopUp(this);
}
private:
string data;
};
class AndExperssion :public Expression {
public:
AndExperssion(Expression* e1, Expression* e2) : e1(e1), e2(e2) {}
int interpreter(Context* context) override {
return e1->interpreter(context) && e2->interpreter(context);
}
private:
Expression* e1;
Expression* e2;
};
class OrExperssion :public Expression {
public:
OrExperssion(Expression* e1, Expression* e2) : e1(e1), e2(e2) {}
int interpreter(Context* context) override {
return e1->interpreter(context) || e2->interpreter(context);
}
private:
Expression* e1;
Expression* e2;
};
int main()
{
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
Context* context = new Context();
Expression* robert = new TerminalExpression("Robert");
Expression* john = new TerminalExpression("John");
OrExperssion* orExpression = new OrExperssion(robert, john);
context->add(robert);
context->add(john);
cout << orExpression->interpreter(context);
delete robert;
delete john;
delete orExpression;
}
二三:访问者模式
要将数据结构与数据操作分离。
在访问者模式(Visitor Pattern)中,我们使用了一个访问者类,它改变了元素类的执行算法。通过这种方式,元素的执行算法可以随着访问者改变而改变。这种类型的设计模式属于行为型模式。根据模式,元素对象已接受访问者对象,这样访问者对象就可以处理元素对象上的操作。
class Paper;
class Cuprum;
class Company {
public:
virtual void create(Paper* element) = 0;
virtual void create(Cuprum* element) = 0;
virtual ~Company() {}
};
class Material {
public:
virtual void accept(Company* cmp) = 0;
virtual ~Material() {}
};
class Paper :public Material {
public:
void accept(Company* visitor) {
visitor->create(this);
cout << "纸" << endl;
}
};
class Cuprum :public Material {
public:
void accept(Company* visitor) {
visitor->create(this);
cout << "铜" << endl;
}
};
class ArtCompany : public Company {
public:
virtual void create(Paper* element) {
cout << "制造佛像,材料: ";
}
virtual void create(Cuprum* element) {
cout << "制造仿画,材料: ";
}
};
class Mint : public Company {
public:
virtual void create(Paper* element) {
cout << "制造硬币,材料: ";
}
virtual void create(Cuprum* element) {
cout << "制造纸币,材料: ";
}
};
class SetMaterial {
public:
void accept(Company* cmp) {
for (auto e : list) {
e->accept(cmp);
}
}
void add(Material* me) {
list.push_back(me);
}
private:
vector<Material*> list;
};
int main()
{
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
Company* artCompany = new ArtCompany;
Company* mint = new Mint;
Paper* paper = new Paper();
Cuprum* cuprum = new Cuprum();
SetMaterial* setMaterial = new SetMaterial();
setMaterial->add(paper);
setMaterial->add(cuprum);
setMaterial->accept(artCompany);
setMaterial->accept(mint);
delete artCompany;
delete paper;
delete cuprum;
delete setMaterial;
}
设计原则
单一职责原则
就一个类而言,应该仅有一个引起它变化的原因。
如果一个类承担的职责过多,就等于把这些职责耦合在一起,一个职责的变化可能会削弱或者一直这个类完成其他职责的能力,这种耦合会导致脆弱的设计,当变化发生时,设计会遭受到意想不到的破坏。
说人话:
开放-封闭原则
面对需求,增加或者修改功能,通过类扩展实现,而不是修改类本身实现。对扩展开放,对修改封闭。
也就是如果增加一个新功能,要通过重写一个新类实现,而不是修改现有的类。
可以通过继承多态实现。需求类有公共父类,如果增加新功能,只需要增加一个功能类即可。
里氏代换原则
子类必须能够替换掉他们的父类。
当一个类具有某个类的所有方法时,可以继承这个类。使用时,用父类指针指向子类对象,实现复用。
当父类的某个函数需要修改的时候,可以通过生成新的子类来进行。父类类型的木块无需修改,就可以扩展。
依赖倒置原则
- 高层模块不应该依赖底层模块,两个都应该依赖抽象。
- 抽象不应该依赖细节,细节应该依赖抽象。
- 面向接口编程,不要面对实现编程。
高层模块和底层模块通过稳定的接口实现联系,而不是直接联系,这样可以各自修改,而不影响其他模块。
迪米特法则
迪米特法则(Law of demeter,缩写是LOD)要求:一个对象应该对其他对象保持最少了解, 通缩的讲就是一个类对自己依赖的类知道的越少越好,也就是对于被依赖的类,向外公开的方法应该尽可能的少。
迪米特法则还有一种解释:Only talk to your immediate friends,即只与直接朋友通信.首先来解释编程中的朋友:两个对象之间的耦合关系称之为朋友,通常有依赖,关联,聚合和组成等.而直接朋友则通常表现为关联,聚合和组成关系,即两个对象之间联系更为紧密,通常以成员变量,方法的参数和返回值的形式出现.
那么为什么说是要与直接朋友通信呢?观察直接朋友出现的地方,我们发现在直接朋友出现的地方,大部分情况下可以接口或者父类来代替,可以增加灵活性. (需要注意,在考虑这个问题的时候,我们只考虑新增的类,而忽视java为我们提供的基础类.)
迪米特法则强调了一下两点:
- 第一要义:从被依赖者的角度来说:只暴露应该暴露的方法或者属性,即在编写相关的类的时候确定方法/属性的权限
- 第二要义:从依赖者的角度来说,只依赖应该依赖的对象
例子:人关闭计算机
- 计算构件接口:有个关闭的函数。
- 系统类:继承自计算机构件类,重写了关闭系统的方法。
- 电源类:继承自计算机构件类,重写了关闭电源方法。
- 计算机接口类:有两个计算机构件属性。有个关闭方法。
- dell计算机类:继承自计算机接口类。重写了关闭方法。
- 人类:有个计算机属性,有个关闭计算机方法。
人类只依赖于计算机接口,计算机接口实现关闭系统的各个具体步骤。
class ComputerComponent {
public:
virtual void close() = 0;
virtual ~ComputerComponent() {}
};
class ComputerSystem:public ComputerComponent {
public:
void close() {
closeSystem();
cout << "关闭系统" << endl;
}
void closeSystem() {
cout << "执行关闭系统的各个步骤" << endl;
}
};
class ComputerPower : public ComputerComponent {
public:
void close() {
closePower();
cout << "关闭电源" << endl;
}
void closePower() {
cout << "执行关闭电源的各个步骤" << endl;
}
};
class Computer {
public:
Computer(ComputerComponent* pw, ComputerComponent* sy) :power(pw), system(sy) {};
virtual void close() = 0;
virtual ~Computer() {
delete power;
delete system;
}
protected:
ComputerComponent* power;
ComputerComponent* system;
};
class DellComputer:public Computer {
public:
DellComputer(ComputerComponent* pw, ComputerComponent* sy) :Computer(pw, sy) {}
void close() {
system->close();
power->close();
cout << "关闭完成" << endl;
}
};
class Person {
public:
Person(Computer* cp) :computer(cp) {}
void close()
{
computer->close();
}
private:
Computer* computer;
};
int main()
{
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
Computer* cp = new DellComputer(new ComputerPower(), new ComputerSystem());
Person* person = new Person(cp);
person->close();
delete cp;
delete person;
}
2024年最新整理的八股文。 包括计算机网络,操作系统,MySQL,linux,设计模式,数据结构和算法,等等。 题目来源于网友爆料,GZH摘录,CSDN等等。 根据考察知识点,将题目进行分类,方便背诵。