C++設計模式之創建型模式

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博主一起交流呀!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/89155.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/89155.shtml
英文地址,請注明出處:http://en.pswp.cn/web/89155.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

kubernetes學習筆記(一)

kubernetes學習筆記(一) kubernetes簡介 ? Kubernetes是Google開源的一個容器編排引擎&#xff0c;它支持自動化部署、大規模可伸縮、應用容器化管理。在生產環境中部署一個應用程序時&#xff0c;通常要部署該應用的多個實例以便對應用請求進行負載均衡。 ? 在Kubernetes…

Eureka實戰

1.創建父工程SpringCloudTestSpringCloudTest為父工程&#xff0c;用于引入通用依賴&#xff0c;如spring-boot-starter-web、lombok&#xff0c;這樣子工程就可以直接繼承&#xff0c;無需重復引入。在dependencyManagement標簽中引入和springboot版本對應的springcloud&#…

如何把鏡頭對焦在超焦距上

要把鏡頭對焦在超焦距上&#xff0c;可以按照以下步驟操作&#xff1a;1. 計算超焦距 首先需要知道你的鏡頭參數和相機參數&#xff1a; 焦距 f&#xff08;如 24mm、35mm&#xff09;光圈 N&#xff08;如 f/8、f/11&#xff09;容許彌散圓直徑 c&#xff08;與傳感器尺寸有關…

idea docker插件連接docker失敗

報錯org.apache.hc.client5.http.HttpHostConnectException:Connect to http://localhost:2375 [localhost/127.0.0.1, localhost/0:0:0:o:0:0:0:1] failed:Connection refused:getsockopt解決方法&#xff1a;

【后端】.NET Core API框架搭建(6) --配置使用MongoDB

目錄 1.添加包 2. 連接配置 2.1.鏈接字符串 2.2.連接類 3.倉儲配置 3.1.倉儲實現 3.2.倉儲接口 4.獲取配置和注冊 4.1.添加配置獲取方法 4.2.注冊 5.常規使用案例 5.1實體 5.2.實現 5.3.接口 5.4.控制器 NET Core 應用程序中使用 MongoDB 有許多好處&#xff0c;尤其是在…

Spring AI快速入門

文章目錄1 介紹1_大模型對比2_開發框架對比2 快速入門1_引入依賴2 配置模型3 配置客戶端4 測試3 會話日志1_Advisor2 添加日志Advisor4 會話記憶1_定義會話存儲方式2 配置會話記憶Advisor5 會話歷史1_管理會話歷史2 保存會話id3 查詢會話歷史6 后續1 介紹 SpringAI整合了全球&…

Windows下編譯pthreads

本文記錄在Windows下編譯pthreads的流程。 零、環境 操作系統Windows 11VS Code1.92.1Git2.34.1MSYS2msys2-x86_64-20240507Visual StudioVisual Studio Community 2022CMake3.22.1 一、編譯安裝 1.1 下載 git clone https://git.code.sf.net/p/pthreads4w/code 1.2 構建…

WP Force SSL Pro – HTTPS SSL Redirect Boost Your Website‘s Trust in Minutes!

In the vast digital landscape where security and user trust are paramount, ensuring your WordPress site uses HTTPS is not just a recommendation—it’s a necessity. That’s where WP Force SSL Pro – HTTPS SSL Redirect steps in as your silent guardian, makin…

jvm--java代碼對照字節碼圖解

java代碼&#xff1a;無靜態方法&#xff1b;&#xff08;對應字節碼沒有方法&#xff09; 任何一個類&#xff0c;至少有一個構造器&#xff0c;默認是無參構造java代碼包含&#xff1a;靜態方法java代碼包含&#xff1a;靜態方法、顯示構造方法public class ClassInitTest {p…

動態規劃題解_打家劫舍【LeetCode】

198. 打家劫舍 你是一個專業的小偷&#xff0c;計劃偷竊沿街的房屋。每間房內都藏有一定的現金&#xff0c;影響你偷竊的唯一制約因素就是相鄰的房屋裝有相互連通的防盜系統&#xff0c;如果兩間相鄰的房屋在同一晚上被小偷闖入&#xff0c;系統會自動報警。 給定一個代表每個…

