接續昨天的問題,我們要來看如何更彈性的設計出這個架構!
那要怎麼應用呢?書本提到了第二個原則:
針對"介面"而非"實作"寫程式
為了要讓程式更靈活,我們把行為放在 "介面" 中,而不在鴨子類別中實作細節,這樣就不會把實作綁死在類別裡,例如飛行的實作:(以下皆由C++改寫原書內JAVA程式)
class FlyBehavior // interface of fly
{
public:
virtual void fly()=0;
};
class FlyInSky: public FlyBehavior
{
public:
void fly() override
{
cout << "fly in sky!" << endl;
}
};
class FlyNoWay: public FlyBehavior
{
public:
void fly() override
{
cout << "cannot fly!" << endl;
}
};
如此,就可以 動態 的改變行為,如下:
//-----
// before: 寫死實作
MallardDuck mallard;
mallard.fly(); // 鴨子fly()行為無法改變
// after: 善用介面
Duck *mallard = new MallardDuck();
mallard->performFly();
// output: 目前fly行為
mallard->setFlyBefavior(new FlyNoWay()); // 可動態改變行為
mallard->performFly();
// output: FlyNoWay()行為
依照這種方式,我們可以把會變動的這幾種行為設計成這種方式,UML圖如下(依據書本內容以draw.io繪製):
有了原本鴨子的類別,也有了告自行為的類別,就可以來將整個結構整合在一起了。
要怎麼將鴨子跟行為串聯在一起呢?我們可以把這兩個行為類別再放到鴨子裡面,例如以下的方式:
class Duck
{
public:
FlyBehavior *flyBehavior = nullptr;
QuackBehavior *quackBehavior = nullptr;
Duck(){}
virtual void display()=0;
void performQuack()
{
flyBehavior->fly();
}
void performFly()
{
quackBehavior->quack();
}
};
這樣,各式的鴨子行為都可以被各自串聯起來,不會再針對每一類多出來的行為,需要重複地改寫不變的部分了。
而要動態地改變行為,我們可以使用setFlyBehavior()
這種函式來設定,如以下:
class Duck
{
public:
void setFlyBehavior(FlyBehavior *fb)
{
flyBehavior = fb;
}
void setQuackBehavior(QuackBehavior *qb)
{
quackBehavior = qb;
}
};
就可以達到前面動態改變的功能。
現在知道了FlyBehavior
與Duck
這兩個類別如何實作,拼起來就可以完成整個架構了;不如自行試試看QuackBehavior
與MallardDuck
的撰寫,並檢視加上了今天這些內容,昨天想到的UML是否有需要修改的地方,明天繼續來看書本中給出的最終UML架構與第三個原則,將策略模式做收尾!