設計模式-對象創建

對象創建

  • 前言
  • 1. Factory Method
    • 1.1 模式介紹
    • 1.2 模式代碼
      • 1.2.1 問題代碼
      • 1.2.2 重構代碼
    • 1.3 模式類圖
    • 1.4 要點總結
  • 2. Abstract Factory
    • 2.1 模式介紹
    • 2.2 模式代碼
      • 2.2.1 問題代碼
      • 2.2.2 重構代碼
    • 2.3 模式類圖
    • 2.4 要點總結
  • 3. Prototype
    • 3.1 模式介紹
    • 3.2 模式代碼
    • 3.3 模式類圖
    • 3.4 要點總結
  • 4. Builder
    • 4.1 模式介紹
    • 4.2 模式代碼
    • 4.3 模式類圖
    • 4.4 要點總結

前言

“對象創建”模式:
通過“對象創建” 模式繞開new,來避免對象創建(new)過程中所導致的緊耦合(依賴具體類),從而支持對象創建的穩定。它是接口抽象之后的第一步工作。

典型模式:

  • Factory Method 工廠方法
  • Abstract Factory 抽象工廠
  • Prototype 原型模式
  • Builder 構建器模式

1. Factory Method

1.1 模式介紹

動機:在軟件系統中,經常面臨著創建對象的工作;由于需求的變化,需要創建的對象的具體類型經常變化。

如何應對這種變化?如何繞過常規的對象創建方法(new),提供一種“封裝機制”來避免客戶程序和這種“具體對象創建工作”的緊耦合?

定義一個用于創建對象的接口,讓子類決定實例化哪一個類。Factory Method使得一個類的實例化延遲(目的:解耦,手段:虛函數)到子類。

——《設計模式》GoF

1.2 模式代碼

1.2.1 問題代碼

class ISplitter{public:virtual void split()=0;virtual ~ISplitter(){}};class BinarySplitter : public ISplitter{};class TxtSplitter: public ISplitter{};class PictureSplitter: public ISplitter{};class VideoSplitter: public ISplitter{};class MainForm : public Form
{TextBox* txtFilePath;TextBox* txtFileNumber;ProgressBar* progressBar;public:void Button1_Click(){ISplitter * splitter=new BinarySplitter();//依賴具體類splitter->split();}
};

你有一個按鈕,點擊按鈕以后可以對二進制文件、文本文件、圖片文件、視頻文件進行分割,如果按照上述代碼進行編寫,其依賴具體類,如果你想增加其他文件格式方法時,得修改button里的方法,這會使得封裝性被破壞

1.2.2 重構代碼

//抽象類
class ISplitter{public:virtual void split()=0;virtual ~ISplitter(){}};//工廠基類
class SplitterFactory{
public:virtual ISplitter* CreateSplitter()=0;virtual ~SplitterFactory(){}
};//具體類
class BinarySplitter : public ISplitter{virtual void split(){}};class TxtSplitter: public ISplitter{virtual void split(){}
};class PictureSplitter: public ISplitter{virtual void split(){}
};class VideoSplitter: public ISplitter{virtual void split(){}
};//具體工廠
class BinarySplitterFactory: public SplitterFactory{
public:virtual ISplitter* CreateSplitter(){return new BinarySplitter();}
};class TxtSplitterFactory: public SplitterFactory{
public:virtual ISplitter* CreateSplitter(){return new TxtSplitter();}
};class PictureSplitterFactory: public SplitterFactory{
public:virtual ISplitter* CreateSplitter(){return new PictureSplitter();}
};class VideoSplitterFactory: public SplitterFactory{
public:virtual ISplitter* CreateSplitter(){return new VideoSplitter();}
};class MainForm : public Form
{SplitterFactory*  factory;//工廠public:MainForm(SplitterFactory*  factory){this->factory=factory;}void Button1_Click(){ISplitter * splitter=factory->CreateSplitter(); //多態newsplitter->split();}
};

解決方法:設計一個工廠基類,聲明一個創建對象的接口,對象基礎工廠基類,實現創建對象的方法,即可實現運行時綁定

1.3 模式類圖

在這里插入圖片描述

1.4 要點總結

