设计Modern
这里的介绍一下设计模式的几种在(我的)工作种常用的模型,以及其代码和伪代码。此处主要作为一个知识积累库。如需详尽的解释还需百度。
观察者模式
用户往一个地方注册一个观察, 如果当观察者有探测到用户观察的消息,就推送给回给用户。观察者模式是一种行为设计模式, 允许你定义一种订阅机制, 可在对象事件发生时通知多个 “观察” 该对象的其他对象。
class Observer
{
public:
Observer(){}
virtual ~Observer(){}
virtual void update(Subject *subject) = 0;
virtual void update(string content) = 0;
};
class Subject
{
public:
Subject() {}
virtual ~ Subject() {}
virtual string getContent() = 0;
virtual string getAbstractContent() = 0;
virtual void setContent(string content) = 0;
// 订阅主题
void attach(Observer *observer) {
observers.push_back(observer);
}
// 取消订阅
void detach(Observer *observer) {
observers.remove(observer);
}
// 通知所有的订阅者
virtual void notifyObservers() {
for(Observer *reader: observers) {
// 拉模型 (也是有推送的性质,只是粒度小一些)
reader->update(this);
}
}
// 通知所有的订阅者
virtual void notifyObservers(string content) {
for(Observer *reader: observers) {
// 推模型
reader->update(content);
}
}
private:
list<Observer *> observers; // 保存注册的观察者
};
class Reader : public Observer
{
public:
Reader() {}
virtual ~Reader() {}
virtual void update(Subject *subject) {
// 调用对应的方法去拉取内容 subject->getContent()
cout << m_name << "收到报纸和阅读它, 具体内容" << subject->getContent() << endl;
}
virtual void update(string content) {
// 推模型
cout << m_name << "收到报纸和阅读它, 具体内容" << content << endl;
}
string getName() {
return m_name;
}
void setName(string name) {
m_name = name;
}
private:
string m_name;
};
class NewsPaper: public Subject
{
public:
NewsPaper() {}
virtual ~NewsPaper() {}
void setContent(string content) {
m_content = content;
notifyObservers();
}
virtual string getContent() {
return m_content;
}
virtual string getAbstractContent() {
return "摘要:";
}
private:
string m_content;
};
责任链模式
这个模式和上面的模式也是差不多的,只不过多了个等级的划分而已。责任链是一种行为设计模式, 允许你将请求沿着处理者链进行发送, 直至其中一个处理者对其进行处理。
该模式允许多个对象来对请求进行处理, 而无需让发送者类与具体接收者类相耦合。 链可在运行时由遵循标准处理者接口的任意处理者动态生成。
class WaterQualitySubject;
// 观察者的接口
/**
* 水质观察者接口定义
*/
class WatcherObserver
{
public:
WatcherObserver(){}
virtual ~WatcherObserver(){}
/**
* 被通知的方法
* @param subject 传入被观察的目标对象
*/
virtual void update(WaterQualitySubject *subject) = 0;
// 和普通观察者模式, 增加了角色
/**
* 设置观察人员的职务
* @param job 观察人员的职务
*/
virtual void setJob(string job) = 0;
/**
* 获取观察人员的职务
* @return 观察人员的职务
*/
virtual string getJob() = 0;
};
/**
* 定义水质监测的目标对象
*/
class WaterQualitySubject {
public:
WaterQualitySubject(){}
virtual ~WaterQualitySubject(){}
/**
* 注册观察者对象
* @param observer 观察者对象
*/
void attach(WatcherObserver *observer) {
observers.push_back(observer);
}
/**
* 删除观察者对象
* @param observer 观察者对象
*/
void detach(WatcherObserver *observer) {
observers.remove(observer);
}
/**
* 通知相应的观察者对象
*/
virtual void notifyWatchers() = 0;
/**
* 获取水质污染的级别
* @return 水质污染的级别
*/
virtual int getPolluteLevel() = 0;
protected:
/**
* 用来保存注册的观察者对象
*/
list<WatcherObserver *> observers;
};
/**
* 具体的观察者实现
*/
class Watcher: public WatcherObserver
{
public:
Watcher(){}
virtual ~Watcher(){}
string getJob() {
return m_job;
}
void setJob(string job) {
m_job = job;
}
virtual void update(WaterQualitySubject *subject) {
//这里采用的是拉的方式
cout << m_job << " 获取到通知,当前污染级别为:" << subject->getPolluteLevel() <<endl;
}
private:
/**
* 职务
*/
string m_job;
};
/**
* 具体的水质监测对象
*/
class WaterQuality: public WaterQualitySubject
{
public:
WaterQuality(){}
virtual ~WaterQuality(){}
/**
* 获取水质污染的级别
* @return 水质污染的级别
*/
int getPolluteLevel() {
return m_polluteLevel;
}
/**
* 当监测水质情况后,设置水质污染的级别
* @param polluteLevel 水质污染的级别
*/
virtual void setPolluteLevel(int polluteLevel) {
m_polluteLevel = polluteLevel;
//通知相应的观察者
notifyWatchers();
}
/**
* 通知相应的观察者对象
*/
virtual void notifyWatchers() {
//循环所有注册的观察者
for(WatcherObserver *watcher : observers){
//开始根据污染级别判断是否需要通知,由这里总控
if(m_polluteLevel >= 0){
//通知监测员做记录
if(watcher->getJob().compare("监测人员") == 0){
watcher->update(this);
}
}
if(m_polluteLevel >= 1){
//通知预警人员
if(watcher->getJob().compare("预警人员") == 0){
watcher->update(this);
}
}
if(m_polluteLevel >= 2){
//通知监测部门领导
if(watcher->getJob().compare("监测部门领导") == 0){
watcher->update(this);
}
}
}
}
private:
/**
* 污染的级别,0表示正常,1表示轻度污染,2表示中度污染,3表示高度污染
*/
int m_polluteLevel = 0;
};
int main()
{
//创建水质主题对象
WaterQuality *subject = new WaterQuality();
//创建几个观察者, 观察者分了不同角色
WatcherObserver *watcher1 = new Watcher();
watcher1->setJob("监测人员");
WatcherObserver *watcher2 = new Watcher();
watcher2->setJob("预警人员");
WatcherObserver *watcher3 = new Watcher();
watcher3->setJob("监测部门领导");
//注册观察者
subject->attach(watcher1);
subject->attach(watcher2);
subject->attach(watcher3);
//填写水质报告
cout << "当水质为正常的时候------------------〉" << endl;
subject->setPolluteLevel(0);
cout << "\n当水质为轻度污染的时候---------------〉"<< endl;
subject->setPolluteLevel(1);
cout << "\n当水质为中度污染的时候---------------〉"<< endl;
subject->setPolluteLevel(2);
// 释放观察者
subject->detach(watcher1);
subject->detach(watcher2);
subject->detach(watcher3);
delete watcher1;
delete watcher2;
delete watcher3;
delete subject;
return 0;
}
抽象工厂模式
抽象工厂本质上是为不同组件或其相互依赖的对象提供创建接口,而无需指定具体的类。能够有效的确保这些相互依赖对象的一致性(客户根据实例化不同的工厂类,只能获取与该工厂中一系列相关的产品)。
工厂类一般为单实例,客户仅需初始化不同的工厂实例就可以获得不同的对象类型。
class AbstractFactory
{
public:
virtual Object_A* createA() =0
{
return new Object_A;
}
virtual Object_N* createN() =0
{
return new Object_N;
}
};
class AbstractFactory_Huawei:public AbstractFactory
{
virtual Object_A* createA(){return new Object_AA1;};
virtual Object_N* createN(){return new Object_NN1;};
};
class AbstractFactory_Xiaomi:public AbstractFactory
{
virtual Object_A* createA(){return new Object_AA2;};
virtual Object_N* createN(){return new Object_NN2;};
};
工厂方法
这个方法本质上可以说就是创建一个对象的两个通用属性,然后具体然后需要其子类去现实它具体的功能。
例如我们经常使用的导出用户数据,我们有好多种方法导出,一种是导入到数据库,一种是导入到本地XML,一种是写入日志,导出数据就是一个通用属性,而不同的导出的方式就是其具体的功能。
#include <iostream>
using namespace std;
class ExportFileProduct
{
public:
ExportFileProduct() {}
virtual ~ExportFileProduct() {}
virtual bool Export(string data) = 0;
};
// 保存成文件
class ExportTextProduct: public ExportFileProduct
{
public:
ExportTextProduct() {}
virtual ~ExportTextProduct() { }
virtual bool Export(string data) {
cout << "导出数据:[" << data << "]保存成文本的方式" << endl;
return true;
}
};
class ExportDBProduct: public ExportFileProduct
{
public:
ExportDBProduct() {}
virtual ~ExportDBProduct() { }
virtual bool Export(string data) {
cout << "导出数据:[" << data << "]保存数据库的方式" << endl;
return true;
}
};
class ExportFactory
{
public:
ExportFactory() {}
virtual ~ExportFactory() {}
/**
* @brief Export
* @param type 导出的类型
* @param data 具体的数据
* @return
*/
virtual bool Export(int type, string data) {
ExportFileProduct *product = nullptr;
product = factoryMethod(type);
bool ret = false;
if(product) {
ret = product->Export(data);
delete product;
} else {
cout << "没有对应的导出方式";
}
return ret;
}
protected:
virtual ExportFileProduct *factoryMethod(int type) {
ExportFileProduct *product = nullptr;
if(1 == type) {
product = new ExportTextProduct();
} else if(2 == type) {
product = new ExportDBProduct();
}
return product;
}
};
// 加一种新的导出方式:
// (1)修改原来的工厂方法
// (2)继承工厂方法去拓展
class ExportXMLProduct: public ExportFileProduct
{
public:
ExportXMLProduct() {}
virtual ~ExportXMLProduct() { }
virtual bool Export(string data) {
cout << "导出数据:[" << data << "]保存XML的方式" << endl;
return true;
}
};
class ExportPortobufferProduct: public ExportFileProduct
{
public:
ExportPortobufferProduct() {}
virtual ~ExportPortobufferProduct() { }
virtual bool Export(string data) {
cout << "导出数据:[" << data << "]保存Portobuffer的方式" << endl;
return true;
}
};
class ExportFactory2: public ExportFactory
{
public:
ExportFactory2() {}
virtual ~ExportFactory2() {}
protected:
virtual ExportFileProduct *factoryMethod(int type) {
ExportFileProduct *product = nullptr;
if(3 == type) {
product = new ExportXMLProduct();
} else if(4 == type) {
product = new ExportPortobufferProduct();
} else {
product = ExportFactory::factoryMethod(type);
}
return product;
}
};
int main()
{
cout << "ExportFactory" << endl;
ExportFactory *factory = new ExportFactory();
factory->Export(1, "上课人数");
factory->Export(2, "上课人数");
factory->Export(3, "上课人数");
cout << "\nExportFactory2" << endl;
ExportFactory *factory2 = new ExportFactory2();
factory2->Export(1, "上课人数");
factory2->Export(2, "上课人数");
factory2->Export(3, "上课人数");
factory2->Export(4, "上课人数");
delete factory;
delete factory2;
return 0;
}
生成器(Builder)
生成器使我们可以将数据的构建和表现形式分开来,允许使用相同的创建代码生成不同类型和形式的对象。
/**
* It makes sense to use the Builder pattern only when your products are quite
* complex and require extensive configuration.
*
* Unlike in other creational patterns, different concrete builders can produce
* unrelated products. In other words, results of various builders may not
* always follow the same interface.
*/
class Product1{
public:
std::vector<std::string> parts_;
void ListParts()const{
std::cout << "Product parts: ";
for (size_t i=0;i<parts_.size();i++){
if(parts_[i]== parts_.back()){
std::cout << parts_[i];
}else{
std::cout << parts_[i] << ", ";
}
}
std::cout << "\n\n";
}
};
/**
* The Builder interface specifies methods for creating the different parts of
* the Product objects.
*/
class Builder{
public:
virtual ~Builder(){}
virtual void ProducePartA() const =0;
virtual void ProducePartB() const =0;
virtual void ProducePartC() const =0;
};
/**
* The Concrete Builder classes follow the Builder interface and provide
* specific implementations of the building steps. Your program may have several
* variations of Builders, implemented differently.
*/
class ConcreteBuilder1 : public Builder{
private:
Product1* product;
/**
* A fresh builder instance should contain a blank product object, which is
* used in further assembly.
*/
public:
ConcreteBuilder1(){
this->Reset();
}
~ConcreteBuilder1(){
delete product;
}
void Reset(){
this->product= new Product1();
}
/**
* All production steps work with the same product instance.
*/
void ProducePartA()const override{
this->product->parts_.push_back("PartA1");
}
void ProducePartB()const override{
this->product->parts_.push_back("PartB1");
}
void ProducePartC()const override{
this->product->parts_.push_back("PartC1");
}
/**
* Concrete Builders are supposed to provide their own methods for
* retrieving results. That's because various types of builders may create
* entirely different products that don't follow the same interface.
* Therefore, such methods cannot be declared in the base Builder interface
* (at least in a statically typed programming language). Note that PHP is a
* dynamically typed language and this method CAN be in the base interface.
* However, we won't declare it there for the sake of clarity.
*
* Usually, after returning the end result to the client, a builder instance
* is expected to be ready to start producing another product. That's why
* it's a usual practice to call the reset method at the end of the
* `getProduct` method body. However, this behavior is not mandatory, and
* you can make your builders wait for an explicit reset call from the
* client code before disposing of the previous result.
*/
/**
* Please be careful here with the memory ownership. Once you call
* GetProduct the user of this function is responsable to release this
* memory. Here could be a better option to use smart pointers to avoid
* memory leaks
*/
Product1* GetProduct() {
Product1* result= this->product;
this->Reset();
return result;
}
};
/**
* The Director is only responsible for executing the building steps in a
* particular sequence. It is helpful when producing products according to a
* specific order or configuration. Strictly speaking, the Director class is
* optional, since the client can control builders directly.
*/
class Director{
/**
* @var Builder
*/
private:
Builder* builder;
/**
* The Director works with any builder instance that the client code passes
* to it. This way, the client code may alter the final type of the newly
* assembled product.
*/
public:
void set_builder(Builder* builder){
this->builder=builder;
}
/**
* The Director can construct several product variations using the same
* building steps.
*/
void BuildMinimalViableProduct(){
this->builder->ProducePartA();
}
void BuildFullFeaturedProduct(){
this->builder->ProducePartA();
this->builder->ProducePartB();
this->builder->ProducePartC();
}
};
/**
* The client code creates a builder object, passes it to the director and then
* initiates the construction process. The end result is retrieved from the
* builder object.
*/
/**
* I used raw pointers for simplicity however you may prefer to use smart
* pointers here
*/
void ClientCode(Director& director)
{
ConcreteBuilder1* builder = new ConcreteBuilder1();
director.set_builder(builder);
std::cout << "Standard basic product:\n";
director.BuildMinimalViableProduct();
Product1* p= builder->GetProduct();
p->ListParts();
delete p;
std::cout << "Standard full featured product:\n";
director.BuildFullFeaturedProduct();
p= builder->GetProduct();
p->ListParts();
delete p;
// Remember, the Builder pattern can be used without a Director class.
std::cout << "Custom product:\n";
builder->ProducePartA();
builder->ProducePartC();
p=builder->GetProduct();
p->ListParts();
delete p;
delete builder;
}
int main(){
Director* director= new Director();
ClientCode(*director);
delete director;
return 0;
}
单例
一次创建多次使用,这个比较简单就不上代码了。
原型模式
原型是一种创建型设计模式, 使你能够复制对象, 甚至是复杂对象, 而又无需使代码依赖它们所属的类。
所有的原型类都必须有一个通用的接口, 使得即使在对象所属的具体类未知的情况下也能复制对象。 原型对象可以生成自身的完整副本, 因为相同类的对象可以相互访问对方的私有成员变量。
using std::string;
// Prototype Design Pattern
//
// Intent: Lets you copy existing objects without making your code dependent on
// their classes.
enum Type {
PROTOTYPE_1 = 0,
PROTOTYPE_2
};
/**
* The example class that has cloning ability. We'll see how the values of field
* with different types will be cloned.
*/
class Prototype {
protected:
string prototype_name_;
float prototype_field_;
public:
Prototype() {}
Prototype(string prototype_name)
: prototype_name_(prototype_name) {
}
virtual ~Prototype() {}
virtual Prototype *Clone() const = 0;
virtual void Method(float prototype_field) {
this->prototype_field_ = prototype_field;
std::cout << "Call Method from " << prototype_name_ << " with field : " << prototype_field << std::endl;
}
};
/**
* ConcretePrototype1 is a Sub-Class of Prototype and implement the Clone Method
* In this example all data members of Prototype Class are in the Stack. If you
* have pointers in your properties for ex: String* name_ ,you will need to
* implement the Copy-Constructor to make sure you have a deep copy from the
* clone method
*/
class ConcretePrototype1 : public Prototype {
private:
float concrete_prototype_field1_;
public:
ConcretePrototype1(string prototype_name, float concrete_prototype_field)
: Prototype(prototype_name), concrete_prototype_field1_(concrete_prototype_field) {
}
/**
* Notice that Clone method return a Pointer to a new ConcretePrototype1
* replica. so, the client (who call the clone method) has the responsability
* to free that memory. I you have smart pointer knowledge you may prefer to
* use unique_pointer here.
*/
Prototype *Clone() const override {
return new ConcretePrototype1(*this);
}
};
class ConcretePrototype2 : public Prototype {
private:
float concrete_prototype_field2_;
public:
ConcretePrototype2(string prototype_name, float concrete_prototype_field)
: Prototype(prototype_name), concrete_prototype_field2_(concrete_prototype_field) {
}
Prototype *Clone() const override {
return new ConcretePrototype2(*this);
}
};
/**
* In PrototypeFactory you have two concrete prototypes, one for each concrete
* prototype class, so each time you want to create a bullet , you can use the
* existing ones and clone those.
*/
class PrototypeFactory {
private:
std::unordered_map<Type, Prototype *, std::hash<int>> prototypes_;
public:
PrototypeFactory() {
prototypes_[Type::PROTOTYPE_1] = new ConcretePrototype1("PROTOTYPE_1 ", 50.f);
prototypes_[Type::PROTOTYPE_2] = new ConcretePrototype2("PROTOTYPE_2 ", 60.f);
}
/**
* Be carefull of free all memory allocated. Again, if you have smart pointers
* knowelege will be better to use it here.
*/
~PrototypeFactory() {
delete prototypes_[Type::PROTOTYPE_1];
delete prototypes_[Type::PROTOTYPE_2];
}
/**
* Notice here that you just need to specify the type of the prototype you
* want and the method will create from the object with this type.
*/
Prototype *CreatePrototype(Type type) {
return prototypes_[type]->Clone();
}
};
void Client(PrototypeFactory &prototype_factory) {
std::cout << "Let's create a Prototype 1\n";
Prototype *prototype = prototype_factory.CreatePrototype(Type::PROTOTYPE_1);
prototype->Method(90);
delete prototype;
std::cout << "\n";
std::cout << "Let's create a Prototype 2 \n";
prototype = prototype_factory.CreatePrototype(Type::PROTOTYPE_2);
prototype->Method(10);
delete prototype;
}
int main() {
PrototypeFactory *prototype_factory = new PrototypeFactory();
Client(*prototype_factory);
delete prototype_factory;
return 0;
}
适配器
适配器是一种结构型设计模式, 它能使不兼容的对象能够相互合作。
适配器可担任两个对象间的封装器, 它会接收对于一个对象的调用, 并将其转换为另一个对象可识别的格式和接口。
/**
* The Target defines the domain-specific interface used by the client code.
*/
class Target {
public:
virtual ~Target() = default;
virtual std::string Request() const {
return "Target: The default target's behavior.";
}
};
/**
* The Adaptee contains some useful behavior, but its interface is incompatible
* with the existing client code. The Adaptee needs some adaptation before the
* client code can use it.
*/
class Adaptee {
public:
std::string SpecificRequest() const {
return ".eetpadA eht fo roivaheb laicepS";
}
};
/**
* The Adapter makes the Adaptee's interface compatible with the Target's
* interface.
*/
class Adapter : public Target {
private:
Adaptee *adaptee_;
public:
Adapter(Adaptee *adaptee) : adaptee_(adaptee) {}
std::string Request() const override {
std::string to_reverse = this->adaptee_->SpecificRequest();
std::reverse(to_reverse.begin(), to_reverse.end());
return "Adapter: (TRANSLATED) " + to_reverse;
}
};
/**
* The client code supports all classes that follow the Target interface.
*/
void ClientCode(const Target *target) {
std::cout << target->Request();
}
int main() {
std::cout << "Client: I can work just fine with the Target objects:\n";
Target *target = new Target;
ClientCode(target);
std::cout << "\n\n";
Adaptee *adaptee = new Adaptee;
std::cout << "Client: The Adaptee class has a weird interface. See, I don't understand it:\n";
std::cout << "Adaptee: " << adaptee->SpecificRequest();
std::cout << "\n\n";
std::cout << "Client: But I can work with it via the Adapter:\n";
Adapter *adapter = new Adapter(adaptee);
ClientCode(adapter);
std::cout << "\n";
delete target;
delete adaptee;
delete adapter;
return 0;
}
桥接
桥接是一种结构型设计模式, 可将业务逻辑或一个大类拆分为不同的层次结构, 从而能独立地进行开发。
层次结构中的第一层 (通常称为抽象部分) 将包含对第二层 (实现部分) 对象的引用。 抽象部分将能将一些 (有时是绝大部分) 对自己的调用委派给实现部分的对象。 所有的实现部分都有一个通用接口, 因此它们能在抽象部分内部相互替换。
/**
* The Implementation defines the interface for all implementation classes. It
* doesn't have to match the Abstraction's interface. In fact, the two
* interfaces can be entirely different. Typically the Implementation interface
* provides only primitive operations, while the Abstraction defines higher-
* level operations based on those primitives.
*/
class Implementation {
public:
virtual ~Implementation() {}
virtual std::string OperationImplementation() const = 0;
};
/**
* Each Concrete Implementation corresponds to a specific platform and
* implements the Implementation interface using that platform's API.
*/
class ConcreteImplementationA : public Implementation {
public:
std::string OperationImplementation() const override {
return "ConcreteImplementationA: Here's the result on the platform A.\n";
}
};
class ConcreteImplementationB : public Implementation {
public:
std::string OperationImplementation() const override {
return "ConcreteImplementationB: Here's the result on the platform B.\n";
}
};
/**
* The Abstraction defines the interface for the "control" part of the two class
* hierarchies. It maintains a reference to an object of the Implementation
* hierarchy and delegates all of the real work to this object.
*/
class Abstraction {
/**
* @var Implementation
*/
protected:
Implementation* implementation_;
public:
Abstraction(Implementation* implementation) : implementation_(implementation) {
}
virtual ~Abstraction() {
}
virtual std::string Operation() const {
return "Abstraction: Base operation with:\n" +
this->implementation_->OperationImplementation();
}
};
/**
* You can extend the Abstraction without changing the Implementation classes.
*/
class ExtendedAbstraction : public Abstraction {
public:
ExtendedAbstraction(Implementation* implementation) : Abstraction(implementation) {
}
std::string Operation() const override {
return "ExtendedAbstraction: Extended operation with:\n" +
this->implementation_->OperationImplementation();
}
};
/**
* Except for the initialization phase, where an Abstraction object gets linked
* with a specific Implementation object, the client code should only depend on
* the Abstraction class. This way the client code can support any abstraction-
* implementation combination.
*/
void ClientCode(const Abstraction& abstraction) {
// ...
std::cout << abstraction.Operation();
// ...
}
/**
* The client code should be able to work with any pre-configured abstraction-
* implementation combination.
*/
int main() {
Implementation* implementation = new ConcreteImplementationA;
Abstraction* abstraction = new Abstraction(implementation);
ClientCode(*abstraction);
std::cout << std::endl;
delete implementation;
delete abstraction;
implementation = new ConcreteImplementationB;
abstraction = new ExtendedAbstraction(implementation);
ClientCode(*abstraction);
delete implementation;
delete abstraction;
return 0;
}
这里先暂时介绍这几种的模型,其他的模型如果以后用到的话会再进行添加。