前面三个会实现
单例
- 只允许创建一个活动的对象(实例),提供了对唯一实例的受控访问
- 原理是将所有能够创建对象的函数都设为private,通过静态成员返回实例
- 分为懒汉式(需要考虑加锁)、饿汉式
懒汉式
```cppinclude
include
using namespace std;
class SingleInstance { public : static SingleInstance getSingleInstance() { if (instance == null) { pthread_mutex_lock(&mutex); if (instance == null) { instance = new SingleInstance(); } pthread_mutex_unlock(&mutex); } return instance; } ~SingleInstance(){} static pthread_mutex_t mutex; private : // 涉及创建对象的函数都设置为private SingleInstance(){} SingleInstance(const SingleInstance& singleInstance){} SingleInstance& operator=(const SingleInstance& other) { return this} static SingleInstance* instance; };
// 懒汉式, 静态变量需要定义 SingleInstance* singleInstance::instance = nullptr; pthread_mutex_t singleInstance::mutex;
int main(){ // 因为没有办法创建对象,就得采用静态成员函数的方法返回静态成员变量 SingleInstance s = SingleInstance::getsingleInstance(); //singleInstance s1 = new singleInstance(); // 报错 delete s; // 防止内存泄露 return 0; }
<a name="GZZoG"></a>## 饿汉式```cpp#include<iostream>using namespace std;class SingleInstance{public :static SingleInstance* getSingleInstance() { // 饿汉式直接创建一个对象static SingleInstance instance;return &instance;}~SingleInstance(){}private:SingleInstance(){}SingleInstance(const SingleInstance& other){}SingleInstance& operator=(const SingleInstance& other){return *this};};int main() {// 因为没办法创建对象,得采用静态成员函数的方法返回SingleInstance* s = SingleInstance::getSingleInstance();//singleInstance *s1 = new singleInstance(); // 报错return 0;}
工厂
- 建立一个工厂类,对实现了同一接口的一些类进行实例的创建
- 简单工厂的实质:一个工厂类根据传入的参数,动态决定应该创建哪个产品类(这些产品类继承自一个父类或接口)的实例
- 为了解耦
简单工厂
```cppinclude
include
using namespace std;
//产品类(抽象类,不能实例化) class Product{ public: Product(){}; virtual void show()=0; //纯虚函数 };
class productA:public Product{ public: productA(){}; void show(){ cout << “product A create!” << endl; }; ~productA(){}; };
class productB:public Product{ public: productB(){}; void show(){ cout << “product B create!” << endl; }; ~productB(){}; };
class simpleFactory{ // 工厂类
public: simpleFactory(){}; Product* product(const string str){ if (str == “productA”) return (new productA()); if (str == “productB”) return (new productB()); return NULL; }; };
int main(){ simpleFactory obj; // 创建工厂 Product* pro; // 创建产品
pro = obj.product("productA");pro->show(); // product A create!delete pro;pro = obj.product("productB");pro->show(); // product B create!delete pro;return 0;
}
<a name="ugWnj"></a>## 抽象工厂- 为了进一步解耦,在简单工厂的基础上发展出了抽象工厂模式,即连工厂都抽象出来,实现了进一步代码解耦```cpp#include<iostream>#include<pthread.h>using namespace std;// 产品类,抽象类,不能实例化class Product {public :Product(){}virtual void show()=0; // 纯虚函数};// 产品Aclass ProductA : public Product {public :ProductA(){}void show(){cout << "product A create!" << endl;}};// 产品Bclass ProductB : public Product {public :ProductB(){}void show(){cout << "product B create!" << endl;}};class Factory { // 抽象类public :virtual Product* createProduct() = 0;};class FactoryA : Factory { // 工厂类A,只生产A产品public :Product* createProduct() {Product* _product = nullptr;_product = new ProductA();return _product;}};class FactoryB : Factory { // 工厂类B,只生产B产品public :Product* createProduct() {Product* _product = nullptr;_product = new ProductB();return _product;}};int main() {Product* _product = nullptr;auto myFactoryA = new FactoryA();_product = myFactoryA->createProduct(); // 调用产品A的工厂来生产a产品_product->show();delete _product;auto MyFactoryB=new FactorB();_product=MyFactoryB->CreateProduct(); // 调用产品B的工厂来生产B产品_product->show();delete _product;return 0;}
代理(委托)
- 找一个对象来替我们访问某个对象
- 为其他对象提供一种代理,以控制对这个对象的访问
- 举例:小明最近刚参加工作,想在单位附近租一间房子,但是对新环境又不熟悉,所以他想找一个房产中介来帮他租房
- 优点:
- 职责清晰:真实角色就是实现实际的业务逻辑,不关心其他非本职责的事务,通过后期的代理完成一件事务,附带的结果就是编程简介清晰
- 高扩展性:具体主题角色可变
- 缺点:
- 这种模式引入了另一个抽象层,这有时可能是一个问题。如果真实主题被某些客户端直接访问,并且其中一些客户端可能访问代理类,这可能会导致不同的行为
- 由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢
- 实现代理模式需要额外的工作,有些代理模式的实现非常复杂
根据目的和实现方式的不同,代理模式可分为很多种,常见的有:
- 远程代理(Remote Proxy)
- 为一个位于不同地址空间的对象提供一个本地代理,对代理的方法调用会导致对远程对象的方法调用。ATM 就是一个例子,ATM 可能会持有(存在于远程服务器中的)银行信息的一个代理对象
- 虚拟代理(Virtual Proxy)
- 使用虚拟代理,代理可以作为一个(资源消耗较大的)对象的代表。虚拟代理经常延迟对象的创建,直到需要为止。在创建对象之前(及创建对象过程中),虚拟代理也可以作为对象的代理;之后,代理将请求直接委托给 RealSubject。
- 保护代理(Protection Proxy)
- 根据访问权限,可以使用保护代理来控制对资源的访问。例如,有一个员工对象,保护代理可以允许普通员工调用对象的某些方法,管理员调用其他方法。
- 缓冲代理(Cache Proxy)
- 为某一个目标操作的结果提供临时的存储空间,以便多个客户端可以共享这些结果。
- 智能引用代理(Smart Reference Proxy)
- 当一个对象被引用时,提供一些额外的操作(例如:将对象被调用的次数记录下来)。
- 代理原理很简单,但实现多种多样
```cpp
include
include
using namespace std;
// Subject
class Person {
public:
virtual void rentHouse() = 0;
};
// RealSubject class XiaoMing : public Person { public: void rentHouse() { cout << “小明需要租一间房子!” << endl; } };
// Proxy class Intermediary : public Person { public: Intermediart(Person person) : m_person(person){} void rentHouse() { m_person->rentHouse(); cout << “中介抽取佣金百分之10!” << endl; } private: Person m_person; };
// 场景 int main() { // 小米 Person* xiaoM = new XiaoMing();
// 找一个中介帮忙租房子Person* intermediary = new Intermediary(xiaoM);intermediary->rentHouse();return 0;
}
<a name="rpMF8"></a># 下面三个能看懂<a name="oTr9C"></a># 适配器- 将一个类的接口转换成客户希望的另一个接口- 使得原本由于接口不兼容而不能一起工作的类,可以一起工作- 主要解决在软件系统中,常常要将一些“现存的对象"放到新的环境中,而新环境要求的接口是现环境不能满足的```cpp#include <iostream>#include <list>using namespace std;//目标抽象类Robot(机器人接口)class Robot{public:virtual void eat() = 0;virtual void sleep() = 0;};//适配者类Douya(Douya类)class Douya{public:void eat(){cout << "豆芽吃饭" << endl;}void sleep(){cout << "豆芽睡觉" << endl;}};//适配器类DouyaAdapter(DouyaAdapter类)class DouyaAdapter : public Robot, public Douya{public:void eat(){cout << "机器人模仿:" ;Douya::eat();}void sleep(){cout << "机器人模仿:" ;Douya::sleep();}};//客户端测试类Clientint main(void){Robot *robot = (Robot*)new DouyaAdapter();robot->eat(); // 机器人模仿:豆芽吃饭robot->sleep(); // 机器人模仿:豆芽睡觉delete robot;return 0;}
观察者
- 作用是定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所以依赖于它的对象都得到通知并被自动更新
- 例如:拍卖时,拍卖师观察最高标价,然后通知给其他竞价者竞价
```cpp
include
include
using namespace std;
class Observer{ // 观察者抽象 public: virtual void Update(int) = 0; };
class Subject{ // 被观察者抽象 public: virtual void Attach(Observer ) = 0; virtual void Detach(Observer ) = 0; virtual void Notify() = 0; };
class ConcreteObserver1:public Observer{ // 第一个观察者 public: ConcreteObserver1(Subject pSubject):m_pSubject(pSubject){} void Update(int value){ cout << “ConcreteObserver1 get the update. New State:” << value << endl; } private: Subject m_pSubject; };
class ConcreteObserver2 : public Observer{ // 第二个观察者 public: ConcreteObserver2(Subject pSubject):m_pSubject(pSubject){} void Update(int value){ cout << “ConcreteObserver2 get the update. New State:” << value << endl; } private: Subject m_pSubject; };
class ConcreteSubject:public Subject{ // 被观察者
public:
void Attach(Observer pObserver);
void Detach(Observer pObserver);
void Notify();
void SetState(int state){
m_iState = state;
}
private:
std::list
void ConcreteSubject::Attach(Observer *pObserver){ // 添加观察者 m_ObserverList.push_back(pObserver); }
void ConcreteSubject::Detach(Observer *pObserver){ // 删除观察者 m_ObserverList.remove(pObserver); }
void ConcreteSubject::Notify(){ // 通知观察者
std::list
int main(){ // Create 被观察者 ConcreteSubject *pSubject = new ConcreteSubject();
// Create 观察者Observer *pObserver1 = new ConcreteObserver1(pSubject);Observer *pObserver2 = new ConcreteObserver2(pSubject);// 改变状态pSubject->SetState(2);// 注册观察者pSubject->Attach(pObserver1);pSubject->Attach(pObserver2);pSubject->Notify();// 通知观察者// 删除观察者pSubject->Detach(pObserver1);pSubject->SetState(3);pSubject->Notify();delete pObserver1;delete pObserver2;delete pSubject;
}
<a name="NHRmm"></a># 模板- 定义一个操作中的算法的[框架](https://so.csdn.net/so/search?q=%E6%A1%86%E6%9E%B6&spm=1001.2101.3001.7020),而将一些步骤延迟到子类中,使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤- 抽象模板中包含三种类型的方法: 基本方法、模板方法和钩子方法(Hook Method)- 基本方法——基本方法也叫做基本操作,是由子类实现的方法,并且在模板方法被调用- 模板方法——核心方法,不允许子类重写,所以都会加上final修饰符,可以有一个或几个,一般是一个具体方法框架,按照固定的流程对基本方法的调度- 钩子方法——为了让模板方法的执行结果的更好地适应因外界条件改变。比如说银行办理业务为例,办理业务是个模板方法,普通人要经历排队、取号、等待、办理四个基本流程,而Vip则不需要排队、取号、等待,那么在设计的时候我们的抽象模板类需要考虑到,于是乎你得需要在模板方法各基本方法调用之前增加条件判断,那么用于设置这个条件的方法就是钩子方法(Hook Method),钩子方法也可以是抽象的还可以由子类的一个方法返回值决定公共部分的执行结果- 模板方法模式的优点- 良好的扩展性,封装不变部分,扩展可变部分,把认为是不变部分的算法封装到父类实现,而可变部分的则可以通过继承来继续扩展。例如增加一个新的功能很简单,只要再增加一个子类,实现父类的基本方法就可以了- 提取公共部分代码,便于维护,减小维护升级成本,基本操作由父类定义,子类实现- 基本方法是由子类实现的,因此子类可以通过扩展的方式增加相应的功能,符合开闭原则- 缺点- 通常抽象类是负责声明某一类的事物的共同属性和抽象方法,实现类则是完成定义具体的特性和方法。但是模板方法模式却颠倒了,抽象类定义了部分抽象方法,由子类实现,子类执行的结果影响了父类的结果,也就是子类对父类产生了影响。每个不同的实现都需要定义一个子类,这会导致类的个数增加,系统更加庞大,设计也更加抽象,但是更加符合“单一职责原则”,使得类的内聚性得以提高- 适用场景- 多个子类有公有的方法,并且逻辑基本相同时- 重要、复杂的算法,可以把核心算法设计为模板方法,周边的相关细节功能则由各个 子类实现- 重构时,模板方法模式是一个经常使用的模式,把相同的代码抽取到父类中,然后通过钩子方法约束其行为- 实例- 填写简历表- 最近有个招聘会,可以带上简历去应聘了。但是,其中有一家公司不接受简历,而是给应聘者发了一张简历表,上面有基本信息、教育背景、工作经历等栏,让应聘者按照要求填写完整。每个人拿到这份表格后,就开始填写。如果用程序实现这个过程,该如何做呢?一种方案就是用模板方法模式:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。我们的例子中,操作就是填写简历这一过程,我们可以在父类中定义操作的算法骨架,而具体的实现由子类完成```cpp#include <iostream>#include <memory>//简历class Resume{protected: //保护成员virtual void SetPersonalInfo() {}virtual void SetEducation() {}virtual void SetWorkExp() {}public:void FillResume(){SetPersonalInfo();SetEducation();SetWorkExp();}};class ResumeA: public Resume{protected:void SetPersonalInfo(){std::cout << "A's PersonalInfo" << std::endl;}void SetEducation(){std::cout << "A's Education" << std::endl;}void SetWorkExp(){std::cout << "A's Work Experience" << std::endl;}};class ResumeB: public Resume{protected:void SetPersonalInfo(){std::cout << "B's PersonalInfo" << std::endl;}void SetEducation(){std::cout << "B's Education" << std::endl;}void SetWorkExp(){std::cout << "B's Work Experience" << std::endl;}};int main(){auto r1 = std::make_unique<ResumeA>();r1->FillResume();auto r2 = std::make_unique<ResumeB>();r2->FillResume();return 0;}
面向对象的设计原则
- SRP(Single Responsibility Principle):单一职责原则,就是说一个类只提供一种功能和仅有一个引起它变化的因素
- OCP(Open Close Principle):开放封闭原则,就是对一个类来说,对它的内部修改是封闭的,对它的扩展是开放的
- DIP(Dependence Inversion Principle):依赖倒置原则,就是程序依赖于抽象,而不依赖于实现,它的主要目的是为了降低耦合性,它一般通过反射和配置文件来实现的
- LSP(Liskov Substitution Principle):里氏替换原则,就是基类出现的地方,通过它的子类也完全可以实现这个功能
- ISP(Interface Segregation Principle):接口隔离原则,建立单一接口,不要建立庞大臃肿的接口,尽量细化接口,接口中的方法尽量少。也就是说,我们要为各个类建立专用的接口,而不要试图去建立一个很庞大的接口供所有依赖它的类去调用
- CRP(Composite Reuse Principle):合成复用原则,多用组合设计类,少用继承。
单一职责原则和接口隔离原则的区别
- 单一职责原则注重的是职责;而接口隔离原则注重对接口依赖的隔离
- 单一职责原则主要是约束类,其次才是接口和方法,它针对的是程序中的实现和细节; 而接口隔离原则主要约束接口,主要针对抽象,针对程序整体框架的构建
