Jan 8, 2025
12 mins read
Factory:工厂类是整个模式的关键.包含了必要的逻辑判断,根据外界给定的信息,决定究竟应该创建哪个具体类的对象。
客户端代码无需关心类的实现细节,如果需要新增运算,只需要新增对应的派生类,并在工厂中添加逻辑。
class Operation
{
protected:
double numberA;
double numberB;
public:
void setNumberA(double a)
{
numberA = a;
}
void setNumberB(double b)
{
numberB = b;
}
virtual double GetResult()
{
double result = 0;
return result;
}
virtual ~Operation() = default;
};
class OperationAdd : public Operation
{
public:
double GetResult() override
{
return numberA + numberB;
}
};
class OperationSub : public Operation
{
public:
double GetResult() override
{
return numberA - numberB;
}
};
class OperationFactory
{
public:
static std::unique_ptr<Operation> createOperation(char op)
{
switch (op)
{
case '+':
return std::make_unique<OperationAdd>();
case'-':
return std::make_unique<OperationSub>();
default:
throw std::invalid_argument("Invalid operation type");
break;
}
}
};
int main()
{
try
{
char op;
std::cin >> op;
auto operation = OperationFactory::createOperation(op);
double a, b;
std::cin >> a >> b;
operation->setNumberA(a);
operation->setNumberB(b);
double result = operation->GetResult();
std::cout << result << std::endl;
}
catch(const std::exception& ex)
{
std::cerr << "Error " << ex.what() << std::endl;
}
return 0;
}
举例超市促销活动,有打八折、满300 - 100的活动等,如果给每一种活动都增加一个实现方法,重复度太高不方便维护。所以要将每个算法封装起来,并且可以互相替换,不同的策略表现出不同的行为,这就是策略模式。
组成:
环境角色(Context):屏蔽客户端对策略算法的直接访问,封装可能存在的变化。
抽象策略角色(Strategy):抽象类,给出所有具体策略类所需的接口。
具体策略角色(ConcreteStrategy):具体的策略、算法实现。
class CashStrategy
{
public:
virtual double calculate(double amount) const = 0;
virtual ~CashStrategy() = default;
};
// 正常收费
class CashNormal : public CashStrategy
{
public:
double calculate(double amount) const override
{
return amount;
}
};
// 满300 -100
class CashReturn :public CashStrategy
{
private:
double condition;
double cashback;
public:
CashReturn(double condition, double cashback) : condition(condition), cashback(cashback){}
double calculate(double amount)const override
{
if (amount >= condition)
{
return amount - (int(amount / condition)) * cashback;
}
return amount;
}
};
class CashRebate : public CashStrategy
{
private:
double rebate;
public:
CashRebate(double rebate):rebate(rebate){}
double calculate(double amount)const override
{
return amount * rebate;
}
};
class CashContext
{
private:
std::unique_ptr<CashStrategy> strategy;
public:
explicit CashContext(std::unique_ptr<CashStrategy> strategy) : strategy(std::move(strategy)){}
double getResult(double amount) const
{
return strategy->calculate(amount);
}
};
int main()
{
double amount;
std::cin >> amount;
CashContext contextNormal(std::make_unique<CashNormal>());
std::cout << "正常收费" << contextNormal.getResult(amount) << std::endl;
CashContext contextReturn(std::make_unique<CashReturn>(300, 100));
std::cout << "满300减100: " << contextReturn.getResult(amount) << std::endl;
CashContext contextRebate(std::make_unique<CashRebate>(0.8));
std::cout << "打八折: " << contextRebate.getResult(amount) << std::endl;
return 0;
}
就一个类而言,应该仅有一个引起它变化的原因。
如果一个类承担的职责过多,就等于把这些职责耦合在一起,一个职责的变化可能会削弱或者抑制这个类完成其他职责的能力。这种耦合会导致脆弱的设计,当变化发生时,设计会遭受到意想不到的破坏。
对于扩展是开放的,对于更改是封闭的。
但无论模块是多么的封闭,都会存在一些无法对之封闭的变化。设计人员必须对他设计的模块应该对哪种变化封闭做出选择。他必须先猜测出最有可能发生的变化种类,然后构造抽象来隔离那些变化。
面对需求,对程序的改动是通过增加新代码进行的,而不是更改现有的代码。
高层模块不应该依赖低层模块,两个都应该依赖抽象。
抽象不应该依赖细节,细节应该依赖于抽象。
总结:谁也不要依赖谁,除了约定的接口,大家都可以灵活自如。
子类可以替换父类,并且程序的行为不会发生变化。
例子:正方形在数学中属于长方形,于是在代码中将正方形继承于长方形,但是宽高在设置的时候会出现冲突,导致最后计算面积出错。所以它不遵循里氏替换原则,需要将正方形、长方形类分离,实现共同的Shape接口。
组成:
主要目的是通过共享对象来减少系统种对象的数量,其本质就是缓存共享对象,降低内存消耗。
享元模式将需要重复使用的对象分为两个部分:内部状态和外部状态。比如用户的账号是外部状态不能被共享,内部状态是可以共享的。
组成:
例子:如围棋游戏,只需要存储两个棋子对象,而不是每盘棋创建几百个棋子对象,比如.Net中的String,相同的字符串只有一个实例。
懒汉式:需要时(第一次调用GetInstance时)才会创建 线程不安全(需要加锁)
适用于可能会用到该实例。
怎么保证单例?
让构造函数 private
,并提供 静态方法 GetInstance()
来获取唯一实例。
class Singleton {
private:
static Singleton* instance;
Singleton() { cout << "构造函数\n"; }
public:
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
static Singleton* GetInstance() {
if (instance == nullptr) {
instance = new Singleton();
}
return instance;
}
void Show() { cout << "Singleton实例方法\n"; }
};
Singleton* Singleton::instance = nullptr;
懒汉式线程安全加锁版:
class Singleton {
private:
static Singleton* instance;
static mutex mtx;
Singleton() { cout << "构造函数\n"; }
public:
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
static Singleton* GetInstance() {
lock_guard<mutex> lock(mtx);
if (instance == nullptr) {
instance = new Singleton();
}
return instance;
}
};
Singleton* Singleton::instance = nullptr;
mutex Singleton::mtx;
懒汉式线程安全局部静态变量版:
class Singleton {
private:
Singleton() { cout << "构造函数\n"; }
public:
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
static Singleton* GetInstance() {
// 对象会自动初始化 但是指针不会自动初始化 所以要初始化对象 返回对象的地址
static Singleton instance;
return &instance;
}
};
void ThreadFunc(int id) {
cout << "id = " << id << ",Singleton = " << Singleton::GetInstance() << endl;
}
int main() {
thread t1(ThreadFunc, 1);
thread t2(ThreadFunc, 2);
t1.join();
t2.join();
return 0;
}
*饿汉式:*一定会用到该实例,程序在启动时就会创建实例。
#include<iostream>
using namespace std;
class Singleton {
private:
static Singleton* instance;
Singleton() { cout << "构造函数\n"; }
public:
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
static Singleton* GetInstance() {
return instance;
}
};
Singleton* Singleton::instance = new Singleton;
Sharing is caring!