電腦安裝 Win10 提示無法在當前分區上安裝Windows的解決辦法

原因&#xff1a; win10系統均添加快速啟動功能&#xff0c;預裝的win10電腦默認都是UEFI引導和GPT硬盤&#xff0c;傳統的引導方式為Legacy引導和MBR硬盤&#xff0c;UEFI必須跟GPT對應&#xff0c;同理Legacy必須跟MBR對應。如果BIOS開啟UEFI&#xff0c;而硬盤分區表格式為M…

大端序與小端序

理解大端序&#xff08;Big-Endian&#xff09;和小端序&#xff08;Little-Endian&#xff09;的關鍵在于數據在內存中存儲時字節的排列順序&#xff0c;特別是在存儲多字節數據類型&#xff08;如整數、浮點數&#xff09;時。以下是清晰易懂的解釋&#xff1a;核心概念 假設…

PyTorch筆記5----------Autograd、nn庫

1.Autograd grad和grad_fn grad&#xff1a;該tensor的梯度值&#xff0c;每次在計算backward時都需要將前一時刻的梯度歸零&#xff0c;否則梯度值會一直累加grad_fn&#xff1a;葉子結點通常為None&#xff0c;只有結果節點的grad_fn才有效&#xff0c;用于只是梯度函數時哪…

Perl 格式化輸出

Perl 格式化輸出 引言 Perl 是一種通用、解釋型、動態編程語言&#xff0c;廣泛應用于文本處理、系統管理、網絡編程等領域。在Perl編程中&#xff0c;格式化輸出是一種常見的需求&#xff0c;它可以幫助開發者更好地展示和打印信息。本文將詳細講解Perl中格式化輸出的方法&…

Python爬蟲實戰:研究markdown2庫相關技術

一、引言 1.1 研究背景與意義 在當今信息爆炸的時代,互聯網上的信息量呈指數級增長。如何高效地獲取和整理這些信息成為了一個重要的研究課題。網絡爬蟲作為一種自動獲取網頁內容的技術,能夠按照一定的規則,自動地抓取萬維網信息,為信息的收集提供了有力手段。 Markdown …

【Linux】基本指令詳解(二) 輸入\輸出重定向、一切皆文件、認識管道、man、cp、mv、echo、cat

文章目錄一、man指令二、輸入/輸出重定向(echo、一切皆文件&#xff09;三、cp指令四、mv指令五、cat指令六、more/less指令七、head/tail指令八、管道初見一、man指令 Linux的指令有很多參數&#xff0c;我們不可能全記住&#xff0c;可以通過查看聯機手冊獲取幫助。 man 指令…

MVC HTML 幫助器

MVC HTML 幫助器 引言 MVC&#xff08;模型-視圖-控制器&#xff09;是一種流行的軟件架構模式&#xff0c;它將應用程序的邏輯分解為三個主要組件&#xff1a;模型&#xff08;Model&#xff09;、視圖&#xff08;View&#xff09;和控制器&#xff08;Controller&#xff09…

linux下手工安裝ollama0.9.6

1、去下載ollama的linux版的壓縮包&#xff1a; 地址&#xff1a;https://github.com/ollama/ollama/releases2、上傳到linux中。3、解壓&#xff1a; tar zxvf ollama-linux-amd64-0.9.6.tgz -C /usr/local/4、如果僅僅是要手工執行&#xff0c;已經可以了&#xff1a; ollama…

kotlin布局交互

將 wrapContentSize() 方法鏈接到 Modifier 對象&#xff0c;然后傳遞 Alignment.Center 作為實參以將組件居中。Alignment.Center 會指定組件同時在水平和垂直方向上居中。 DiceWithButtonAndImage(modifier Modifier.fillMaxSize().wrapContentSize(Alignment.Center) )創建…

50天50個小項目 (Vue3 + Tailwindcss V4) ? | ToastNotification(推送通知)

&#x1f4c5; 我們繼續 50 個小項目挑戰&#xff01;—— ToastNotification組件 倉庫地址&#xff1a;https://github.com/SunACong/50-vue-projects 項目預覽地址&#xff1a;https://50-vue-projects.vercel.app/ 使用 Vue 3 的 Composition API&#xff08;<script s…