1.前言
設計模式一共有23種,主要分為三大類型:創建型,結構型,行為型。本篇文章著重講解的是創建型一些相關的設計模式
2.單例模式
Singleton 模式是設計模式中最為簡單、最為常見、最容易實現,也是最應該熟悉和掌握的模式。Singleton 模式就是一個類只創建一個唯一的對象,即一次創建多次使用。
此前在cpp專欄里面也專門敘述了單例模式,這里就不過多的重復敘述。想了解的可以閱讀這篇文章:
[c++進階(三)]單例模式及特殊類的設計-CSDN博客
3.工廠模式
在面向對象系統設計中經常可以遇到以下的兩類問題:
我們經常會抽象出一些類的公共接口以形成抽象基類或者接口。這樣我們可以通過聲明一個指向基類的指針來指向實際的子類實現,達到了多態的目的。所以就不得不在要用到子類的地方寫new 對象。這樣實體類的使用者必須知道實際的子類名稱,以及會使程序的擴展性和維護變得越來越困難。
還有一種情況就是在父類中并不知道具體要實例化哪一個具體的子類。只能在父類中寫方法調用,具體調用哪一個類的方法交給子類實現。
為了解決上述問題,于是就引出了簡單工廠模式,工廠方法模式和抽象工廠模式三種。
3.1 簡單工廠模式
定義:簡單工廠模式,又叫做靜態工廠方法(Static Factory Method)模式,是由一個工廠對象決定創建出哪一種產品類的實例。
那么這里由一個工廠就指的是用一個靜態對象,客戶端通過調用這個靜態對象,并傳遞參數給這個靜態對象來實現不同對象的不同功能。
eg:我們通過一個基類來構建不同模型的圖形,然后客戶端不直接調用子類對象,而是通過一個靜態工廠的方法來調用子類的相關成員函數
// 產品基類:定義統一接口
class Shape {
public:virtual void draw() = 0;virtual ~Shape() = default;
};// 具體產品:圓形
class Circle : public Shape {
public:void draw() override {std::cout << "畫圓形" << std::endl;}
};// 具體產品:矩形
class Rectangle : public Shape {
public:void draw() override {std::cout << "畫矩形" << std::endl;}
};// 無工廠的情況:客戶端依賴具體產品
Shape* shape1 = new Circle(); // 客戶端必須知道Circle
Shape* shape2 = new Rectangle(); // 客戶端必須知道Rectangle//有工廠的情況下:
// 工廠類:負責所有產品的創建
class ShapeFactory {
public:// 根據參數決定創建哪種產品static Shape* createShape(const std::string& type) {if (type == "circle") {return new Circle();} else if (type == "rectangle") {return new Rectangle();}return nullptr; // 無效類型}
};// 客戶端通過工廠獲取產品,不依賴具體實現
Shape* shape1 = ShapeFactory::createShape("circle"); // 無需知道Circle
Shape* shape2 = ShapeFactory::createShape("rectangle"); // 無需知道Rectangle
shape1->draw(); // 畫圓形
shape2->draw(); // 畫矩形
簡單工廠的優缺點:優點:不直接在客戶端new一個子類,而是通過工廠類來完成一系列操作,降低了耦合性
缺點:違反了開閉原則,(對擴展開放,對修改關閉),不容易形成高內聚松耦合結構。 每當我們增加一種產品的時候就要去修改工廠方法,這樣會破壞其內聚性,給維護帶來額外開支。為了克服簡單工廠方法模式的缺點,工廠方法模式和抽象工廠模式就被提了出來
3.2 工廠方法模式
定義:一個用于創建對象的接口,讓子類決定實例化哪個類
工廠方法模式的核心思想:討論的仍然是如何構建同一類型產品(都實現同一個接口)的問題,只不過是通過為每一種要生產的產品配備一個工廠,就是說每個工廠只生產一種特定的產品。這樣做的好處就是當以后需要增加新的產品時,直接新增加一個對應的工廠就可以了,而不是去修改原有的工廠,符合編程原則的開閉原則。
工廠方法模式為每一種產品生成一個對應的工廠,從而替換掉簡單工廠方法模式中那個靜態工廠方法。
#include <iostream>
#include <memory>// 產品基類:電腦
class Computer {
public:virtual void showInfo() const = 0;virtual ~Computer() = default;
};// 具體產品:小米電腦
class XiaomiComputer : public Computer {
public:void showInfo() const override {std::cout << "這是一臺小米電腦,性價比高!" << std::endl;}
};// 具體產品:Mac電腦
class MacComputer : public Computer {
public:void showInfo() const override {std::cout << "這是一臺Mac電腦,設計精美!" << std::endl;}
};// 工廠基類:定義創建電腦工廠的抽象方法
class ComputerFactory {
public:virtual std::unique_ptr<Computer> createComputer() const = 0;virtual ~ComputerFactory() = default;
};// 具體工廠:小米電腦工廠
class XiaomiComputerFactory : public ComputerFactory {
public:std::unique_ptr<Computer> createComputer() const override {return std::make_unique<XiaomiComputer>();}
};// 具體工廠:Mac電腦工廠
class MacComputerFactory : public ComputerFactory {
public:std::unique_ptr<Computer> createComputer() const override {return std::make_unique<MacComputer>();}
};// 客戶端代碼
int main() {// 創建小米電腦工廠std::unique_ptr<ComputerFactory> xiaomiFactory = std::make_unique<XiaomiComputerFactory>();// 通過小米工廠創建電腦std::unique_ptr<Computer> xiaomiComputer = xiaomiFactory->createComputer();xiaomiComputer->showInfo(); // 輸出:這是一臺小米電腦,性價比高!// 創建Mac電腦工廠std::unique_ptr<ComputerFactory> macFactory = std::make_unique<MacComputerFactory>();// 通過Mac工廠創建電腦std::unique_ptr<Computer> macComputer = macFactory->createComputer();macComputer->showInfo(); // 輸出:這是一臺Mac電腦,設計精美!return 0;
}
優點:不再違反開閉原則
缺點:如果要拓展,那么類會越寫越多。?
3.3 抽象工廠模式
定義:抽象工廠為創建一組相關或者是相互依賴的對象提供一個接口,而不需要指定他們的具體類。
概念永遠都是最難理解的,那么我們用一個實際的例子來理解這句話。
還是小米電腦和Mac電腦,那么此時我又要生產小米手機和mac手機,那么按照工廠方法模式則需要再拓展三個類,一個手機抽象類,2個具體手機類。
那么這就是兩個家族之間的事了,小米家族負責生成小米手機和小米電腦,而mac家族負責生產mac電腦和mac手機。
那么這個時候抽象工廠就可以派上用場了。
還是上述例子:轉換成圖形如下所示:
用工廠方法模式,新增代碼如下:?
// 新增:產品基類:手機
class Phone {
public:virtual void showInfo() const = 0;virtual ~Phone() = default;
};// 新增:具體產品:小米手機
class XiaomiPhone : public Phone {
public:void showInfo() const override {std::cout << "這是一臺小米手機,性能強勁!" << std::endl;}
};// 新增:具體產品:Mac手機(iPhone)
class MacPhone : public Phone {
public:void showInfo() const override {std::cout << "這是一臺Mac手機,生態流暢!" << std::endl;}
};
使用抽象工廠模式代碼如下:
#include <iostream>
#include <memory>// ===== 產品接口 =====
// 電腦接口
class Computer {
public:virtual void showInfo() const = 0;virtual ~Computer() = default;
};// 手機接口
class Phone {
public:virtual void showInfo() const = 0;virtual ~Phone() = default;
};// ===== 具體產品 =====
// 小米電腦
class XiaomiComputer : public Computer {
public:void showInfo() const override {std::cout << "小米電腦:高性能處理器,輕薄設計" << std::endl;}
};// Mac電腦
class MacComputer : public Computer {
public:void showInfo() const override {std::cout << "Mac電腦:精致外觀,流暢系統" << std::endl;}
};// 小米手機
class XiaomiPhone : public Phone {
public:void showInfo() const override {std::cout << "小米手機:高性價比,5G支持" << std::endl;}
};// Mac手機(iPhone)
class MacPhone : public Phone {
public:void showInfo() const override {std::cout << "Mac手機:iOS生態,A系列芯片" << std::endl;}
};// ===== 抽象工廠接口 =====
class DeviceFactory {
public:virtual std::unique_ptr<Computer> createComputer() const = 0;virtual std::unique_ptr<Phone> createPhone() const = 0;virtual ~DeviceFactory() = default;
};// ===== 具體工廠 =====
// 小米工廠:生產小米品牌的設備
class XiaomiFactory : public DeviceFactory {
public:std::unique_ptr<Computer> createComputer() const override {return std::make_unique<XiaomiComputer>();}std::unique_ptr<Phone> createPhone() const override {return std::make_unique<XiaomiPhone>();}
};// Mac工廠:生產Mac品牌的設備
class MacFactory : public DeviceFactory {
public:std::unique_ptr<Computer> createComputer() const override {return std::make_unique<MacComputer>();}std::unique_ptr<Phone> createPhone() const override {return std::make_unique<MacPhone>();}
};// ===== 客戶端代碼 =====
int main() {// 創建小米工廠,生產小米設備std::unique_ptr<DeviceFactory> xiaomiFactory = std::make_unique<XiaomiFactory>();auto xiaomiPC = xiaomiFactory->createComputer();auto xiaomiMobile = xiaomiFactory->createPhone();xiaomiPC->showInfo(); // 輸出:小米電腦:高性能處理器,輕薄設計xiaomiMobile->showInfo(); // 輸出:小米手機:高性價比,5G支持// 創建Mac工廠,生產Mac設備std::unique_ptr<DeviceFactory> macFactory = std::make_unique<MacFactory>();auto macPC = macFactory->createComputer();auto macMobile = macFactory->createPhone();macPC->showInfo(); // 輸出:Mac電腦:精致外觀,流暢系統macMobile->showInfo(); // 輸出:Mac手機:iOS生態,A系列芯片return 0;
}
4.原型模式
原型模式屬于創建型模式,所以它是描述如何創建對象的模式。顧名思義,先搞一個原型對象出來,然后在這個原型對象的基礎上修修補補再弄出一個新對象來。
定義:使用原型實例指定待創建對象的類型,并且通過復制這個原型來創建新的對象。
#include <iostream>
#include <memory>// 原型接口
class Shape {
public:virtual ~Shape() = default;virtual std::unique_ptr<Shape> clone() const = 0; // 純虛函數,定義克隆接口virtual void printInfo() const = 0; // 純虛函數,用于打印信息
};// 具體原型類:Circle
class Circle : public Shape {
private:int x;int y;double radius;public:// 構造函數Circle(int x, int y, double radius) : x(x), y(y), radius(radius) {}// 拷貝構造函數Circle(const Circle& other) : x(other.x), y(other.y), radius(other.radius) {std::cout << "Circle拷貝構造函數被調用" << std::endl;}// 克隆方法實現std::unique_ptr<Shape> clone() const override {std::cout << "Circle克隆方法被調用" << std::endl;return std::make_unique<Circle>(*this); // 調用拷貝構造函數}// 打印信息void printInfo() const override {std::cout << "Circle: x=" << x << ", y=" << y << ", radius=" << radius << std::endl;}
};// 具體原型類:Rectangle
class Rectangle : public Shape {
private:int x;int y;double width;double height;public:// 構造函數Rectangle(int x, int y, double width, double height) : x(x), y(y), width(width), height(height) {}// 拷貝構造函數Rectangle(const Rectangle& other) : x(other.x), y(other.y), width(other.width), height(other.height) {std::cout << "Rectangle拷貝構造函數被調用" << std::endl;}// 克隆方法實現std::unique_ptr<Shape> clone() const override {std::cout << "Rectangle克隆方法被調用" << std::endl;return std::make_unique<Rectangle>(*this); // 調用拷貝構造函數}// 打印信息void printInfo() const override {std::cout << "Rectangle: x=" << x << ", y=" << y << ", width=" << width << ", height=" << height << std::endl;}
};int main() {// 使用原型模式創建對象std::unique_ptr<Shape> circle1 = std::make_unique<Circle>(10, 20, 5.0);std::unique_ptr<Shape> circle2 = circle1->clone(); // 通過克隆方法復制對象std::unique_ptr<Shape> rect1 = std::make_unique<Rectangle>(0, 0, 100.0, 200.0);std::unique_ptr<Shape> rect2 = rect1->clone(); // 通過克隆方法復制對象// 使用拷貝構造函數直接創建對象Circle circle3(*circle1); // 直接調用Circle的拷貝構造函數Rectangle rect3(*rect1); // 直接調用Rectangle的拷貝構造函數// 打印所有對象信息std::cout << "\n所有對象的信息:" << std::endl;circle1->printInfo();circle2->printInfo();circle3.printInfo();rect1->printInfo();rect2->printInfo();rect3.printInfo();return 0;
}
5.建造者模式
定義:將一個復雜對象的構建與其表示分離,使得同樣的構建過程可以創建不同的表示。
簡單來說就是將一個復雜的對象通過分成好幾步來進行構建,然后統一把這好幾步交給一個指揮者來來完成最后的組裝。
比如組裝一臺電腦,那么就需要有cpu、主板、鍵盤、顯示器等組成。
建造者模式
#include <memory>//eg:通過蘋果電腦的構造理解建造者模式
class computer {//電腦要包含os,board,display
public:virtual void setboard(const string& board) = 0;virtual void setdisplay(const string& display) = 0;virtual void setos() = 0;void print() {string param = "computer paramaters:\n";param += "\tboard: " + _board + "\n";param += "\tdisplay: " + _display + "\n";param += "\tos: " + _os + "\n";cout << param << endl;}
protected:string _os;string _board;string _display;
};//具體mac電腦
class maccomputer :public computer {
public:void setboard(const string& board) override{_board = board;}void setdisplay(const string& display) override {_display = display;}void setos() override {_os = "mac x64";}
};//抽象構建類
class builder {
public:virtual void buildboard(const string& board) = 0;virtual void builddisplay(const string& display) = 0;virtual void buildos() = 0;virtual std::shared_ptr<computer> build() = 0;
};//具體產品的構建
class macbookbuild :public builder{
public:macbookbuild() :_computer(new maccomputer()) {}void buildboard(const string& board) override {_computer->setboard(board);}void builddisplay(const string& display) override {_computer->setdisplay(display);}void buildos() override {_computer->setos();}std::shared_ptr<computer> build() {return _computer;}
private:std::shared_ptr<computer> _computer;
};class director {//指揮者負責組裝一整套電腦
public:director(builder* builder):_build(builder){}void construct(const string& board, const string& display) {_build->buildboard(board);_build->builddisplay(display);_build->buildos();}
private:std::shared_ptr<builder> _build;
};int main() {builder* builder = new macbookbuild();unique_ptr<director> director(new director(builder));director->construct("羅技","三星");shared_ptr<computer> computer = builder->build();computer->print();return 0;
}
建造者模式的使用場景:當一個類的構造函數的參數超過四個,且這四個參數還是可選可不選的,那么就可以考慮使用建造者模式了。?
6.總結
我們一定要牢記設計模式是前人總結的一套可以有效解決問題的經驗,不要一寫代碼就在考慮該使用什么設計模式,這是極其不可取的。正確的做法應該是在實現業務需求的同時,盡量遵守面向對象設計的六大設計原則即可。后期隨著業務的擴展,你的代碼會不斷的演化和重構,在此過程中設計模式絕逼會大放異彩的。
說實話博主寫完這篇文章感覺原型模式模式是不是有點多余啊,想復制原來創建的新的對象,使用拷貝構造函數不就可以了嘛,為什么還要弄一個新的設計模式出來呢。目前博主的理解就到這啦,有興趣的可以后臺TT博主一起交流呀!