設計模式之策略模式

代碼設計原則

1.找出應用中(一個類具有的方法中)可能需要變化之處,把它們獨立出來,不要和那些不需要變化的代碼(功能)混在一起。

這句話很好理解,但是要怎麼個獨立法纔是關鍵,也是很疑惑的地方,那請繼續往下看吧。


2.針對接口編程,而不是針對實現編程。

假如現在你要寫一個類具有多個方法,即有多個功能。那麼怎麼來寫代碼或者說怎麼來定義這個類呢?

現在有兩種方式:

       1)  如果在基類本身中直接定義這些功能的虛方法,子類通過繼承來實現這些功能,這種方式就是針對實現編程;其實針對實現編程就是將某一個類的方法定義太死(即因爲某個類要實現一個功能而爲這個類定義一個方法),使其沒有拓展性,也就是沒有對其(該方法或功能)進行再抽象,可與針對接口編程對比理解。

           這個方式有什麼不好了,就是沒法拓展,設計模式裏的說法叫沒有彈性,因爲要對這個類增加一個方法即功能,得去基類中加,就會對所有從這個類繼承的子類造成影響,都得去改代碼,這個一看就望而生畏了,然後我們通常就會作出從子類增加一個方法的方式來達到這個目的,然後我們就周而復始的這麼幹活,然後我們就越來越覺得我們寫的代碼很垃圾,組織結構越來越亂,而下次想在其它工程中再用這些代碼或功能的時候感覺要搬過去很麻煩或是很難再用(如果你有這樣的困惑,去看設計模式吧,會有相見恨晚的感覺,多麼痛的領悟額額)。

           2)如果我們不直接在基類中直接定義這些方法(功能),而是將這些方法(第一個設計原則提到的)獨立成一個類(一個接  口)InterfaceFunction來單獨定義它,而在基類中增加一個這個方法接口定義的成員InterfaceFunction IFun,如果要增加對這個方法的實現只需新繼承這個接口再實現,並賦給基類的方法接口定義的成員IFun, 這種方式叫做針對接口編程;

           這個方式有什麼好處了,顯然我們可以在以後的編程中來動態實現這個方法接口,如果新增加方法或是同一個方法不同實現都    可以新繼承然後直接實現,而不會對其它繼承至這個基類的子類有任何影響,在代碼的重用性上面,在其它的工程中也可以直接運用這個方法接口實現,而不會有將基類也帶入其它工程的苦惱。這種方式也叫運行時動態綁定,即在程序運行的過程中可以動態改變這個方法的實現,即動態賦值的過程。

3.多用組合,少用繼承。

在上面針對接口編程中,將類的方法定義成抽象接口類,當我們在實現這個類的時候實際上是用兩個類的組合來完成這個類的定義及實現,而不是採


c代碼示例:(參考Head+First+設計模式)

//fly method interface
typedef struct _IFlyInterface{
	void (*fly)();
}IFlyInterface;

//quack method interface
typedef struct _IQuackInterface{
	void (*quack)();
}IQuackInterface;

//針對接口編程類定義
typedef struct _CDuck{
	IFlyInterface* pIFlyInterface;
	IQuackInterface* pIQuackInterface;
	void (*performFly)( CDuck* this );
	void (*siwm)( CDuck* this );
	void (*display)( CDuck* this );
}CDuck;

//針對實現編程類定義,也就是我們拿起來開始寫代碼乾的事
typedef struct CDuck{
	void (*fly)( CDuck* this );
	void (*quack)( CDuck* this );
	void (*siwm)( CDuck* this );
	void (*display)( CDuck* this );
};


/////////////////////////////////應用///////////////////////////////////////////
//該fly方法可以在不同的地方使用而與CDuck類無關(可重用性)
void fly1()
{
	printf( "i can fly1" );
}
void fly2()
{
    printf( "i can fly2" );
}
IFlyInterface* pIFlyInterface = (IFlyInterface*)malloc( sizeof(IFlyInterface) );
//可以設置不同的飛行方式,即fly方法的不同實現(fly1 or fly2 ...)(拓展性)
//pIFlyInterface->fly = fly1;
pIFlyInterface->fly = fly2;
//CDuck
void performFly( CDuck* this )
{
	this->pIFlyInterface->fly();
}
CDuck* pMyDuck = (CDuck*)malloc( sizeof(CDuck) );
//可以對CDuck的pIFlyInterface進行動態賦值,達到運行時動態綁定的效果
//而不是直接一個方法寫死,只能調一個方法
pMyDuck->pIFlyInterface = pIFlyInterface;
pMyDuck->performFly = performFly;

//invoke
pMyDuck->performFly( pMyDuck );


//如果我們採用第二種定義方式,即針對實現編程的話,就會碰到如下問題
//如果以後這個類要增加一個fly方法,我們可能就會去裏面定義一個fly2方法,如果這個
//類是基類的話,那麼所有的子類都會受到影響或者我們選擇在繼承的子類中重新定義一個
//fly方法,但是這樣顯得我們的代碼組織結構很亂,到後面越來越亂,而且如果這個fly方法
//後面在別的工程裏用的時候就會跟基類CDuck搭上關係,不方便移植。

上面的這個模型就叫做策略模式(Strategy Pattern),定義如下:

策略模式定義了算法族,分別封裝起來,讓它們之間可以互相替換,此模式讓算法的變化獨立於使用算法的客戶。

其實什麼模式不重要,主要是以後寫代碼的時候知道怎麼去組織就好,寫得不對的地方還請見諒,指證(個人看書後見解)。

發佈了23 篇原創文章 · 獲贊 2 · 訪問量 10萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章