引言
本篇文章介绍了多种设计模式,我们不仅仅只需要理解设计模式,还要可以写出类似的代码。
单例模式
单例模式故名思意就是全局只设置一个实例,全局的所有对象都可以访问这个实例。这样子的优点有几个:
我们可以比较方便的管理这一个实例,并且在创建的时候我们也有两个选择,一个是一开始就创建这个对象,这个叫"饿汉模式",一个是当要用的时候再创建,这个叫"懒汉模式"。但是大家注意,这个懒汉模式会有一个比较坑的地方,如果更巧在同一时刻多个线程同时创建了这个对象,那么很可能一下子就出现了多个单例,所以就失效了,那么我们就需要加锁~~~不过对于c++来说不需要,我们代码里面会说明。
我们要创建一个单例需要满足几个条件:私有的构造函数(防止new出新对象),共有的静态方法,私有的静态实例变量(就是和这个实例有关的变量,因为实例既然不可以让别人公开创建,说明这个变量全局也只有一个)。
我们还是用一个例子来引出单例模式的代码,不过我们还是先分析一下这个例子为什么是单例模式。首先我们只有一个购物车,这个购物车
小明去了一家大型商场,拿到了一个购物车,并开始购物。请你设计一个购物车管理器,记录商品添加到购物车的信息(商品名称和购买数量),并在购买结束后打印出商品清单。
首先我们为了禁止new其他的实例,我们先把构造函数放进私有类,然后c++11里面增加了一个新特性就是static函数的调用会按照顺序调用,也就是相当于加了一个锁,所以在c++里面我们不需要单独加锁,并且我们的static也方便外部调用,不需要创建对象,直接访问,并且因为这是一个全局变量,所以我们使用指针的形式进行保存,当然也可以用引用&的形式。
cpp
class ShoppingCartManager {
private:
unordered_map<string, int> cart;
vector<string> order; // 保持顺序
// 私有构造函数
ShoppingCartManager() {}
public:
// 获取购物车实例
static ShoppingCartManager* getInstance() {
static ShoppingCartManager instance;
return &instance;
}
// 添加商品
void addToCart(const string& itemName, int quantity) {
if (cart.find(itemName) == cart.end()) {
order.emplace_back(itemName);
}
cart[itemName] += quantity;
}
// 查看商品
void viewCart() const {
for (const auto& itemName : order) {
cout << itemName << " " << cart.at(itemName) << endl;
}
}
};
我们先得到我们的实例,然后才开始操作
cpp
void SingletonPatternTest() {
string itemName;
int quantity;
ShoppingCartManager* cart = ShoppingCartManager::getInstance();
string line;
while (getline(cin, line)) {
if (line.empty()) {
break;
}
stringstream input(line);
if (input >> itemName >> quantity) {
cart->addToCart(itemName, quantity);
}
}
cart->viewCart();
}
工厂模式
我们首先要介绍工厂模式,就是if-else之类的,但是这个有一个很大的弊端就是代码一旦扩展,那么这所有的代码都需要被改动,所以扩展性十分的差。那么就要提到我们的工厂模式。我们的工厂分为具体工厂和抽象工厂,具体工厂就是具体干活的,而抽象工厂是一个基类,也就是提供给我们一个访问的接口,我们外人的访问都是抽象的,因为这样就可以避免我们对于具体类的一个操作,完完全全的屏蔽了对于内部的一个访问。
工厂模式有一下的几个角色:
- 抽象工厂:一个接口,包含一个抽象的工厂方法(用于创建产品对象)。
- 具体工厂:实现抽象工厂接口,创建具体的产品。
- 抽象产品:定义产品的接口。
- 具体产品:实现抽象产品接口,是工厂创建的对象。
我们还是用一个场景带入理解:
小明家有两个工厂,一个用于生产圆形积木,一个用于生产方形积木,请你帮他设计一个积木工厂系统,记录积木生产的信息。
这个是我们的产品区,也就是我们怎么产生产品(利用工厂提供的资源),首先我们提供了一个产品的抽象类,我们之后也是通过这个接口来访问不同的产品。
cpp
class Block {
public:
virtual void produce() = 0;
};
class CircleBlock : public Block {
public:
void produce() override {
std::cout << "CircleBlock produced" << std::endl;
}
};
class SquareBlock : public Block {
public:
void produce() override {
std::cout << "SquareBlock produced" << std::endl;
}
};
这个是工厂区,因为这个比较的简单,所以工厂的作用就是返回每个工厂对应的产品(new出来),而我们对外面提供的就是抽象类。
cpp
class BlockFactory {
public:
virtual Block* createBlock() = 0;
};
class CircleBlockFactory : public BlockFactory {
public:
virtual Block* createBlock() override {
return new CircleBlock();
}
};
class SquareBlockFactory : public BlockFactory {
public:
virtual Block* createBlock() override {
return new SquareBlock();
}
};
最后我们对外面提供一个统一的接口,外面访问的时候需要传递需要制造物品的工厂,对应的数量,所以我们可以看到我们使用的全部都是基类,因为外面传递进来的一定是具体的子类,然后我们在调用的函数也是抽象类里面的,因为最后根据传入的参数会匹配到对应的函数。
cpp
class BlockFactorySystem {
private:
std::vector<Block*> blocks;
public:
void produceBlock(BlockFactory* factory, int quantity) {
for (int i = 0; i < quantity; i++) {
Block* block = factory->createBlock();
blocks.push_back(block);
block->produce();
}
}
const std::vector<Block*>& getBlocks() {
return blocks;
}
~BlockFactorySystem() {
for (Block* block : blocks) {
delete block;
}
}
};
最后就是怎么使用这个工厂,我们先创建一个大工厂,因为这个是我们访问的入口,然后根据需求传递参数,不过大家要记得我们在一开始只创建了一个大工厂,而我们的小工厂还没有创建,所以在传递的时候要顺便new一个工厂,而产品的产生则不需要我们关心,因为工厂会自己产生。
cpp
void FactoryPartternTest() {
BlockFactorySystem blockFactorySystem;
int productionCount = 0;
std::cin >> productionCount;
for (int i = 0; i < productionCount; i++) {
std::string blockType;
int quantity;
std::cin >> blockType >> quantity;
if (blockType == "Circle") {
blockFactorySystem.produceBlock(new CircleBlockFactory(), quantity);
} else if (blockType == "Square") {
blockFactorySystem.produceBlock(new SquareBlockFactory(), quantity);
}
}
return;
}
总结
本篇文章就到这里结束了!!!设计模式不仅仅在我们构建架构的时候会有帮助,其实更多的时候是在读代码,可以帮我们省很多的时间帮我们搞清楚架构,这个很重要。所以希望可以帮助大家理解~~~