  • Factory Method模式用于隔離類對象的使用者和具體類型之間的耦合關系。面對一個經常變化的具體類型,緊耦合關系(new)會導致軟件的脆弱。
  • Factory Method模式通過面向對象的手法,將所要創建的具體對象工作延遲到子類,從而實現一種擴展(而非更改)的策略,較好地解決了這種緊耦合關系。
  • Factory Method模式解決“單個對象”的需求變化。缺點在于要求創建方法/參數相同。

2. Abstract Factory

2.1 模式介紹

動機:在軟件系統中,經常面臨著“一系列相互依賴的對象”的創建工作;同時,由于需求的變化,往往存在更多系列對象的創建工作。

如何應對這種變化?如何繞過常規的對象創建方法(new),提供一種“封裝機制”來避免客戶程序和這種“多系列具體對象創建工作”的緊耦合?

提供一個接口,讓該接口負責創建一系列“相關或者相互依賴的對象”,無需指定它們具體的類。

——《設計模式》GoF

2.2 模式代碼

抽象工廠是工廠方法模式的子集,當要創建的對象之間有關聯時才使用抽象工廠

2.2.1 問題代碼

假設現在要實現SQL處理的功能

問題代碼1:直接將類型硬編碼到功能里,如果有多種數據庫,比如MySQL、Oracle等等,就會不利于擴展

class EmployeeDAO{public:vector<EmployeeDO> GetEmployees(){SqlConnection* connection =new SqlConnection();connection->ConnectionString = "...";SqlCommand* command =new SqlCommand();command->CommandText="...";command->SetConnection(connection);SqlDataReader* reader = command->ExecuteReader();while (reader->Read()){}}
};

問題代碼2:在問題代碼1的基礎上,使用工廠方法解決依賴具體類問題


//數據庫訪問有關的基類
class IDBConnection{};
class IDBConnectionFactory{
public:virtual IDBConnection* CreateDBConnection()=0;
};class IDBCommand{};
class IDBCommandFactory{
public:virtual IDBCommand* CreateDBCommand()=0;
};class IDataReader{};
class IDataReaderFactory{
public:virtual IDataReader* CreateDataReader()=0;
};//支持SQL Server
class SqlConnection: public IDBConnection{};
class SqlConnectionFactory:public IDBConnectionFactory{};class SqlCommand: public IDBCommand{};
class SqlCommandFactory:public IDBCommandFactory{};class SqlDataReader: public IDataReader{};
class SqlDataReaderFactory:public IDataReaderFactory{};//支持Oracle
class OracleConnection: public IDBConnection{};class OracleCommand: public IDBCommand{};class OracleDataReader: public IDataReader{};class EmployeeDAO{IDBConnectionFactory* dbConnectionFactory;IDBCommandFactory* dbCommandFactory;IDataReaderFactory* dataReaderFactory;public:vector<EmployeeDO> GetEmployees(){IDBConnection* connection =dbConnectionFactory->CreateDBConnection();connection->ConnectionString("...");IDBCommand* command =dbCommandFactory->CreateDBCommand();command->CommandText("...");command->SetConnection(connection); //關聯性IDBDataReader* reader = command->ExecuteReader(); //關聯性while (reader->Read()){}}
};

如果傳遞的dbConnectionFactory、dbCommandFactory、dataReaderFactory不是同一系列的就會出問題,例如一部分是MySQL對象,一部分是Oracle對象

2.2.2 重構代碼


//數據庫訪問有關的基類
class IDBConnection{};class IDBCommand{};class IDataReader{};class IDBFactory{
public:virtual IDBConnection* CreateDBConnection()=0;virtual IDBCommand* CreateDBCommand()=0;virtual IDataReader* CreateDataReader()=0;};//支持SQL Server
class SqlConnection: public IDBConnection{};
class SqlCommand: public IDBCommand{};
class SqlDataReader: public IDataReader{};class SqlDBFactory:public IDBFactory{
public:virtual IDBConnection* CreateDBConnection()=0;virtual IDBCommand* CreateDBCommand()=0;virtual IDataReader* CreateDataReader()=0;};//支持Oracle
class OracleConnection: public IDBConnection{};class OracleCommand: public IDBCommand{};class OracleDataReader: public IDataReader{};class EmployeeDAO{IDBFactory* dbFactory;public:vector<EmployeeDO> GetEmployees(){IDBConnection* connection =dbFactory->CreateDBConnection();connection->ConnectionString("...");IDBCommand* command =dbFactory->CreateDBCommand();command->CommandText("...");command->SetConnection(connection); //關聯性IDBDataReader* reader = command->ExecuteReader(); //關聯性while (reader->Read()){}}
};

將一系列方法封裝在一起,這便是抽象工廠模式

2.3 模式類圖

在這里插入圖片描述

2.4 要點總結

