1. 程式人生 > >C++ 學習筆記 Template Method Pattern

C++ 學習筆記 Template Method Pattern

Tamplate Method Pattern

      模板方法模式:特點是把共有部分集中到基類中,不同部分分散到不同子類中去實現。C++ 對此還有一種特殊的設計 NVI 

       設計出發點:(1)提高程式碼複用性, 不同派生類的共同部分集中在基類中(2)基類通過對派生類的具體實現來拓展不同的功能。

       生活中的例子:(1)炒菜,炒不同的菜,會有相同的步驟(倒油,熱油,裝盤等),也會有不同的步驟(加不同的材料,佐料等),在這一連串操作中,把不同的部分分到不同的派生類中實現(2)登入不同系統的過程,有相同的步驟(聯網,開啟瀏覽器,關閉瀏覽器),也有不同的步驟(輸入不同網址,不同登入方式等),這一連串操作也可以運用模板方法。

例子:登入不同系統

基類 : loginSystem

     (1)將一連串操作的共同部分集中在一起,供派生類直接呼叫

     (2)為派生類提供介面,實現各自不同的部分

// 基類, 登入某個系統
class loginSystem {
public:
	virtual ~loginSystem() = default;
	// ... 特別處理 copy constructor, assignment oeprator, move constructor 等

	void loginIn() {
		connect();          // 連線校園網
		openBrowser();		// 開啟瀏覽器
		inputAdress();		// 輸入網址
		loginMethod();		// 登陸方式
		closeBrowser();		// 關閉瀏覽器
	}

	static void connect() {
		std::cout << "\n連線校園網\n";
	}
	static void openBrowser() {
		std::cout << "\n開啟瀏覽器\n";
	}

	virtual void inputAdress() = 0;   // 介面

	virtual void loginMethod() = 0;   // 介面

	static void closeBrowser() {
		std::cout << "\n關閉瀏覽器\n";
	}
} ;

派生類 1 : 登入支付寶

// 派生類, 登入支付寶
// 登入方式 : 手機驗證碼
class loginAlipay : public loginSystem {
private:
	std::string phoneNumber = "1326176****";
	std::string verifyCode  = "**0219";
public:

	void inputAdress() override {
		std::cout << "\n輸入 https://www.alipay.com\n";
	}

	void loginMethod() override {
		std::cout << "\n手機號 : ";
		std::cin >> phoneNumber;
		std::cout << "\n驗證碼 : ";
		std::cin >> verifyCode;
	}
} ;

派生類 2 : 登入 QQ 郵箱

// 派生類, 登入 QQ 郵箱
// 登陸方式 : 使用者名稱密碼登入
class loginMail : public loginSystem {
private:
	std::string userName = "Fluence";
	std::string passWord = "YHL";
public:

	void inputAdress() override {
		std::cout << "\n輸入 https://mail.qq.com\n";
	}

	void loginMethod() override {
		std::cout << "\n使用者名稱 : ";
		std::cin >> userName;
		std::cout << "\n密碼   : ";
		std::cin >> passWord;
	}
} ;

派生類 3:  登入銀行賬戶

// 派生類, 登入銀行
// 登陸方式: 指紋識別
class loginBank : public loginSystem {
private:
	std::vector<double> fingerPrint;
public:

	void inputAdress() override {
		std::cout << "\n輸入 http://www.****.com/cn/index.html\n";
	}

	void loginMethod() override {
		std::cout << "\n從本地採取指紋 : \n";
		std::ifstream statistics("myFingerPrin.txt");
		double details;
		while(statistics >> details)
			fingerPrint.emplace_back(details);
		statistics.close();   // 為了簡略, 未加入 RAII, 非異常安全
	}
} ;

測試程式碼:

#include <iostream>
#include <fstream>
#include <memory>
#include <string>
#include <vector>

// ..... 以上 class 

int main() {
	using ptrType = std::shared_ptr<loginSystem>;

	ptrType alier = std::make_shared<loginAlipay>();
	std::cout << "\n\n--------------- 登入支付寶 ----------------\n";
	alier->loginIn();

	ptrType qqmailer = std::make_shared<loginMail>();
	std::cout << "\n\n--------------- 登入 QQ 郵箱 ----------------\n";
	qqmailer->loginIn();

	ptrType banker = std::make_shared<loginBank>();
	std::cout << "\n\n--------------- 登入銀行 ----------------\n";13261
	banker->loginIn();
	return 0;
}

執行結果:

--------------- 登入支付寶 ----------------

連線校園網

開啟瀏覽器

輸入 https://www.alipay.com

手機號 : 13261764268

驗證碼 : 920219

關閉瀏覽器

--------------- 登入 QQ 郵箱 ----------------

連線校園網

開啟瀏覽器

輸入 https://mail.qq.com

使用者名稱 : Fluence

密碼   : YHL

關閉瀏覽器

--------------- 登入銀行 ----------------

連線校園網

開啟瀏覽器

輸入 http://www.****.com/cn/index.html

從本地採取指紋 :

關閉瀏覽器

學習《Effective C++ 》的過程中看到了 NVI (non-virtual interface) 的概念,開啟了我對模板方法的好奇。個人以為,NVI 對於理解模板方法大有裨益。