面向?qū)ο笤O(shè)計(jì),為什么?
回答:變化是復(fù)用的天敵!面向?qū)ο笤O(shè)計(jì)最大的優(yōu)勢(shì)在于:抵御變化
重新認(rèn)識(shí)面向?qū)ο?/h2>
?理解隔離變化
? 從宏觀層面來看,面向?qū)ο蟮臉?gòu)建方式更能適應(yīng)軟件的變化,能將變化所帶來的影響減為最小
?各司其職
? ? 從微觀層面來看,面向?qū)ο蟮姆绞礁鼜?qiáng)調(diào)各個(gè)類的“責(zé)任”
? ? 由于需求變化導(dǎo)致的新增類型不應(yīng)該影響原來類型的實(shí)現(xiàn)——是所謂各負(fù)其責(zé)
?對(duì)象是什么?
? ? 從語言實(shí)現(xiàn)層面來看,對(duì)象封裝了代碼和數(shù)據(jù)。
? ? 從規(guī)格層面講,對(duì)象是一系列可被使用的公共接口。
? ? 從概念層面講,對(duì)象是某種擁有責(zé)任的抽象。
GOF-23 模式分類
?從目的來看:
? ? 創(chuàng)建型(Creational)模式:將對(duì)象的部分創(chuàng)建工作延遲到子類或者其他對(duì)象,從而應(yīng)對(duì)需求變化為對(duì)象創(chuàng)建時(shí)具體類型實(shí)現(xiàn)引來的沖擊。
? ? 結(jié)構(gòu)型(Structural)模式:通過類繼承或者對(duì)象組合獲得更靈活的結(jié)構(gòu),從而應(yīng)對(duì)需求變化為對(duì)象的結(jié)構(gòu)帶來的沖擊。
? ? 行為型(Behavioral)模式:通過類繼承或者對(duì)象組合來劃分類與對(duì)象間的職責(zé),從而應(yīng)對(duì)需求變化為多個(gè)交互的對(duì)象帶來的沖擊。
?從范圍來看:
? ? 類模式處理類與子類的靜態(tài)關(guān)系。
? ? 對(duì)象模式處理對(duì)象間的動(dòng)態(tài)關(guān)系。
從封裝變化角度對(duì)模式分類
重構(gòu)獲得模式 Refactoring to Patterns
? 面向?qū)ο笤O(shè)計(jì)模式是“好的面向?qū)ο笤O(shè)計(jì)”,所謂“好的面向?qū)ο笤O(shè)計(jì)”指是那些可以滿足 “應(yīng)對(duì)變化,提高復(fù)用”的設(shè)計(jì) 。
? 現(xiàn)代軟件設(shè)計(jì)的特征是**“需求的頻繁變化”。設(shè)計(jì)模式的要點(diǎn)是“尋找變化點(diǎn),然后在變化點(diǎn)處應(yīng)用設(shè)計(jì)模式,從而來更好地應(yīng)對(duì)需求的變化”.“什么時(shí)候、什么地點(diǎn)應(yīng)用設(shè)計(jì)模式”**比“理解設(shè)計(jì)模式結(jié)構(gòu)本身”更為重要。
? 設(shè)計(jì)模式的應(yīng)用不宜先入為主,一上來就使用設(shè)計(jì)模式是對(duì)設(shè)計(jì)模式的最大誤用。沒有一步到位的設(shè)計(jì)模式。**敏捷軟件開發(fā)實(shí)踐提倡的“Refactoring to Patterns”**是目前普遍公認(rèn)的最好的使用設(shè)計(jì)模式的方法。
重構(gòu)關(guān)鍵技法
? ? 靜態(tài) -> ? 動(dòng)態(tài)
? ?早綁定 -> ? 晚綁定
? ? 繼承 -> ? 組合
? ? 編譯時(shí)依賴 -> ? 運(yùn)行時(shí)依賴
? ? 緊耦合 -> ? 松耦合
模板方法模式(c++)
一、組件協(xié)作型模式
“組件協(xié)作”模式 :現(xiàn)代軟件專業(yè)分工之后的第一個(gè)結(jié)果是“框架與應(yīng)用程序的劃分”,“組件協(xié)作”模式通過晚期綁定,來實(shí)現(xiàn)框架與應(yīng)用程序之間的松耦合,是二者之間協(xié)作時(shí)常用的模式。
早綁定——————>晚綁定
早綁定:在應(yīng)用程序中調(diào)用庫
晚綁定:在庫中調(diào)用應(yīng)用程序,模板方法模式就是把主流程放在庫里,庫的使用者只需要實(shí)現(xiàn)子步驟就可以了
典型模式
? ? Template Method
? ? Observer / Event
? ? Strategy
【1】TemplateMethod
**動(dòng)機(jī)(Motivation)**??:
- 在軟件構(gòu)建過程中,對(duì)于某一項(xiàng)任務(wù),它常常有穩(wěn)定的整體操作結(jié)構(gòu),但各個(gè)子步驟卻有很多改變的需求,或者由于固有的原因 (比如框架與應(yīng)用之間的關(guān)系)子步驟無法和任務(wù)的整體結(jié)構(gòu)同時(shí)實(shí)現(xiàn)。
**問題思考(Consider)**??:
- 如何在確定穩(wěn)定操作結(jié)構(gòu)的前提下,來靈活應(yīng)對(duì)各個(gè)子步驟的變化或者晚期實(shí)現(xiàn)需求?
對(duì)某一項(xiàng)任務(wù),用結(jié)構(gòu)化軟件設(shè)計(jì)流程(使用了早綁定)和面向?qū)ο笤O(shè)計(jì)軟件流程(使用了晚綁定)的區(qū)別:
(1)結(jié)構(gòu)化軟件設(shè)計(jì)流程
C++執(zhí)行代碼:
template1_lib.cpp
#include<iostream>
using namespace std;
//程序庫開發(fā)人員
class Library {
public:
void Step1() {
cout << "Step1" << endl;
}
void Step3() {
cout << "Step3" << endl;
}
void Step5() {
cout << "Step5" << endl;
}
};
template1_app.cpp
#include "template1_lib.cpp"
//應(yīng)用程序開發(fā)人員
class Application {
public:
bool Step2() {
cout << "myStep2" << endl;
return true;
}
void Step4() {
cout << "myStep4" << endl;
}
};
int main() {
Library lib;
Application app;
lib.Step1();
if (app.Step2()) {
lib.Step3();
}
for (int i = 0; i < 4; i++) {
app.Step4();
}
lib.Step5();
}
(2)面向?qū)ο筌浖O(shè)計(jì)流程
C++執(zhí)行代碼:
template2_lib.cpp
#include<iostream>
using namespace std;
//程序庫開發(fā)人員
class Library {
public:
//穩(wěn)定 template method
void Run() {
Step1();
if (Step2()) {
//支持變化 ==> 虛函數(shù)的多態(tài)調(diào)用
Step3();
}
for (int i = 0; i < 4;i++) {
Step4();//支持變化 ==> 虛函數(shù)的多態(tài)調(diào)用
}
Step5();
}
virtual ~Library() {};
protected:
void Step1() {
//穩(wěn)定
cout << "Step1" << endl;
}
void Step3() {
//穩(wěn)定
cout << "Step3" << endl;
}
void Step5() {
//穩(wěn)定
cout << "Step5" << endl;
}
virtual bool Step2() = 0;//變化
virtual void Step4() = 0;//變化
};
template2_app.cpp
#include "template2_lib.cpp"
#include <iostream>
using namespace std;
//應(yīng)用程序開發(fā)人員
class Application : public Library {
protected:
virtual bool Step2() {
//... 子類重寫實(shí)現(xiàn)
cout << "override Step2" << endl;
return true;
}
virtual void Step4() {
//... 子類重寫實(shí)現(xiàn)
cout << "override Step4" << endl;
}
};
int main() {
Library* pLib = new Application();
pLib->Run();
delete pLib;
return 0;
}
模式定義
定義一個(gè)操作中的算法的骨架 (穩(wěn)定),而將一些步驟延遲(變化)到子類中。Template Method使得子類可以不改變(復(fù)用)一個(gè)算法的結(jié)構(gòu)即可重定義(override 重寫)該算法的某些特定步驟。
——《設(shè)計(jì)模式》GoF
結(jié)構(gòu)(Structure)
要點(diǎn)總結(jié)
? Template Method模式是一種非?;A(chǔ)性的設(shè)計(jì)模式,在面向?qū)ο笙到y(tǒng)中有著大量的應(yīng)用。它用最簡(jiǎn)潔的機(jī)制**(虛函數(shù)的多態(tài)性)為很多應(yīng)用程序框架提供了靈活的擴(kuò)展點(diǎn),是代碼復(fù)用方面的基本實(shí)現(xiàn)結(jié)構(gòu)**。
? 除了可以靈活應(yīng)對(duì)子步驟的變化外,“不要調(diào)用我,讓我來調(diào)用你”的反向控制結(jié)構(gòu)是Template Method的典型應(yīng)用。
? 在具體實(shí)現(xiàn)方面,被Template Method調(diào)用的虛方法可以具有實(shí)現(xiàn),也可以沒有任何實(shí)現(xiàn)(抽象方法、純虛方法),但一般推薦將它們設(shè)置為protected方法。
【2】Strategy 策略模式
動(dòng)機(jī)(Motivation)??:
- 在軟件構(gòu)建過程中,某些對(duì)象使用的算法可能多種多樣,經(jīng)常改變,如果將這些算法都編碼到對(duì)象中,將會(huì)使對(duì)象變得異常復(fù)雜;而且有時(shí)候支持不使用的算法也是一個(gè)性能負(fù)擔(dān)。
**問題思考(Consider)**??:
- 如何在運(yùn)行時(shí)根據(jù)需要透明地更改對(duì)象的算法?將算法與對(duì)象本身解耦,從而避免上述問題?
C++執(zhí)行代碼:
strategy1.cpp(無使用Strategy策略模式)
若想新增法國(guó)稅法,如何實(shí)現(xiàn)?
#include<iostream>
using namespace std;
enum TaxBase {
CN_Tax,
US_Tax,
DE_Tax,
FR_Tax //更改(增加法國(guó)稅法計(jì)算方式)
};
class SalesOrder {
TaxBase tax;
double sum = 0;
public:
double CalculateTax(TaxBase tax) {
//...
if (tax == CN_Tax) {
//CN***********
cout << "中國(guó)稅法計(jì)算" << endl;
sum += 1;
}
else if (tax == US_Tax) {
//US***********
cout << "美國(guó)稅法計(jì)算" << endl;
sum += 2;
}
else if (tax == DE_Tax) {
//DE***********
cout << "德國(guó)稅法計(jì)算" << endl;
sum += 3;
}
else if (tax == FR_Tax) { //更改(增加法國(guó)稅法計(jì)算方式)
cout << "法國(guó)稅法計(jì)算" << endl;
sum += 4;
}
//....
return sum;
}
};
int main() {
TaxBase tax;
tax = FR_Tax;
SalesOrder *so = new SalesOrder();
cout << so->CalculateTax(tax) << endl;
return 0;
}
缺點(diǎn):第一種方法需要去改枚舉類型,需要往方法內(nèi)部補(bǔ)充一些代碼,打破了開放封閉原則,違背了復(fù)用性(二進(jìn)制層面的復(fù)用性)。
strategy2.cpp(使用Strategy策略模式)
// 這兩個(gè)類的具體實(shí)現(xiàn)不重要
class Context {
};
class StrategyFactory {
public:
TaxStrategy* NewStrategy() {
return nullptr; // ...
}
};
//稅率策略模式類
class TaxStrategy {
public:
virtual double Calculate(const Context& context) = 0;
virtual ~TaxStrategy() {}
};
//CNTax 中國(guó)稅法
class CNTax : public TaxStrategy {
public:
virtual double Calculate(const Context& context) {
//***********
}
};
//USTax 美國(guó)稅法
class USTax : public TaxStrategy {
public:
virtual double Calculate(const Context& context) {
//***********
}
};
//DETax 德國(guó)稅法
class DETax : public TaxStrategy {
public:
virtual double Calculate(const Context& context) {
//***********
}
};
//擴(kuò)展
//*********************************
//FRTax 法國(guó)稅法
class FRTax : public TaxStrategy {
public:
virtual double Calculate(const Context& context) {
//.........
}
};
//不需要變化的
class SalesOrder {
private:
TaxStrategy* strategy;
public:
// 工廠模式
SalesOrder(StrategyFactory* strategyFactory) {
this->strategy = strategyFactory->NewStrategy();
}
~SalesOrder() {
delete this->strategy;
}
double CalculateTax() {
//...
Context context;
double val = strategy->Calculate(context); //多態(tài)調(diào)用
//...
}
};
第二種寫法遵循了開放封閉原則
模式定義
定義一系列算法,把它們一個(gè)個(gè)封裝起來,并且使它們可互相替換(變化)。該模式使得算法可獨(dú)立于使用它的客戶程序(穩(wěn)定)而變化(擴(kuò)展,子類化)。
——《設(shè)計(jì)模式》GoF
結(jié)構(gòu)(Structure)
要點(diǎn)總結(jié)
? Strategy及其子類為組件提供了一系列可重用的算法,從而可以使得類型在運(yùn)行時(shí)方便地根據(jù)需要在各個(gè)算法之間進(jìn)行切換。
? Strategy模式提供了用條件判斷語句以外的另一種選擇,消除條件判斷語句,就是在解耦合。含有許多條件判斷語句的代碼通常都需要Strategy模式。
? 如果Strategy對(duì)象沒有實(shí)例變量,那么各個(gè)上下文可以共享同一個(gè)Strategy對(duì)象,從而節(jié)省對(duì)象開銷。
【3】Observer 觀察者模式
動(dòng)機(jī)(Motivation)??:
? 在軟件構(gòu)建過程中,我們需要為某些對(duì)象建立一種**“通知依賴關(guān)系”** ——一個(gè)對(duì)象(目標(biāo)對(duì)象)的狀態(tài)發(fā)生改變,所有的依賴對(duì)
象(觀察者對(duì)象)都將得到通知。如果這樣的依賴關(guān)系過于緊密,將使軟件不能很好地抵御變化。
? 使用面向?qū)ο蠹夹g(shù),可以將這種依賴關(guān)系弱化,并形成一種穩(wěn)定的依賴關(guān)系。從而實(shí)現(xiàn)軟件體系結(jié)構(gòu)的松耦合。
**問題思考(Consider)**??:
C++執(zhí)行代碼:
FileSplitter1.cpp
#include <string>
#include <iostream>
using std::string;
class ProgressBar{
public:
void setValue(float value){
// ...
}
};
class FileSplitter
{
string m_filePath;
int m_fileNumber;
ProgressBar* m_progressBar;//具體通知機(jī)制
public:
FileSplitter(const string& filePath, int fileNumber, ProgressBar* progressBar) :
m_filePath(filePath),
m_fileNumber(fileNumber),
m_progressBar(progressBar){
}
void split(){
//1.讀取大文件
//2.分批次向小文件中寫入
for (int i = 0; i < m_fileNumber; i++){
//...
float progressValue = m_fileNumber;
progressValue = (i + 1) / progressValue;
m_progressBar->setValue(progressValue);
}
}
};
MainForm1.cpp
#include <string>
#include <iostream>
using std::string;
// 以下幾個(gè)類的具體實(shí)現(xiàn)不重要
class Form{
};
class TextBox{
public:
string getText(){
// ...
return "";
}
};
class ProgressBar;
class FileSplitter{
public:
FileSplitter(string filePath, int number, ProgressBar* progressBar){
// ...
}
void split(){
// ...
}
};
//
class MainForm : public Form
{
TextBox* txtFilePath;
TextBox* txtFileNumber;
ProgressBar* progressBar;
public:
void Button1_Click(){
string filePath = txtFilePath->getText();
int number = atoi(txtFileNumber->getText().c_str());
FileSplitter splitter(filePath, number, progressBar);
splitter.split();
}
};
FileSplitter1.cpp
class IProgress{
public:
virtual void DoProgress(float value)=0;
virtual ~IProgress(){}
};
class FileSplitter
{
string m_filePath;
int m_fileNumber;
//ProgressBar* m_progressBar; //具體通知機(jī)制
//IProgress* m_iprogressList;//抽象通知機(jī)制,支持1個(gè)觀察者
List<IProgress*> m_iprogressList; // 抽象通知機(jī)制,支持多個(gè)觀察者
public:
FileSplitter(const string& filePath, int fileNumber) :
m_filePath(filePath),
m_fileNumber(fileNumber){
}
void split(){
//1.讀取大文件
//2.分批次向小文件中寫入
for (int i = 0; i < m_fileNumber; i++){
//...
float progressValue = m_fileNumber;
progressValue = (i + 1) / progressValue;
onProgress(progressValue);//發(fā)送通知
}
}
void addIProgress(IProgress* iprogress){
m_iprogressList.push_back(iprogress);
}
void removeIProgress(IProgress* iprogress){
m_iprogressList.remove(iprogress);
}
protected:
virtual void onProgress(float value){
List<IProgress*>::iterator itor=m_iprogressList.begin();
while (itor != m_iprogressList.end() )
(*itor)->DoProgress(value); //更新進(jìn)度條
itor++;
}
}
};
MainForm2.cpp
class MainForm : public Form, public IProgress
{
TextBox* txtFilePath;
TextBox* txtFileNumber;
ProgressBar* progressBar;
public:
void Button1_Click(){
string filePath = txtFilePath->getText();
int number = atoi(txtFileNumber->getText().c_str());
ConsoleNotifier cn;
FileSplitter splitter(filePath, number);
splitter.addIProgress(this); //訂閱通知
splitter.addIProgress(&cn); //訂閱通知
splitter.split();
splitter.removeIProgress(this);
}
virtual void DoProgress(float value){
progressBar->setValue(value);
}
};
class ConsoleNotifier : public IProgress {
public:
virtual void DoProgress(float value){
cout << ".";
}
};
網(wǎng)上代碼(來子這個(gè)博主):https://github.com/chouxianyu/design-patterns-cpp
Observer.cpp:
#include <iostream>
#include <vector>
class Subject;
/// Observer 和 ConcreteObserver ///
//抽象觀察者
class Observer
{
public:
virtual ~Observer() {}
virtual int getState() = 0;
virtual void update(Subject* subject) = 0;
};
//具體觀察者
class ConcreteObserver : public Observer
{
public:
ConcreteObserver(const int state) :
observer_state(state) {} //osbserver_state = state
//析構(gòu)函數(shù)
~ConcreteObserver() {}
//獲取observer_state
int getState()
{
return observer_state;
}
void update(Subject* subject);
private:
int observer_state;
};
/// Subject 和 ConcreteObserver ///
class Subject
{
public:
virtual ~Subject() {}
//往observers添加observer
void attach(Observer* observer)
{
observers.push_back(observer);
}
//從observers刪除指定index的observer
void detach(const int index)
{
observers.erase(observers.begin() + index);
}
//遍歷observers,調(diào)用ConcreteObserver的update函數(shù)
void notify()
{
for (unsigned int i = 0; i < observers.size(); i++)
{
observers.at(i)->update(this);
}
}
virtual int getState() = 0;
virtual void setState(const int s) = 0;
private:
std::vector<Observer*> observers;// 抽象通知機(jī)制,支持多個(gè)觀察者
};
class ConcreteSubject : public Subject
{
public:
~ConcreteSubject() {}
int getState()
{
return subject_state;
}
void setState(const int s)
{
subject_state = s;
}
private:
int subject_state;
};
void ConcreteObserver::update(Subject* subject)
{
observer_state = subject->getState();
std::cout << "Observer state updated." << std::endl;
}
int main()
{
ConcreteObserver observer1( 1 );
ConcreteObserver observer2( 2 );
std::cout << "Observer 1 state: " << observer1.getState() << std::endl;//Observer 1 state: 1
std::cout << "Observer 2 state: " << observer2.getState() << std::endl;//Observer 2 state: 2
Subject *subject = new ConcreteSubject();
subject->attach( &observer1 ); //將observer1 放進(jìn)observers
subject->attach( &observer2 ); //將observer2 放進(jìn)observers
subject->setState( 10 );//設(shè)置subject的subject_state = 10
subject->notify();//遍歷observers,調(diào)用ConcreteObserver的update(Subject *subject)函數(shù),將subject的subject_state=10 給 observers里的每一個(gè)對(duì)象設(shè)置 observer_state = 10
std::cout << "Observer 1 state: " << observer1.getState() << std::endl;//Observer 1 state: 10
std::cout << "Observer 2 state: " << observer2.getState() << std::endl;//Observer 2 state: 10
delete subject;
return 0;
}
模式定義
定義對(duì)象間的一種一對(duì)多(變化)的依賴關(guān)系,以便當(dāng)一個(gè)對(duì)象(Subject)的狀態(tài)發(fā)生改變時(shí),所有依賴于它的對(duì)象都得到通知并自動(dòng)更新。
——《設(shè)計(jì)模式》GoF
結(jié)構(gòu)(Structure)
要點(diǎn)總結(jié)
? 使用面向?qū)ο蟮某橄?,Observer模式使得我們可以獨(dú)立地改變目標(biāo)與觀察者,從而使二者之間的依賴關(guān)系達(dá)致松耦合。
? 目標(biāo)發(fā)送通知時(shí),無需指定觀察者,通知(可以攜帶通知信息作為參數(shù))會(huì)自動(dòng)傳播。
? 觀察者自己決定是否需要訂閱通知,目標(biāo)對(duì)象對(duì)此一無所知。文章來源:http://www.zghlxwxcb.cn/news/detail-483361.html
? Observer模式是基于事件的UI框架中非常常用的設(shè)計(jì)模式,也是MVC模式的一個(gè)重要組成部分。文章來源地址http://www.zghlxwxcb.cn/news/detail-483361.html
到了這里,關(guān)于C++ 設(shè)計(jì)模式----組件協(xié)作型模式的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!