  • 如果沒有應對“多系列對象構建”的需求變化,則沒有必要使用Abstract Factory模式,這時候使用簡單的工廠完全可以。
  • “系列對象”指的是在某一特定系列下的對象之間有相互依賴、或作用的關系。不同系列的對象之間不能相互依賴。
  • Abstract Factory模式主要在于應對“新系列”的需求變動。其缺點在于難以應對“新對象”的需求變動。

3. Prototype

3.1 模式介紹

原型是一種創建型設計模式,它允許您復制現有對象,而不使您的代碼依賴于它們的類。

問題:
假設你有一個對象,你想創建它的一個精確副本。你會怎么做?首先,你必須創建一個相同類的新對象。然后你必須遍歷原始對象的所有字段并將其值復制到新對象。

很好!但是有一個問題。并非所有對象都可以通過這種方式復制,因為某些對象的字段可能是私有的,從對象本身外部不可見。
在這里插入圖片描述

直接方法還有一個問題。由于您必須知道對象的類才能創建副本,因此您的代碼將依賴于該類。如果額外的依賴關系不嚇到您,那么還有另一個問題。有時您只知道對象遵循的接口,但不知道其具體類,例如,當方法中的參數接受遵循某個接口的任何對象時。

3.2 模式代碼


//抽象類
class ISplitter{
public:virtual void split()=0;virtual ISplitter* clone()=0; //通過克隆自己來創建對象virtual ~ISplitter(){}};//具體類
class BinarySplitter : public ISplitter{
public:virtual ISplitter* clone(){return new BinarySplitter(*this);}virtual void split(){}
};class TxtSplitter: public ISplitter{
public:virtual ISplitter* clone(){return new TxtSplitter(*this);}virtual void split(){}
};class PictureSplitter: public ISplitter{
public:virtual ISplitter* clone(){return new PictureSplitter(*this);}virtual void split(){}
};class VideoSplitter: public ISplitter{
public:virtual ISplitter* clone(){return new VideoSplitter(*this);}virtual void split(){}
};class MainForm : public Form
{ISplitter*  prototype;//原型對象public:MainForm(ISplitter*  prototype){this->prototype=prototype;}void Button1_Click(){ISplitter * splitter=prototype->clone(); //克隆原型splitter->split();}
};

3.3 模式類圖

在這里插入圖片描述

3.4 要點總結

