设计模式
设计模式7大原则
单一职责原则
应该有且仅有一个原因引起类的变更。
开闭原则
软件应该对扩展开放,而对修改关闭。
里氏替换原则
如果调用父类的方法可以成功,那么替换成子类调用也可以。
依赖倒置原则
面向接口编程。模块间的依赖通过抽象发生,实现类之间不直接发生依赖关系,其依赖关系是通过接口或抽象类产生的;
接口隔离原则
类间的依赖关系应该建立在最小的接口上。
迪米特法则
一个对象应该对其他对象有最少的了解。
创建型
单例模式
单例模式(Singleton)的目的是为了保证在一个进程中,某个类有且仅有一个实例。实际项目案例:
线程不安全的 饿汉式的
class Singleton {
public:
static Singleton& getInstance() {
if (instance_ == nullptr) {
instance_ = new Singleton();
}
return *instance_;
}
private:
static Singleton* instance_;
Singleton() = default;
~Singleton() = default;
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
};
Singleton* Singleton::instance_ = nullptr;
int main() {
Singleton& s1 = Singleton::getInstance();
Singleton& s2 = Singleton::getInstance();
assert(&s1 == &s2);
}
线程安全的
class Singleton {
public:
static Singleton& getInstance() {
static Singleton instance;
return instance;
}
private:
Singleton() = default;
~Singleton() = default;
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
};
int main() {
Singleton& s1 = Singleton::getInstance();
Singleton& s2 = Singleton::getInstance();
assert(&s1 == &s2);
}
在这个例子中,我们使用了C++11中的静态局部变量来实现单例模式。静态局部变量在第一次使用时初始化,因此我们可以保证只有一个实例被创建。由于静态局部变量的初始化是线程安全的,因此我们不需要使用额外的同步机制来保证线程安全。
在这个例子中,我们还将拷贝构造函数和赋值运算符设为删除,以防止实例被复制或赋值。这可以防止通过复制或赋值操作创建多个实例。
最后,我们在main()函数中创建两个实例并进行比较,以确保只有一个实例被创建。
data_manager_factory 用于构造data_manager, which is用来和DB utility process进行数据交互
里面有一个GetServiceForContext方法
std::map<void*, std::unique_ptr<KeyedService>> mapping_;
brower context 作为键 service 作为值
如果不存在 构建 并绑定service = BuildServiceInstanceFor Associate
实现了相同context下data_manager的复用
工厂模式
manager和managerFactory
#include <iostream>
#include <string>
#include <memory>
// 抽象产品类
class Product {
public:
virtual void use() const = 0;
};
// 具体产品类 A
class ProductA : public Product {
public:
void use() const override {
std::cout << "Using ProductA" << std::endl;
}
};
// 具体产品类 B
class ProductB : public Product {
public:
void use() const override {
std::cout << "Using ProductB" << std::endl;
}
};
// 抽象工厂类
class Factory {
public:
virtual std::unique_ptr<Product> createProduct() const = 0;
};
// 具体工厂类 A
class FactoryA : public Factory {
public:
std::unique_ptr<Product> createProduct() const override {
return std::make_unique<ProductA>();
}
};
// 具体工厂类 B
class FactoryB : public Factory {
public:
std::unique_ptr<Product> createProduct() const override {
return std::make_unique<ProductB>();
}
};
int main() {
// 使用工厂A创建产品A
std::unique_ptr<Factory> factoryA = std::make_unique<FactoryA>();
std::unique_ptr<Product> productA = factoryA->createProduct();
productA->use();
// 使用工厂B创建产品B
std::unique_ptr<Factory> factoryB = std::make_unique<FactoryB>();
std::unique_ptr<Product> productB = factoryB->createProduct();
productB->use();
return 0;
}
在这个例子中,我们定义了两个具体产品类 ProductA 和 ProductB,它们都实现了抽象产品类 Product 中的 use() 方法。
我们还定义了两个具体工厂类 FactoryA 和 FactoryB,它们都实现了抽象工厂类 Factory 中的 createProduct() 方法,分别创建产品 ProductA 和 ProductB 的实例。
在 main() 函数中,我们首先使用工厂 FactoryA 创建产品 ProductA 的实例,并调用 use() 方法输出信息。接着,我们使用工厂 FactoryB 创建产品 ProductB 的实例,并调用 use() 方法输出信息。
工厂模式的优点是可以将对象的创建过程与使用过程分离,使得代码更易于扩展和维护。如果我们需要增加一个新的产品类,只需要添加一个新的具体产品类和一个新的具体工厂类即可,无需修改现有的代码。同时,工厂模式还可以隐藏对象的具体类型,使得客户端代码与具体实现解耦,从而增加了代码的灵活性和可维护性。
原型
结构型
适配器模式
#include <iostream>
#include <memory>
// 目标接口类
class Target {
public:
virtual void request() const = 0;
};
// 源接口类
class Adaptee {
public:
void specificRequest() const {
std::cout << "Adaptee::specificRequest()" << std::endl;
}
};
// 对象适配器类
class Adapter : public Target {
public:
Adapter(std::shared_ptr<Adaptee> adaptee)
: adaptee_(adaptee) {}
void request() const override {
adaptee_->specificRequest();
}
private:
std::shared_ptr<Adaptee> adaptee_;
};
int main() {
// 创建 Adaptee 对象
std::shared_ptr<Adaptee> adaptee = std::make_shared<Adaptee>();
// 创建 Adapter 对象,并将 Adaptee 对象传递给它
std::shared_ptr<Target> adapter = std::make_shared<Adapter>(adaptee);
// 调用目标接口的方法,实际上会调用 Adaptee::specificRequest() 方法
adapter->request();
return 0;
}
目标:让原接口调用目标接口的方法。
该例子定义了目标接口类 Target 和源接口类 Adaptee。为了适配这两个不兼容的接口,定义了适配器类 Adapter。在 Adapter 类中,使用了对象适配器模式,通过在构造函数中接收一个指向 Adaptee 对象的指针,并在 request 方法中调用 Adaptee 的 specificRequest 方法,实现了将目标接口和源接口进行适配。
在 main 函数中,首先创建了 Adaptee 对象,然后创建了 Adapter 对象,并将 Adaptee 对象传递给它。最后,通过调用 Target 接口的 request 方法,实际上会调用 Adaptee 的 specificRequest 方法,展示了适配器模式的作用。
适配器模式的优点包括:
- 解耦性强:通过适配器,可以将客户端和被适配者解耦,降低了它们之间的依赖关系,从而提高了系统的可维护性和可扩展性。
- 复用性好:适配器可以重复使用,因为它们是将客户端和被适配者之间的接口进行转换,所以可以用于多个客户端和被适配者之间的接口转换。
- 扩展性强:当需要添加新的被适配者时,只需要编写一个新的适配器类即可,无需修改客户端代码。
适配器模式的使用场景包括:
- 旧系统升级:当需要将旧系统升级到新系统时,可能需要使用适配器模式来将旧系统中的接口转换成新系统中的接口。
- 不兼容的接口:当两个类之间的接口不兼容时,可以使用适配器模式来进行转换,从而使它们能够正常地工作在一起。
- 第三方库的接口:当需要使用第三方库的接口时,但是它的接口与我们的系统不兼容时,可以使用适配器模式来进行转换。
总之,适配器模式适用于需要将一个类的接口转换成另一个类的接口,以便能够使它们能够正常地工作在一起的场景。
代理模式
行为型
观察者模式
当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。当一个对象被修改时,则会自动通知依赖它的对象。
组成结构:抽象类Observer 实现类 抽象Subject 具体Subject Subject里面有Observer的一个Vector
可以attach和detach,当做某些操作的时候可以调用Observer里的方法
#include <iostream>
#include <vector>
using namespace std;
// 抽象观察者类
class Observer {
public:
virtual void update(int) = 0;
};
// 具体观察者类A
class ObserverA : public Observer {
public:
void update(int value) override {
cout << "ObserverA received update with value: " << value << endl;
}
};
// 具体观察者类B
class ObserverB : public Observer {
public:
void update(int value) override {
cout << "ObserverB received update with value: " << value << endl;
}
};
// 抽象主题类
class Subject {
public:
virtual void attach(Observer*) = 0;
virtual void detach(Observer*) = 0;
virtual void notify() = 0;
};
// 具体主题类
class ConcreteSubject : public Subject {
private:
vector<Observer*> observers;
int value;
public:
void attach(Observer* observer) override {
observers.push_back(observer);
}
void detach(Observer* observer) override {
for (auto it = observers.begin(); it != observers.end(); ++it) {
if (*it == observer) {
observers.erase(it);
break;
}
}
}
void notify() override {
for (auto observer : observers) {
observer->update(value);
}
}
void setValue(int value) {
this->value = value;
notify();
}
};
int main() {
ConcreteSubject subject;
ObserverA observerA;
ObserverB observerB;
subject.attach(&observerA);
subject.attach(&observerB);
subject.setValue(10);
subject.detach(&observerB);
subject.setValue(20);
return 0;
}
使用场景:向前端发事情通知
根据edge的登录,更改前端功能是否开启/样式改变。
- 用户登录、退出登录,触发OnPrimaryAccountChanged,观察者模式发出通知
- 改变前端状态
具体操作:
- 写被Observer的抽象类,里面有Observer的抽象方法
- 写实体的Observer类
- 写Subject类,在里面写类的成员变量observerList,在里面写addObserver、removeObserver以及通知所有Observer方法
- 在用的地方初始化Object对象,并构造方法里把自己加入到observerList里
- 调用subject的方法,通知所有Observers
装饰器模式
#include <iostream>
// 抽象组件
class Component {
public:
virtual void operation() = 0;
};
// 具体组件
class ConcreteComponent : public Component {
public:
void operation() override {
std::cout << "ConcreteComponent operation." << std::endl;
}
};
// 装饰器基类
class Decorator : public Component {
public:
Decorator(Component* component) : component_(component) {}
void operation() override {
component_->operation();
}
private:
Component* component_;
};
// 具体装饰器 A
class ConcreteDecoratorA : public Decorator {
public:
ConcreteDecoratorA(Component* component) : Decorator(component) {}
void operation() override {
Decorator::operation();
std::cout << "Added behavior A." << std::endl;
}
};
// 具体装饰器 B
class ConcreteDecoratorB : public Decorator {
public:
ConcreteDecoratorB(Component* component) : Decorator(component) {}
void operation() override {
Decorator::operation();
std::cout << "Added behavior B." << std::endl;
}
};
int main() {
// 创建具体组件对象
Component* component = new ConcreteComponent();
// 使用装饰器 A 包装具体组件对象
component = new ConcreteDecoratorA(component);
// 使用装饰器 B 包装装饰器 A 包装的对象
component = new ConcreteDecoratorB(component);
// 执行操作
component->operation();
// 释放内存
delete component;
return 0;
}
在这个示例中,Component是一个抽象类,它定义了组件的基本操作。ConcreteComponent是一个具体组件,它实现了Component接口,并提供了具体实现。
Decorator是装饰器基类,它也实现了Component接口,并包含了一个指向Component对象的指针。Decorator类的主要作用是将行为添加到组件中。
ConcreteDecoratorA和ConcreteDecoratorB是具体装饰器类。它们派生自Decorator类,并重写了operation()方法以添加额外的行为。
在main()函数中,我们首先创建一个具体组件对象。然后使用ConcreteDecoratorA装饰该对象,并再次使用ConcreteDecoratorB装饰该对象。最后,我们调用operation()方法,执行带有所有附加行为的操作。
装饰器模式(Decorator Pattern)有以下几个优点:
- 动态地给对象添加行为:装饰器模式允许在运行时动态地给对象添加行为,而不需要使用静态继承。这使得我们可以在不修改现有代码的情况下,给对象添加新的功能。
- 多个装饰器可以组合使用:由于装饰器是可以嵌套使用的,因此我们可以创建多个不同的装饰器,将它们组合在一起,以实现复杂的行为。
- 保持接口一致性:使用装饰器模式可以保持对象的接口不变,这意味着无论是否使用装饰器,客户端代码都可以像处理常规对象一样处理装饰后的对象。
装饰器模式适用于以下场景:
- 在不影响其他对象的情况下,动态地给对象添加行为。这是装饰器模式的主要用途。
- 在不使用静态继承的情况下,扩展对象的功能。由于继承是静态的,因此如果需要在运行时动态添加行为,则使用装饰器模式是更好的选择。
- 需要在多个装饰器中组合使用不同的行为,以实现复杂的功能。使用装饰器模式可以方便地将多个行为组合在一起,而无需创建大量的子类。
- 需要保持接口的一致性,以便客户端代码可以像处理常规对象一样处理装饰后的对象。使用装饰器模式可以保持接口不变,这意味着客户端代码可以像处理常规对象一样处理装饰后的对象。
策略模式
#include <iostream>
using namespace std;
// 抽象策略类
class Strategy {
public:
virtual int execute(int a, int b) = 0;
};
// 具体策略类A
class ConcreteStrategyA : public Strategy {
public:
int execute(int a, int b) override { return a + b; }
};
// 具体策略类B
class ConcreteStrategyB : public Strategy {
public:
int execute(int a, int b) override { return a - b; }
};
// 上下文类
class Context {
public:
Context(Strategy* strategy) : m_strategy(strategy) {}
void setStrategy(Strategy* strategy) { m_strategy = strategy; }
int executeStrategy(int a, int b) { return m_strategy->execute(a, b); }
private:
Strategy* m_strategy;
};
// 客户端代码
int main() {
Context* context = new Context(new ConcreteStrategyA());
cout << "Result: " << context->executeStrategy(10, 5) << endl;
context->setStrategy(new ConcreteStrategyB());
cout << "Result: " << context->executeStrategy(10, 5) << endl;
delete context;
return 0;
}
上面的例子中,我们定义了一个抽象策略类 Strategy,它包含一个 execute 方法,用于执行具体的策略。我们还定义了两个具体策略类 ConcreteStrategyA 和 ConcreteStrategyB,它们分别实现了抽象策略类中的方法。
上下文类 Context 包含了一个策略对象,并提供了方法来切换策略。在客户端代码中,我们首先创建了一个上下文对象,它的策略是具体策略类A。然后我们执行上下文对象的方法,输出结果。接着我们将策略切换成具体策略类B,并再次执行方法,输出结果。最后我们删除上下文对象。
策略模式的优点是可以使算法的变化独立于使用算法的客户端,即客户端可以在不修改代码的情况下切换不同的算法。使用策略模式的场景是系统中有许多类似的行为,需要在不同的场景下使用不同的算法来完成这些行为。
最后更新于 2023年2月27日 by qlili