目录
一,设计模式
设计模式(Design Pattern)是一套被反复使用的、多数人知晓的、经过分类编目的代码设计经验的总结,使用设计模式是为了可重用代码、让代码更容易被他人理解。
二,23种设计模式的分类
根据其目的可分为创建型、结构型和行为型三种设计模式。
- 创建型模式主要用于创建对象。
- 结构型模式主要用于处理类或对象的组合。
- 行为型模式主要用于描述对类或对象怎样交互和怎样分配职责。
根据范围,即模式主要是用于处理类之间关系还是处理对象之间的关系,可分为类模式和对象模式两种:
- 类模式处理类和子类之间的关系,这些关系通过继承建立,在编译时刻就被确定下来,是属于静态的。
- 对象模式处理对象间的关系,这些关系在运行时时刻变化,更具动态性。
范围\目的 | 创建型模式 | 结构型模式 | 行为型模式 |
类模式 | 工厂方法模式 | (类)适配器模式 | 解释器模式 模板方法模式 |
对象模式 | 抽象工厂模式 建造者模式 原型模式 单例模式 | (对象)适配器模式 桥接模式 组合模式 装饰模式 外观模式 享元模式 代理模式 | 职责链模式 命令模式 迭代器模式 中介者模式 备忘录模式 观察者模式 状态模式 策略模式 访问者模式 |
三,创建型模式
1,工厂方法模式
(1)简单工厂模式
简单工厂模式,不属于23种工厂模式。
C语言很难真正实现设计模式,以简单工厂模式为例,假设天神既可以创造猫,也可以创造狗:
#include <stdio.h>
typedef void(*OutFun)(void*);
typedef struct Cat
{
char *catName;
OutFun outFun;
}Cat;
typedef struct Dog
{
char *dogName;
OutFun outFun;
}Dog;
enum Animal
{
CAT,
DOG
};
void AnimalOutFun(void* p)
{
printf("I'm %s\n", (char *)*((char **)p));
}
void* GetAnimal(int type)
{
if (type == CAT)
{
Cat *cat = (Cat *)malloc(sizeof(Cat));
cat->catName = "cat1";
cat->outFun = AnimalOutFun;
return cat;
}
if (type == DOG)
{
Dog* dog = (Dog *)malloc(sizeof(Dog));
dog->dogName = "dog1";
dog->outFun = AnimalOutFun;
return dog;
}
return NULL;
}
int main()
{
Cat *cat = (Cat *)GetAnimal(CAT);
cat->outFun(cat);
Dog* dog = (Dog *)GetAnimal(DOG);
dog->outFun(dog);
return 0;
}
运行结果:
I'm cat1
I'm dog1
其中GetAnimal函数就是工厂方法。
正如这个代码一样,C语言实现设计模式会很复杂,而 cat->outFun(cat) 这种自己处理自己的代码更是满天飞。
用C++实现同样的功能,头文件:
class Animals
{
public:
virtual void OutFun() {};
};
class Cat :public Animals
{
public:
void OutFun();
};
class Dog :public Animals
{
public:
void OutFun();
};
class Factory
{
public:
static Animals* GetAnimal(int type);
};
cpp:
enum Animal
{
CAT,
DOG
};
void Cat::OutFun() {
cout << "I'm Cat\n";
}
void Dog::OutFun() {
cout << "I'm Dog\n";
}
Animals* Factory::GetAnimal(int type)
{
if (type == CAT)
{
Animals* res = new Cat();
return res;
}
if (type == DOG)
{
Animals* res = new Dog();
return res;
}
return NULL;
}
int main()
{
Animals *cat = Factory::GetAnimal(CAT);
cat->OutFun();
Animals* dog = Factory::GetAnimal(DOG);
dog->OutFun();
return 0;
}
简单工厂模式由抽象产品类(Animals)、具体产品类(Cat等)、工厂(GetAnimal)组成,抽象产品类和工厂类可以合并。
合并后的头文件:
class Animals
{
public:
virtual void OutFun() {};
static Animals* GetAnimal(int type);
};
class Cat :public Animals
{
public:
void OutFun();
};
class Dog :public Animals
{
public:
void OutFun();
};
cpp:
enum Animal
{
CAT,
DOG
};
void Cat::OutFun() {
cout << "I'm Cat\n";
}
void Dog::OutFun() {
cout << "I'm Dog\n";
}
Animals* Animals::GetAnimal(int type)
{
if (type == CAT)
{
Animals* res = new Cat();
return res;
}
if (type == DOG)
{
Animals* res = new Dog();
return res;
}
return NULL;
}
int main()
{
Animals *cat = Animals::GetAnimal(CAT);
cat->OutFun();
Animals* dog = Animals::GetAnimal(DOG);
dog->OutFun();
return 0;
}
因为工厂方法通常可以是静态函数,所以简单工厂模式也叫静态工厂模式。
(2)工厂方法模式
简单工厂模式中,每次新增产品都需要修改工厂,不符合开闭原则。
工厂方法模式由抽象产品类(Animals)、具体产品类(Cat等)、抽象工厂(Factory)、具体工厂(CatFactory等)组成
头文件:
class Animals
{
public:
virtual void OutFun() =0;
};
class Cat :public Animals
{
public:
void OutFun();
};
class Dog :public Animals
{
public:
void OutFun();
};
class Factory
{
public:
virtual Animals* GetAnimal() =0;
};
class CatFactory :public Factory
{
public:
Animals* GetAnimal();
};
class DogFactory :public Factory
{
public:
Animals* GetAnimal();
};
cpp:
void Cat::OutFun() {
cout << "I'm Cat\n";
}
void Dog::OutFun() {
cout << "I'm Dog\n";
}
Animals* CatFactory::GetAnimal()
{
Animals* res = new Cat();
return res;
}
Animals* DogFactory::GetAnimal()
{
Animals* res = new Dog();
return res;
}
int main()
{
CatFactory fac1;
Animals *cat = fac1.GetAnimal();
cat->OutFun();
DogFactory fac2;
Animals* dog = fac2.GetAnimal();
dog->OutFun();
return 0;
}
工厂方法模式也叫虚拟构造器模式、多态工厂模式。
新增产品不需要修改工厂,只需要新增具体工厂,符合开闭原则。
但缺点很明显,类太多了。
2,抽象工厂模式
有很多交叉分类场景,比如天神既可以创造猫,也可以创造狗,但是创造的都是成年动物,而小猫和小狗是阎王安排投胎去的,那阎王也需要自己的工厂。
大猫大狗构成一个产品簇,小猫小狗构成一个产品簇,产品簇的分类和产品的分类构成交叉分类。
根据2个分类系能否用聚合形式表示,分为2种情况:
(1)可聚合形式
如果可以单独的表示产品簇的信息,然后用聚合的形式表示具体产品,那么具体工厂数是产品簇的分类数 + 产品的分类数。
class Animals
{
public:
virtual void OutFun() {};
static Animals* GetAnimal(int type);
};
class Cat :public Animals
{
public:
void OutFun() {
cout << "cat\n";
}
};
class Dog :public Animals
{
public:
void OutFun() {
cout << "dog\n";
}
};
enum Animal
{
CAT,
DOG
};
Animals* Animals::GetAnimal(int type)
{
if (type == CAT)
{
Animals* res = new Cat();
return res;
}
if (type == DOG)
{
Animals* res = new Dog();
return res;
}
return NULL;
}
class Size
{
public:
virtual void OutFun() {};
static Size* GetSize(int type);
};
class Big :public Size
{
public:
void OutFun() {
cout << "I'm big ";
}
};
class Small :public Size
{
public:
void OutFun() {
cout << "I'm small ";
}
};
enum Size_
{
BIG,
SMALL
};
Size* Size::GetSize(int type)
{
if (type == BIG)
{
Size* res = new Big();
return res;
}
if (type == SMALL)
{
Size* res = new Small();
return res;
}
return NULL;
}
class FinalAnimal {
public:
FinalAnimal(int animalType, int sizeType) {
animal = Animals::GetAnimal(animalType);
size = Size::GetSize(sizeType);
}
void OutFun() {
size->OutFun();
animal->OutFun();
}
private:
Animals* animal;
Size* size;
};
int main()
{
FinalAnimal(CAT, BIG).OutFun();
FinalAnimal(DOG, SMALL).OutFun();
return 0;
}
输出:
(2)不可聚合形式
如果不能单独的表示产品簇的信息,那么具体工厂数 = 产品簇的分类数 * 产品的分类数。
class Animals {
public:
virtual void OutFun() {};
static Animals* GetAnimal(int animalType, int sizeType);
};
class BigAnimals :public Animals
{
public:
virtual void OutFun() {};
static BigAnimals* GetAnimal(int type);
};
class BigCat :public BigAnimals
{
public:
void OutFun() {
cout << "I'm big cat\n";
}
};
class BigDog :public BigAnimals
{
public:
void OutFun() {
cout << "I'm big dog\n";
}
};
enum Animal
{
CAT,
DOG
};
BigAnimals* BigAnimals::GetAnimal(int type)
{
if (type == CAT)
{
BigAnimals* res = new BigCat();
return res;
}
if (type == DOG)
{
BigAnimals* res = new BigDog();
return res;
}
return NULL;
}
class SmallAnimals :public Animals
{
public:
virtual void OutFun() {};
static SmallAnimals* GetAnimal(int type);
};
class SmallCat :public SmallAnimals
{
public:
void OutFun() {
cout << "I'm small cat\n";
}
};
class SmallDog :public SmallAnimals
{
public:
void OutFun() {
cout << "I'm small dog\n";
}
};
SmallAnimals* SmallAnimals::GetAnimal(int type)
{
if (type == CAT)
{
SmallAnimals* res = new SmallCat();
return res;
}
if (type == DOG)
{
SmallAnimals* res = new SmallDog();
return res;
}
return NULL;
}
enum Size_
{
BIG,
SMALL
};
Animals* Animals::GetAnimal(int animalType, int sizeType) {
if (sizeType == BIG) return (Animals*)BigAnimals::GetAnimal(animalType);
if (sizeType == SMALL) return (Animals*)SmallAnimals::GetAnimal(animalType);
return NULL;
}
int main()
{
Animals::GetAnimal(CAT, BIG)->OutFun();
Animals::GetAnimal(DOG, SMALL)->OutFun();
return 0;
}
输出:
如果要新增产品簇(老狗和老猫),那么只需要新增一个OldAnimals类,新增一个GetAnimal方法即可。
如果要新增产品(大马和小马),那么每个产品簇都需要修改。
3,建造者模式
建造者模式是用来解决建造复杂对象的问题。
复杂对象:
class Head
{
public:
void set(string color, int weight){}
private:
string color;
int weight;
};
class Hand
{
public:
void set(string color, int weight) {}
private:
string color;
int weight;
};
class Human
{
public:
void setHead(string color, int weight)
{
head.set(color, weight);
}
void setHand(string color, int weight)
{
hand.set(color, weight);
}
private:
Head head;
Hand hand;
};
建造基类:
class Builder
{
public:
Builder()
{
human = new Human();
}
Human* get()
{
return human;
}
virtual void setHead() {}
virtual void setHand() {}
protected:
Human* human;
};
建造子类:
class ManBuilder:public Builder
{
void setHead()
{
human->setHead("yellow", 10);
}
void setHand()
{
human->setHand("yellow", 8);
}
};
class WomanBuilder :public Builder
{
void setHead()
{
human->setHead("white", 9);
}
void setHand()
{
human->setHand("white", 7);
}
};
4,原型模式
原型模式就是抽象类提供clone函数,具体类实现具体的clone函数。
代码:
class Person
{
public:
virtual Person* clone()
{
return new Person();
}
void show()
{
cout << "id=" << id << endl;
cout << "name=" << name << endl;
}
protected:
int id = 0;
string name;
};
class Student :public Person
{
public:
Student(string name)
{
this->name = name;
this->id = 0;
}
Student* clone()
{
Student* s = new Student(this->name);
s->id = this->id + 1;
return s;
}
};
int main()
{
Person* p1 = new Student("zsan");
p1->show();
Person* p2 = p1->clone();
p2->show();
return 0;
}
5,单例模式
单例模式是保证类只有一个实例的模式,非常实用。
(1) 饿汉式(线程安全)
恶汉式采用的是static成员,因为进程初始化时就分配好了,所以叫恶汉式。
第一种写法(引用):
class SingleA {
public:
static SingleA& GetSingleA() {
return s;
}
static SingleA s;
private:
SingleA(const SingleA&) = delete;
SingleA(SingleA&&) = delete;
SingleA& operator=(const SingleA&) = delete;
SingleA& operator=(SingleA&&) = delete;
SingleA() = default;
~SingleA() = default;
};
SingleA SingleA::s;
第二种写法(指针):
class SingleA {
public:
static SingleA* GetSingleA() {
return s;
}
static SingleA* s;
private:
SingleA(const SingleA&) = delete;
SingleA(SingleA&&) = delete;
SingleA& operator=(const SingleA&) = delete;
SingleA& operator=(SingleA&&) = delete;
SingleA() = default;
~SingleA() = default;
};
SingleA* SingleA::s = new SingleA();
(2)懒汉式
懒汉式到第一次申请实例的时候才会分配。
第一种写法(引用,推荐写法,线程安全):
class SingleA {
public:
static SingleA& GetSingleA() {
static SingleA s;
return s;
}
private:
SingleA(const SingleA&) = delete;
SingleA(SingleA&&) = delete;
SingleA& operator=(const SingleA&) = delete;
SingleA& operator=(SingleA&&) = delete;
SingleA() = default;
~SingleA() = default;
};
第二种写法(指针):
class SingleA {
public:
static SingleA* GetSingleA() {
static SingleA* s = new SingleA();
return s;
}
private:
SingleA(const SingleA&) = delete;
SingleA(SingleA&&) = delete;
SingleA& operator=(const SingleA&) = delete;
SingleA& operator=(SingleA&&) = delete;
SingleA() = default;
~SingleA() = default;
};
第三种写法(带判断的指针):
class SingleA {
public:
static SingleA* GetSingleA() {
if (!s) s = new SingleA();
return s;
}
static SingleA* s;
private:
SingleA(const SingleA&) = delete;
SingleA(SingleA&&) = delete;
SingleA& operator=(const SingleA&) = delete;
SingleA& operator=(SingleA&&) = delete;
SingleA() = default;
~SingleA() = default;
};
SingleA* SingleA::s;
ps:引用的方式也可以用这种带判断的懒汉式写法,只需要把(!s)改成对应的判断是否初始化的条件即可。
(3)懒汉式的多线程安全写法
上面的懒汉式写法,只有第一种是安全的,用new的方式的都是不安全的,但是可以通过加锁变成安全的。
第一种写法(多线程安全写法):
class SingleA {
public:
static SingleA* GetSingleA() {
Lock();
if (!s) s = new SingleA();
Unlock();
return s;
}
static SingleA* s;
private:
SingleA(const SingleA&) = delete;
SingleA(SingleA&&) = delete;
SingleA& operator=(const SingleA&) = delete;
SingleA& operator=(SingleA&&) = delete;
SingleA() = default;
~SingleA() = default;
};
SingleA* SingleA::s;
第二种写法(多线程安全的double check写法):
class SingleA {
public:
static SingleA* GetSingleA() {
if (!s) {
Lock();
if (!s) s = new SingleA();
Unlock();
}
return s;
}
static SingleA* s;
private:
SingleA(const SingleA&) = delete;
SingleA(SingleA&&) = delete;
SingleA& operator=(const SingleA&) = delete;
SingleA& operator=(SingleA&&) = delete;
SingleA() = default;
~SingleA() = default;
};
SingleA* SingleA::s;
采用double check可以减少锁的次数,减少性能消耗。
(4)单例模板
需要用到CRTP实现
template<typename T>
class SingleA {
public:
static T& GetSingleA() {
static T s;
return s;
}
protected:
SingleA(const SingleA&) = delete;
SingleA(SingleA&&) = delete;
SingleA& operator=(const SingleA&) = delete;
SingleA& operator=(SingleA&&) = delete;
SingleA() = default;
~SingleA() = default;
};
class A :public SingleA<A>
{
friend class SingleA<A>;
public:
int x;
private:
A() = default;
};
父类的构造函数要声明为protected,子类还要声明父类为友元类。
四,结构型模式
1,适配器模式
适配器模式是把老的接口封装成新的接口。
(1)类适配器模式
类适配器模式是用新的类继承2个类。
如果老接口是具体类:
class A
{
public:
void open() {};
void show() {};
};
class C
{
public:
virtual void openAndShow() {};
};
class NewA :public A, public C
{
public:
void openAndShow()
{
open();
show();
}
};
如果老接口是抽象接口+具体实现类,那就不能用类适配器模式。
(2)对象适配器模式
对象适配器模式是新的类聚合老接口,继承目标类。
如果老接口是具体类:
class A
{
public:
void open() {};
void show() {};
};
class C
{
public:
virtual void openAndShow() {};
};
class NewA :public C
{
public:
NewA(A* p)
{
opt = p;
}
void openAndShow()
{
opt->open();
opt->show();
}
private:
A* opt;
};
如果老接口是抽象接口+具体实现类,那就只能用对象适配器模式。
原接口AB和目标接口C:
class A
{
public:
virtual void open() {};
virtual void show() {};
};
class B :public A
{
public:
void open() {};
void show() {};
};
class C
{
public:
virtual void openAndShow() {};
};
class NewB :public C
{
public:
NewB(A* p)
{
opt = p;
}
void openAndShow()
{
opt->open();
opt->show();
}
private:
A* opt;
};
2,桥接模式
桥接模式就是把包含2组信息的类拆分成2个类,通过聚合的形式桥接到一起。
这样,就只需要独立定义每一组信息的不同的类,而不需要交叉定义所有的类。
如红苹果、绿苹果、红桃子、绿桃子四个子类显得很冗余,再新增水果或颜色就更麻烦了,我们把信息拆分成水果类型和颜色2个类。
抽象颜色和具体颜色:
class Color
{
public:
virtual void show()
{
cout << col;
}
virtual bool canEat() { return true; };
protected:
string col;
};
class Red :public Color
{
public:
Red()
{
col = "red";
}
bool canEat()
{
return true;
}
};
class Green :public Color
{
public:
Green()
{
col = "green";
}
bool canEat()
{
return false;
}
};
承载简单逻辑:红的可以吃,绿的不能吃。
抽象水果和具体水果:
class Fruit
{
public:
void show()
{
if (color->canEat())
{
howEat();
}
else {
cout << "cannot eat\n";
}
}
void setColor(Color* col)
{
color = col;
}
protected:
virtual void howEat() {};
private:
Color* color;
};
class Apple :public Fruit
{
void howEat()
{
cout << "eat directly\n";
}
};
class Peach :public Fruit
{
void howEat()
{
cout << "skin and eat\n";
}
};
抽象水果类聚合抽象颜色类。
调用示例:
int main()
{
Apple ap;
ap.setColor(new Red());
ap.show();
ap.setColor(new Green());
ap.show();
Peach pe;
pe.setColor(new Red());
pe.show();
pe.setColor(new Green());
pe.show();
}
输出:
eat directly
cannot eat
skin and eat
cannot eat
即4种水果的情况。
3,组合模式
组合模式就是把一些有相同处理的对象放到一起,形成树的结构,递归调用。
节点基类:
class Component
{
public:
virtual void Run() {};
};
组合节点:
class Composite : public Component
{
public:
void Add(Component* c)
{
v.push_back(c);
}
void Run()
{
cout << "Composite run.\n";
for (auto &vi : v) {
vi->Run();
}
}
private:
vector<Component*>v;
};
叶子节点:
class A : public Component
{
public:
void Run()
{
cout << "A run.\n";
}
};
class B : public Component
{
public:
void Run()
{
cout << "B run.\n";
}
};
调用示例:
int main()
{
Composite* s = new Composite();
Component* a = new A();
Component* b = new B();
s->Add(a);
s->Add(b);
s->Run();
return 0;
}
输出:
Composite run.
A run.
B run.
4,装饰模式
装饰模式就是通过额外的装饰类来给对象添加新的行为。
抽象基类和具体类:
class Phone
{
public:
virtual void show() = 0;
};
class Andr :public Phone
{
public:
void show()
{
cout << "\nAndroid";
}
};
class Ipho :public Phone
{
public:
void show()
{
cout << "\nIphone";
}
};
抽象装饰和具体装饰:(抽象装饰要继承抽象基类)
class Decorator :public Phone
{
public:
Decorator(Phone* p)
{
mphone = p;
}
void show()
{
mphone->show();
}
protected:
Phone* mphone;
};
class NameDecorator :public Decorator
{
using Decorator::Decorator;
public:
void setName(string name)
{
this->name = name;
}
void show()
{
mphone->show();
cout << name;
}
private:
string name;
};
class IdDecorator :public Decorator
{
using Decorator::Decorator;
public:
void setId(int id)
{
this->id = id;
}
void show()
{
mphone->show();
cout << id;
}
private:
int id;
};
调用示例:
int main()
{
Phone* p1 = new Andr();
p1->show();
Phone* p2 = new Ipho();
p2->show();
NameDecorator* p3 = new NameDecorator(p1);
p3->setName(" Honor ");
p3->show();
IdDecorator* p4 = new IdDecorator(p3);
p4->setId(13);
p4->show();
}
输出:
Android
Iphone
Android Honor
Android Honor 13
5,外观模式
外观模式也叫门面模式,通过调用封装,使得对外的接口稳定且简洁。
class A
{
public:
void run() {};
};
class B
{
public:
void run() {};
};
class C
{
public:
void run() {};
};
class Facade
{
public:
Facade()
{
a = new A;
b = new B;
c = new C;
}
void fun1()
{
a->run();
b->run();
}
void fun2()
{
c->run();
}
private:
A* a;
B* b;
C* c;
};
6,享元模式
享元模式就是利用共享技术存储大量小对象。
比如要存储大量字符串,对于重复的字符串可以合并。
字符串包装类:
class MyString
{
public:
MyString(string s)
{
this->s = s;
}
bool operator< (MyString s) const
{
return this->s < s.s;
}
string GetData() const
{
return s;
}
private:
string s;
};
字符串集合类:
struct cmp
{
bool operator()(MyString* m1, MyString* m2)const
{
return *m1 < *m2;
}
};
class StringSet
{
public:
MyString* GetMyString(string s)
{
MyString* ms = new MyString(s);
auto it = se.find(ms);
if (it == se.end()) {
se.insert(ms);
return ms;
}
return *it;
}
void Push(string s)
{
GetMyString(s);
}
int size()
{
return se.size();
}
private:
set < MyString*, cmp> se;
};
调用示例:
int main()
{
StringSet s;
s.Push("123");
s.Push("123");
MyString* it = s.GetMyString("123");
cout << it->GetData() << endl;
cout << s.size();
return 0;
}
输出:
123
1
7,代理模式
代理模式是用一个类的对象控制另外一个类的对象,2个类有相同的接口。
按照使用场景,可以分为若干种代理场景。
以虚拟代理为例:
虚拟代理是为了实现延迟加载,比如一个网页中有图片,加载网页时可以不加载图片,等到一定时机(比如滑动滚轮,图片进入视域范围)才加载。
抽象图片基类:
class ImageBase
{
public:
ImageBase(string name) :fileName(name) {}
virtual void show() {}
protected:
string fileName;
};
图片类:
class Image :public ImageBase
{
public:
Image(string name) : ImageBase(name) {}
void show() {
cout << "show image " << fileName;
}
};
图片代理类:
class ImageProxy:public ImageBase
{
public:
ImageProxy(string name) : ImageBase(name) {}
void show() {
if (!img)img = new Image(fileName);
img->show();
}
private:
Image* img;
};
调用示例:
int main()
{
ImageBase* img = new ImageProxy("123.png");
img->show();
return 0;
}
五,行为型模式
1,解释器模式
解释器模式也叫解析器模式,是把复杂的业务规则抽象成语法规则。
优点:把复杂抽象的逻辑,显式化成具体的表达式
缺点:小类太多,代码膨胀,性能也可能有影响。
以标准体重计算器为例:
数据:
class H
{
public:
static int h;
static void set(int h) {
H::h = h;
}
};
class W
{
public:
static int w;
static void set(int w) {
W::w = w;
}
};
int H::h = 0;
int W::w = 0;
纯数字表达式:
// 把字符串转化为整数
int atoi(const char *nptr, int radix) //copy from somebody
{
while (isspace((int)(unsigned char)*nptr)) ++nptr;/* skip whitespace */
int c = (int)(unsigned char)*nptr++;
int sign = c; /* save sign indication */
if (c == '-' || c == '+') c = (int)(unsigned char)*nptr++; /* skip sign */
int total = 0;
while (isdigit(c)) {
total = radix * total + (c - '0'); /* accumulate digit */
c = (int)(unsigned char)*nptr++; /* get next char */
}
if (sign == '-') return -total;
else return total; /* return result, negated if necessary */
}
class Data
{
public:
static int solve(string str) {
if (str == "H")return H::h;
if (str == "W")return W::w;
return atoi(str.data(), 10);
}
};
-和>组成的表达式:
class SubExp
{
public:
static int solve(string str) {
for (int i = str.size()-1; i > 0; i--) {
if (str[i] != '-')continue;
return SubExp::solve(str.substr(0, i)) - Data::solve(str.substr(i + 1, str.size() - i - 1));
}
return Data::solve(str);
}
};
class GreaterExp
{
public:
static int solve(string str) {
for (int i = 0; i < str.size(); i++) {
if (str[i] != '>')continue;
return SubExp::solve(str.substr(0, i)) > SubExp::solve(str.substr(i + 1, str.size() - i - 1));
}
return SubExp::solve(str);
}
};
对外接口:
int solve(string s)
{
return GreaterExp::solve(s);
}
优先级最低的表达式,作为表达式的入口。
承载业务规则的表达式:
bool thinMan(int height, int weight)
{
H::set(height);
W::set(weight);
return solve("H-105>W");
}
bool thinWoman(int height, int weight)
{
H::set(height);
W::set(weight);
return solve("H-108>W");
}
调用示例:
int main()
{
cout << thinMan(169, 67);
cout << thinWoman(165, 55);
cout << solve("3>2");
cout << solve("10-2-3");
return 0;
}
既支持计算常量表达式,也支持设定抽象表达式,往里传值。
2,模板方法模式
解释:
模板方法模式,就是父类规定了统一的流程,子类去实现流程每一步的细节。
场景一:
当我们教小孩穿衣服的时候,我们希望小孩知道,先穿上衣,再穿裤子,再穿鞋子,这个是固定的流程,和衣服的颜色无关,和裤子是运动裤还是牛仔裤也无关。
有了这个概念之后,再教孩子各种上衣怎么穿,各种裤子怎么穿,各种鞋子怎么穿,孩子的脑海中就会有知识图谱的概念了。
实现:
我们用父类规定穿衣服的流程,子类去实现各个步骤如何操作。
#include<iostream>
using namespace std;
class dress
{
virtual void dressCoat()=0;
virtual void dressPants()=0;
virtual void dressShoes()=0;
public:
void dressAll()
{
dressCoat();
dressPants();
dressShoes();
cout<<endl;
}
};
class dressRed:public dress
{
void dressCoat(){
cout<<"Red coat ";
}
void dressPants(){
cout<<"Red pants ";
}
void dressShoes(){
cout<<"Red shoes ";
}
};
class dressBlue:public dress
{
void dressCoat(){
cout<<"Blue coat ";
}
void dressPants(){
cout<<"Blue pants ";
}
void dressShoes(){
cout<<"Blue shoes ";
}
};
int main()
{
dressRed d;
d.dressAll();
dressBlue d2;
d2.dressAll();
return 0;
}
输出:
Red coat Red pants Red shoes
Blue coat Blue pants Blue shoes
C语言模拟:
C语言没有这种代码机制,可以用函数指针实现回调机制,实现类似的效果。
参考我的毕设作品:本科毕业设计
场景二:
如果各个子类的流程不完全相同,比如其中有一步有的子类不需要实现,那么我们可以把父类函数设为虚函数,提供一个空实现即可。
#include<iostream>
using namespace std;
class dress
{
virtual void dressCoat() {};
virtual void dressPants() {};
virtual void dressShoes() {};
public:
void dressAll()
{
dressCoat();
dressPants();
dressShoes();
cout << endl;
}
};
class dressRed :public dress
{
void dressCoat() {
cout << "Red coat ";
}
void dressPants() {
cout << "Red pants ";
}
void dressShoes() {
cout << "Red shoes ";
}
};
class dressBlue :public dress
{
void dressCoat() {
cout << "Blue coat ";
}
void dressPants() {
cout << "Blue pants ";
}
};
int main()
{
dressRed d;
d.dressAll();
dressBlue d2;
d2.dressAll();
return 0;
}
输出:
Red coat Red pants Red shoes
Blue coat Blue pants
CRTP + 模板方法模式
#include<iostream>
using namespace std;
template<typename T>
class dress
{
public:
void dressAll()
{
T* p = sub();
p->dressCoat();
p->dressPants();
p->dressShoes();
cout << endl;
}
protected:
void dressShoes() {}
T* sub() {
return static_cast<T*>(this);
}
};
class dressRed :public dress< dressRed>
{
public:
void dressCoat() {
cout << "Red coat ";
}
void dressPants() {
cout << "Red pants ";
}
void dressShoes() {
cout << "Red shoes ";
}
};
class dressBlue :public dress<dressBlue>
{
public:
void dressCoat() {
cout << "Blue coat ";
}
void dressPants() {
cout << "Blue pants ";
}
};
int main()
{
dressRed d;
d.dressAll();
dressBlue d2;
d2.dressAll();
return 0;
}
3,职责链模式
职责链模式就是一系列的处理过程依次执行,各部件可以自由的用链表的形式链接起来。
优点:降低了部件之间的耦合。
缺点:需要客户端进行组合链接,增加了客户端的复杂性。(从形式上可以任意组合链接,但从业务逻辑上可能有限制)
例一,抽象例子
我这里的例子比较简单,是无参的处理函数,实际上也可以把参数一直传递。
处理基类:
class Handle
{
public:
virtual void Run() {};
void SetNext(Handle* next)
{
this->next = next;
}
protected:
Handle* next = nullptr;
};
处理子类:
class HandleA : public Handle
{
public:
void Run()
{
cout << "HandleA run.\n";
if (next)next->Run();
}
};
class HandleB : public Handle
{
public:
void Run()
{
cout << "HandleB run.\n";
if (next)next->Run();
}
};
class HandleC : public Handle
{
public:
void Run()
{
cout << "HandleC run.\n";
if (next)next->Run();
}
};
调用示例:
int main()
{
Handle* a = new HandleA();
Handle* b = new HandleB();
Handle* c = new HandleC();
a->SetNext(b);
b->SetNext(c);
a->Run();
return 0;
}
输出:
HandleA run.
HandleB run.
HandleC run.
例二,告警例子
#include <iostream>
using namespace std;
class Responder {
public:
virtual ~Responder() {
}
void setSuccessor(Responder *s) {
successor = s;
}
//沿着链表处理
void handleAlert() {
if (this->canHandle()) {
this->handle();
}
else {
if (successor != nullptr) {
successor->handleAlert();
}
else {
cout << "NO Response" << endl;
//缺省处理
}
}
}
private:
virtual bool canHandle() = 0;
virtual void handle() = 0;
Responder *successor{ nullptr };
};
class EmailResponder : public Responder {
public:
~EmailResponder() {
}
private:
bool canHandle() override {
return false;
}
void handle() override {
std::cout << "email the alert" << std::endl;
}
}
;
class SMSResponder : public Responder {
public:
~SMSResponder() {
}
private:
bool canHandle() override {
return false;
}
void handle() override {
std::cout << "sms the alert" << std::endl;
}
};
class PhoneResponder : public Responder {
public:
~PhoneResponder() {
}
private:
bool canHandle() override {
return false;
}
void handle() override {
std::cout << "call to tell the alert" << std::endl;
}
};
class WhistleResponder : public Responder {
public:
~WhistleResponder() {
}
private:
bool canHandle() override {
return true;
}
void handle() override {
std::cout << "whistle to tell the alert" << std::endl;
}
};
using ResponderPtr = unique_ptr<Responder>;
int main() {
ResponderPtr responder1 = make_unique<EmailResponder>();
ResponderPtr responder2 = make_unique<SMSResponder>();
ResponderPtr responder3 = make_unique<PhoneResponder>();
ResponderPtr responder4 = make_unique<WhistleResponder>();
// 1- > 2 -> 3 ->4
responder1->setSuccessor(responder2.get());
responder2->setSuccessor(responder3.get());
responder3->setSuccessor(responder4.get());
responder1->handleAlert();
return 0;
}
职责链的变体:职责网,即不是链条而是有向图,更准确的说是偏序有向图。
gg浏览器的例子:
4,命令模式
优点:可以用来做批处理
缺点:每一个命令都要有对应的类,造成代码膨胀。
命令模式就是把不同命令封装成抽象类的不同子类,请求命令和处理命令可以分开。
处理命令的类:
class TV
{
public:
void open()
{
cout << "open ";
}
void close()
{
cout << "close ";
}
};
处理命令的抽象类和子类(每个子类封装一个处理命令的函数):
class Command
{
public:
virtual void run() {};
};
class OpenCommond :public Command
{
public:
OpenCommond(TV* p)
{
opt = p;
}
void run()
{
opt->open();
}
private:
TV* opt;
};
class CloseCommond :public Command
{
public:
CloseCommond(TV* p)
{
opt = p;
}
void run()
{
opt->close();
}
private:
TV* opt;
};
调用示例:
int main()
{
//请求命令
auto tv = std::make_unique<TV>();
std::vector<std::unique_ptr<Command>> script;
script.emplace_back(std::make_unique<OpenCommond>(tv.get()));
script.emplace_back(std::make_unique<CloseCommond>(tv.get()));
//处理命令
std::for_each(script.begin(), script.end(), [](auto &&command) { command->run(); });
return 0;
}
5,迭代器模式
迭代器模式就是像STL迭代器一样,隐藏内部实现提供通用操作的类。
抽象迭代器:
class Iterator
{
public:
virtual void getNext() {};
};
分段容器的具体迭代器:
template<typename T>
class RealIter:public Iterator
{
public:
RealIter(vector<T> &vec) :v(vec),id(0){};
void getNext() {
static auto it = v[0].begin();
Print(*it);
it++;
while (it == v[id].end()) {
++id;
if (id >= v.size())id = 0;
it= v[id].begin();
}
}
private:
vector<T>&v;
int id;
};
其中的Print函数来自输入输出模板
抽象容器类:
class DataBase
{
public:
virtual unsigned size() { return 0; };
virtual void getNext() {};
virtual void add(int a) {};
virtual void add(int a, int b) {};
void showAll() {
for (int i = 0; i < size(); i++) {
getNext();
}
}
};
分段Map的具体容器类:
class MyMap :public DataBase
{
public:
MyMap() :it(vm) {
vm.resize(10);
}
unsigned size() {
return s;
}
void add(int a, int b)
{
int k = a % 10;
if (vm[k].find(a) == vm[k].end())s++;
vm[k][a] = b;
}
void getNext() {
it.getNext();
}
private:
vector<map<int, int>>vm;
unsigned s;
RealIter<map<int, int>> it;
};
调用示例:
int main()
{
map<int, int>mm;
DataBase* m = new MyMap();
m->add(1, 2);
m->add(10, 3);
m->add(15, 4);
m->add(27, 5);
m->add(44, 6);
m->add(27, 7);
m->add(100, 8);
m->add(222, 9);
m->showAll();
return 0;
}
输出:
10 3
100 8
1 2
222 9
44 6
15 4
27 7
我是按照key的个位数来划分成10段,每段是个独立的map,所以输出也是按照个位数的顺序。
6,中介者模式
中介者模式,就是双观察者模式,以租房为例,客户可以收到所有房东信息,房东可以收到所有客户信息。
用户基类和中介类:
class Mediator;
class Person
{
public:
void SetMeditor(Mediator* m)
{
med = m;
}
virtual void SendMessage(string s) {};
virtual void GetMessage(string s) {};
protected:
Mediator* med;
string name;
};
class Mediator
{
public:
void PushRenters(Person* p)
{
renters.push_back(p);
}
void PushLandlord(Person* p)
{
landlord.push_back(p);
}
void SendMessage(Person* p, string s)
{
auto it = find(renters.begin(), renters.end(), p);
if (it != renters.end()) {
for (auto& p : landlord)p->GetMessage(s);
}
else {
for (auto& p : renters)p->GetMessage(s);
}
}
private:
vector<Person*> renters;
vector<Person*> landlord;
};
租客子类和房东子类:
class Renter :public Person
{
public:
Renter(Mediator* m, string s)
{
SetMeditor(m);
name = s;
}
void SendMessage(string s)
{
med->SendMessage(this, s);
}
void GetMessage(string s)
{
cout << name << " get message:" << s << endl;
}
};
class Landlord :public Person
{
public:
Landlord(Mediator* m, string s)
{
SetMeditor(m);
name = s;
}
void SendMessage(string s)
{
med->SendMessage(this, s);
}
void GetMessage(string s)
{
cout << name << " get message:" << s << endl;
}
};
调用示例:
int main()
{
Mediator m;
Renter r1(&m, "r1");
Renter r2(&m, "r2");
Landlord l1(&m, "l1");
Landlord l2(&m, "l2");
m.PushRenters(&r1);
m.PushRenters(&r2);
m.PushLandlord(&l1);
m.PushLandlord(&l2);
r1.SendMessage("r1 want a room");
l1.SendMessage("l1 has a room");
return 0;
}
输出:
l1 get message:r1 want a room
l2 get message:r1 want a room
r1 get message:l1 has a room
r2 get message:l1 has a room
7,备忘录模式
备忘录模式就是为一个类专门创建一个保存状态数据的类。
保存状态数据的类:
class KeyValue
{
public:
KeyValue(int val)
{
this->val = val;
}
private:
int val;
friend class A;
};
原类本身:
class A
{
public:
KeyValue save()
{
return KeyValue(val);
}
void load(KeyValue k)
{
val = k.val;
}
void run()
{
// do sth
}
private:
int val;
int sthNoNeedSave;
};
还可以再搞个管理类,把KeyValue的所有对象放在一个栈里面。
因为现在序列化方法很成熟了,这个设计模式基本上没用了。
8,观察者模式
观察者模式就是在目标类中维护一个观察者列表,当更新条件触发时自动通知所有注册的观察者。
抽象观察者和具体观察者:
class Observer
{
public:
virtual void Update()
{
cout << "updated!\n";
}
};
class Observer1 :public Observer
{
public:
virtual void Update()
{
cout << "Observer1 updated!\n";
}
};
class Observer2 :public Observer
{
public:
virtual void Update()
{
cout << "Observer2 updated!\n";
}
};
抽象目标和具体目标:
class Subject
{
public:
void Register(Observer* ob)
{
vobs.push_back(ob);
}
void Unregister(Observer* ob)
{
auto it = find(vobs.begin(), vobs.end(), ob);
if (it != vobs.end())vobs.erase(it);
}
void Notify()
{
for (auto &ob : vobs)ob->Update();
}
private:
vector<Observer*>vobs;
};
class SubSubject: public Subject
{
public:
void set(int x)
{
this->x = x;
if (x / 100 != x0 / 100) {
x0 = x;
Notify();
}
}
void add()
{
set(x + 1);
}
private:
int x = 0;
int x0 = 0;
};
我设定的更新条件是被检测的x的值发生大的变化,即百位即以上的位发生变化时,通知观察者。
调用示例:
int main()
{
SubSubject sub;
sub.Register(new Observer1());
sub.Register(new Observer2());
sub.set(98);
sub.add();
sub.add();
}
输出:
Observer1 updated!
Observer2 updated!
9,状态模式
状态模式就是把状态机拆开,每个状态由一个类来处理。
状态基类:
class State
{
public:
virtual void run() {}
virtual State* GetNext() {};
};
状态子类(单例):
template<typename T>
class SingleA {
public:
static T& GetSingleA() {
static T s;
return s;
}
protected:
SingleA(const SingleA&) = delete;
SingleA(SingleA&&) = delete;
SingleA& operator=(const SingleA&) = delete;
SingleA& operator=(SingleA&&) = delete;
SingleA() = default;
~SingleA() = default;
};
class StateA :public State,public SingleA<StateA>
{
friend class SingleA<StateA>;
public:
void run()
{
// do sth
}
State* GetNext();
};
class StateB :public State, public SingleA<StateB>
{
friend class SingleA<StateB>;
public:
void run()
{
// do sth
}
State* GetNext();
};
State* StateA::GetNext()
{
return &StateB::GetSingleA();
}
State* StateB::GetNext()
{
return &StateA::GetSingleA();
}
处理类:
class Process
{
public:
Process(State* state)
{
this->state = state;
}
void run()
{
state->run();
state = state->GetNext();
}
private:
State* state;
};
10,策略模式
策略模式和模板方法模式非常像,我个人理解场景是一样的,实现方式略有区别。
策略模式是把有差异的操作抽象成虚基类(策略工厂),不同的场景各自继承虚基类实现差异化操作,主流程类把虚基类的指针作为数据成员,用它来调用实际的操作(多态)。
代码:
#include<iostream>
using namespace std;
class dress
{
public:
virtual void dressCoat() = 0;
virtual void dressPants() = 0;
virtual void dressShoes() = 0;
};
class dressRed :public dress
{
public:
void dressCoat() {
cout << "Red coat ";
}
void dressPants() {
cout << "Red pants ";
}
void dressShoes() {
cout << "Red shoes ";
}
};
class dressBlue :public dress
{
public:
void dressCoat() {
cout << "Blue coat ";
}
void dressPants() {
cout << "Blue pants ";
}
void dressShoes() {
cout << "Blue shoes ";
}
};
class run
{
public:
run(dress* p)
{
this->p = p;
}
void dressAll()
{
p->dressCoat();
p->dressPants();
p->dressShoes();
cout << endl;
}
private:
dress* p;
};
int main()
{
run* runRed = new run(new dressRed);
runRed->dressAll();
run* runBlue = new run(new dressBlue);
runBlue->dressAll();
return 0;
}
这个代码其实已经和CRTP+模板方法模式的实现代码非常接近了。
11,访问者模式
访问者模式的场景是,不同的操作和不同的对象之间正交,即任一操作都可用于任一对象。
实现方法是提供操作基类和对象基类,不同操作和不同对象分别继承父类(双重多态分发)。
抽象操作和具体操作:
class Pick
{
public:
virtual void run() {};
};
class PickBig
{
public:
void run()
{
cout << "pick big ";
}
};
class PickSmall
{
public:
void run()
{
cout << "pick small ";
}
};
抽象对象和具体对象:
class Animal
{
public:
void SetOpt(Pick* p)
{
opt = p;
}
virtual void run() {};
protected:
Pick* opt;
};
class Pig :public Animal
{
public:
void run()
{
opt->run();
cout << "Pig\n";
}
};
class Dog :public Animal
{
public:
void run()
{
opt->run();
cout << "Dog\n";
}
};
调用示例:
int main()
{
Pick* p1 = new PickBig();
Pick* p2 = new PickSmall();
Animal* animal = new Pig();
animal->SetOpt(p1);
animal->run();
animal->SetOpt(p2);
animal->run();
animal = new Dog();
animal->SetOpt(p1);
animal->run();
animal->SetOpt(p2);
animal->run();
return 0;
}
输出:
pick big Pig
pick small Pig
pick big Dog
pick small Dog