  • 您可以克隆對象而不與其具體類耦合。
  • 您可以擺脫重復的初始化代碼,轉而克隆預先構建的原型。
  • 您可以更加方便地制作復雜的物體。
  • 處理復雜對象的配置預設時,您可以獲得繼承的替代方法。

4. Builder

4.1 模式介紹

動機:在軟件系統中,有時候面臨著“一個復雜對象”的創建工作,其通常由各個部分的子對象用一定的算法構成;由于需求的變化,這個復雜對象的各個部分經常面臨著劇烈的變化,但是將它們組合在一起的算法卻相對穩定。

如何應對這種變化?如何提供一種“封裝機制”來隔離出“復雜對象的各個部分”的變化,從而保持系統中的“穩定構建算法”不隨著需求改變而改變?

將一個復雜對象的構建與其表示相分離,使得同樣的構建過程(穩定)可以創建不同的表示(變化)。

——《設計模式》GoF

4.2 模式代碼

class House{//....
};class HouseBuilder {
public:House* GetResult(){return pHouse;}virtual ~HouseBuilder(){}
protected:House* pHouse;virtual void BuildPart1()=0;virtual void BuildPart2()=0;virtual void BuildPart3()=0;virtual void BuildPart4()=0;virtual void BuildPart5()=0;};class StoneHouse: public House{};class StoneHouseBuilder: public HouseBuilder{
protected:virtual void BuildPart1(){//pHouse->Part1 = ...;}virtual void BuildPart2(){}virtual void BuildPart3(){}virtual void BuildPart4(){}virtual void BuildPart5(){}};class HouseDirector{public:HouseBuilder* pHouseBuilder;HouseDirector(HouseBuilder* pHouseBuilder){this->pHouseBuilder=pHouseBuilder;}House* Construct(){pHouseBuilder->BuildPart1();for (int i = 0; i < 4; i++){pHouseBuilder->BuildPart2();}bool flag=pHouseBuilder->BuildPart3();if(flag){pHouseBuilder->BuildPart4();}pHouseBuilder->BuildPart5();return pHouseBuilder->GetResult();}
};

構建器HouseBuilder負責定義構建House時每個步驟的具體接口(變化)和管理正在構建的對象,由子類繼承并實現接口;
HouseDirector負責實現House在構建時的整體流程(不變)

4.3 模式類圖

在這里插入圖片描述

4.4 要點總結

  • Builder 模式主要用于“分步驟構建一個復雜的對象”。在這其中“分步驟”是一個穩定的算法,而復雜對象的各個部分則經常變化。
  • 變化點在哪里,封裝哪里—— Builder模式主要在于應對“復雜對象各個部分”的頻繁需求變動。其缺點在于難以應對“分步驟構建算法”的需求變動。
  • 在Builder模式中,要注意不同語言中構造器內調用虛函數的差別(C++ vs. C#) 。

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

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

相關文章

SQLAlchemy系列教程:批量插入數據

高效地批量插入數據對于應用程序的性能至關重要。SQLAlchemy為批處理操作提供了幾種機制&#xff0c;可以最大限度地減少開銷并加快數據庫事務時間。在本指南中&#xff0c;我們將探討如何使用SQLAlchemy執行批量插入&#xff0c;包括從基礎技術到高級技術。 搭建環境 在開始之…

V2X驗證

1. 標準和規范驗證 歐洲對 DSRC 和 V2X 系統有一系列的標準和規范,主要由 ETSI (European Telecommunications Standards Institute) 和 IEEE 等組織制定。驗證通常包括以下標準和規范: ETSI EN 302 571:這是DSRC在歐洲的主要標準,規定了DSRC系統的技術要求和操作條件。ET…

openEuler系統遷移 Docker 數據目錄到 /home,解決Docker 臨時文件占用大問題

根據錯誤信息 write /var/lib/docker/tmp/...: no space left on device&#xff0c;問題的根源是 根分區&#xff08;/&#xff09;的磁盤空間不足&#xff0c;而非 /home 分區的問題。以下是詳細解釋和解決方案&#xff1a; 問題原因分析 Docker 臨時文件占用根分區空間&…

Matlab 四分之一車輛被動懸架和模糊pid控制對比

1、內容簡介 Matlab 183-四分之一車輛被動懸架和模糊pid控制對比 可以交流、咨詢、答疑 2、內容說明 略 3.1 車輛多自由度模型建立 對于車輛動力學&#xff0c;一般都是研究其懸架系統&#xff0c;懸架系統由輪胎&#xff0c;輪胎空氣&#xff0c;彈簧&#xff0c;減震器和…

LabVIEW旋轉設備狀態在線監測系統

為了提高大型旋轉設備如電機和水泵的監控效率和故障診斷能力&#xff0c;用LabVIEW軟件開發了一套實時監測與故障診斷系統。該系統集成了趨勢分析、振動數據處理等多項功能&#xff0c;可實時分析電機電流、壓力、溫度及振動數據&#xff0c;以早期識別和預報故障。 ? 項目背…

微前端 無界wujie

開發環境配置: Node.js 版本 < 18.0.0 pnpm 腳手架示例模版基于 pnpm turborepo 管理項目 如果您的當前環境中需要切換 node.js 版本, 可以使用 nvm or fnm 進行安裝. 以下是通過 nvm 或者nvs 安裝 Node.js 16 LTS 版本 nvs安裝教程 https://blog.csdn.net/glorydx/artic…

跟網型逆變器小干擾穩定性分析與控制策略優化simulink仿真模型和代碼(包含完整仿真報告)

關注&#xff1a;“電擊小子程高興的MATLAB小屋”獲取巨額優惠 1.模型簡介 本仿真模型基于MATLAB/Simulink&#xff08;版本MATLAB 2016Rb&#xff09;軟件。建議采用matlab2016 Rb及以上版本打開。&#xff08;若需要其他版本可聯系代為轉換&#xff09; 近年來&#xff0c…

基于SpringBoot的“城市公交查詢系統”的設計與實現(源碼+數據庫+文檔+PPT)

基于SpringBoot的“城市公交查詢系統”的設計與實現&#xff08;源碼數據庫文檔PPT) 開發語言&#xff1a;Java 數據庫&#xff1a;MySQL 技術&#xff1a;SpringBoot 工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 系統展示 系統總體結構圖 系統首頁界面 用戶登錄界面 公…

框架源碼私享筆記(02)Mybatis核心框架原理 | 一條SQL透析核心組件功能特性

最近在思考一個問題&#xff1a;如何能夠更好的分享主流框架源碼學習筆記&#xff08;主要是源碼部分&#xff09;?讓有緣刷到的同學既可以有所收獲&#xff0c;還能保持對相關技術架構探討學習熱情和興趣。以及自己也保持較高的分享熱情和動力。 今天嘗試用一個SQL查詢作為引…

UNI-APP uts插件 支持ANDROID 監聽手機狀態

插件地址 https://ext.dcloud.net.cn/plugin?id22646 模塊 import {startPhoneListener,stopPhoneListener,checkIsAutoRecord,toCallAutoRecorderPage,navigateToCallRecordingSettings,jumpToPermissionPage,makePhoneCall,allRecorderFilesAction,registerSmsReceiver,} f…

windows協議不再續簽,華為再無windows可用,將于四月發布鴻蒙PC

大家好&#xff0c;我是國貨系創始人張云澤&#xff0c;最近不少小伙伴在后臺問&#xff1a;“聽說Windows協議要到期了&#xff1f;我的電腦會不會變磚&#xff1f;”還有人說&#xff1a;“華為筆記本以后用不了Windows了&#xff1f;鴻蒙系統能用嗎&#xff1f;”今天咱們就…

Stable Diffusion API /sdapi/v1/txt2img的完整參數列表及其說明

基本參數 {"prompt": "高質量&#xff0c;精細的恐龍", // 主提示詞"negative_prompt": "模糊&#xff0c;低質量", // 負面提示詞"styles": ["photorealistic", "detailed"], // 應用的風格預設&q…

TK矩陣:提高多賬號管理效率的利器

隨著TikTok的火爆&#xff0c;越來越多的人開始利用這個平臺進行內容創作和社交互動。無論是個人創作者、品牌方&#xff0c;還是營銷公司&#xff0c;TikTok都提供了巨大的機會&#xff0c;但同時也帶來了運營上的挑戰&#xff0c;尤其是在管理多個賬戶時。每個賬號的維護、內…

關于Redis的集群(上)

目錄 基本概念 數據分片算法 哈希求余 ?編輯一致性哈希算法 哈希槽分區算法 搭建集群環境 創建目錄和配置 編寫 docker-compose.yml 啟動容器 構建集群 基本概念 廣義的集群&#xff0c;只要是多個機器構成了分布式系統&#xff0c;都可以成為是一個“集群”。 但…

【CSS3】化神篇

目錄 平面轉換平移旋轉改變旋轉原點多重轉換縮放傾斜 漸變線性漸變徑向漸變 空間轉換平移視距旋轉立體呈現縮放 動畫使現步驟animation 復合屬性animation 屬性拆分逐幀動畫多組動畫 平面轉換 作用&#xff1a;為元素添加動態效果&#xff0c;一般與過渡配合使用 概念&#x…

Java 線程創建全解析:五種方式詳細對比與實戰示例

目錄 Java 線程創建全解析&#xff1a;五種方式詳細對比與實戰示例一、引言二、創建線程的五種方式1. 繼承 Thread 類2. 實現 Runnable 接口3. 匿名內部類實現 Thread4. 匿名內部類實現 Runnable5. 使用 Lambda 表達式&#xff08;Java 8及以上&#xff09; 三、線程創建方式對…

Dify平臺離線鏡像部署

Dify 是一款開源的大語言模型(LLM) 應用開發平臺。它融合了后端即服務&#xff08;Backend as Service&#xff09;和 LLMOps 的理念&#xff0c;使開發者可以快速搭建生產級的生成式 AI 應用。即使你是非技術人員&#xff0c;也能參與到 AI 應用的定義和數據運營過程中。 前提…

Unitest和pytest區別

1?? 設計理念 ?? unittest &#xff1a; 是 Python 標準庫的一部分&#xff0c;設計靈感來自 Java 的 JUnit&#xff0c;強調面向對象的測試模式。 測試用例需要繼承 unittest.TestCase 類&#xff0c;測試方法以 test_ 開頭。 更適合傳統、結構化的測試開發方式。 ? pyte…

GIT標簽(Tag)操作

在Git中&#xff0c;標簽&#xff08;Tag&#xff09;用于標記特定的提交點&#xff0c;通常用于發布版本。 切換到需要打標簽的分支&#xff1a; git checkout <branch-name>創建標簽 git tag v1.0.0 git tag -a v1.0.0 -m "Release version 1.0.0"查看所…

玩轉github

me github 可以給倉庫添加開發人員嗎 4o 是的&#xff0c;GitHub允許倉庫管理員為倉庫添加開發人員&#xff0c;并設置這些開發人員的角色和權限。這里是一個簡單的步驟指導&#xff0c;教你如何給一個 GitHub 倉庫添加開發人員&#xff1a; 前提條件 你必須有這個倉庫的權限&…