close

雙分派實例說明

參考《設計模式之禪》P371的例子,但是改成 C++。
考慮以下例子:

演員演電影角色,一個演員可以扮演多個角色。角色分成功夫角色和諧星角色,演員有年紀大的演員和年輕的演員。

角色

class CRole		//角色
{
};
class CKungFu : public CRole	//功夫角色
{
};
class CFunny : public CRole		//諧星角色
{
};

演員

//宣告
class CAbsActor		//演員
{
public:
	virtual void Act(CRole *pRole);
	virtual void Act(CKungFu *pKungFu);
	virtual void Act(CFunny *pFunny);
};
class CYoung : public CAbsActor		//年輕演員
{
public:
	virtual void Act(CKungFu *pKungFu);
	virtual void Act(CFunny *pFunny);
};
class COld : public CAbsActor		//年紀大演員
{
public:
	virtual void Act(CKungFu *pKungFu);
	virtual void Act(CFunny *pFunny);
};


//定義
//CAbsActor
void CAbsActor::Act(CRole *pRole)
{
	cout<<"只要是演員就可以演各種角色"<<endl;
}
void CAbsActor::Act(CKungFu *pKungFu)
{
	cout<<"演功夫角色"<<endl;
}
void CAbsActor::Act(CFunny *pFunny)
{
	cout<<"演諧星角色"<<endl;
}
//CYoung
void CYoung::Act(CKungFu *pKungFu)
{
	cout<<"年輕人最喜歡演功夫"<<endl;
}
void CYoung::Act(CFunny *pFunny)
{
	cout<<"年輕人愛搞笑"<<endl;
}

//COld
void COld::Act(CKungFu *pKungFu)
{
	cout<<"年紀大演功夫很累"<<endl;
}
void COld::Act(CFunny *pFunny)
{
	cout<<"年紀大笑點很怪"<<endl;
}


主程式

//主程式
int main()
{
	CAbsActor *pActor = new COld;	//定義一個年紀大的演員
	CRole *pRole = new CKungFu;		//定義一個功夫角色
	pActor->Act(pRole);		//演戲
	delete pActor;
	delete pRole;
	system("pause");
	return 0;
}

這裡特別要注意的是,我都是用父類別宣告,因為在專案裡還是比較常遇到用父類別當參數的情況。

執行結果:只要是演員就可以演各種角色

但我其實希望的結果是「年紀大演功夫很累」,也就是執行COld::Act(CKungFu *pKungFu),而不是CAbsActor::Act(CRole *pRole)。會造成這樣是因為宣告時使用CAbsActor和CRole,所以會執行CAbsActor::Act(CRole *pRole)。

我們的希望是CAbsActor::Act會根據傳進來的CRole是哪個類別而做不同的事,所以改成這樣?

角色

//角色
class CRole		//角色
{
public:
	enum{ROLE, KUNGFU, FUNNY};
	int m_nKind;	//區分子類別
	CRole(){m_nKind = ROLE;}
};
class CKungFu : public CRole	//功夫角色
{
public:
	CKungFu(){m_nKind = KUNGFU;}
};
class CFunny : public CRole		//諧星角色
{
	public:
	CFunny(){m_nKind = FUNNY;}
};

演員

//宣告
class CAbsActor		//演員
{
public:
	virtual void Act(CRole *pRole);
};
class CYoung : public CAbsActor		//年輕演員
{
public:
	virtual void Act(CRole *pRole);
};
class COld : public CAbsActor		//年紀大演員
{
public:
	virtual void Act(CRole *pRole);
};

//定義
//CAbsActor
void CAbsActor::Act(CRole *pRole)
{
      //根據子類別不同做不同的事
	if(pRole->m_nKind == CRole::ROLE)
		cout<<"只要是演員就可以演各種角色"<<endl;
	else if(pRole->m_nKind == CRole::KUNGFU)
		cout<<"演功夫角色"<<endl;
	else if(pRole->m_nKind == CRole::FUNNY)
		cout<<"演諧星角色"<<endl;
}

//CYoung
void CYoung::Act(CRole *pRole)
{
	if(pRole->m_nKind == CRole::ROLE)
		cout<<"只要是演員就可以演各種角色"<<endl;
	else if(pRole->m_nKind == CRole::KUNGFU)
		cout<<"年輕人最喜歡演功夫"<<endl;
	else if(pRole->m_nKind == CRole::FUNNY)
		cout<<"年輕人愛搞笑"<<endl;
}

//COld
void COld::Act(CRole *pRole)
{
	if(pRole->m_nKind == CRole::ROLE)
		cout<<"只要是演員就可以演各種角色"<<endl;
	else if(pRole->m_nKind == CRole::KUNGFU)
		cout<<"年紀大演功夫很累"<<endl;
	else if(pRole->m_nKind == CRole::FUNNY)
		cout<<"年紀大笑點很怪"<<endl;
}


主程式不變

int main()
{
	CAbsActor *pActor = new COld;	//定義一個年紀大的演員
	CRole *pRole = new CKungFu;		//定義一個功夫角色
	pActor->Act(pRole);		//演戲
	delete pActor;
	delete pRole;
	system("pause");
	return 0;
}


執行結果:「年紀大演功夫很累」。
是啦,是有達到需求,但是3個長得很像的if-else如果要改一定會很頭大。所以這種方式完完全全不能考慮。

這裡的問題在於,連act都必須因為Role不同而做不同的act。這不是光用抽象就可以辦到的,因此考慮雙分派的做法。

角色

class CRole		//角色
{
public:
	//演員要扮演的角色
	virtual void Accept(CAbsActor *pActor)=0;
};
class CKungFu : public CRole	//功夫角色
{
	virtual void Accept(CAbsActor *pActor);
};
class CFunny : public CRole		//諧星角色
{
	virtual void Accept(CAbsActor *pActor);
};

void CFunny::Accept(CAbsActor *pActor)
{
	pActor->Act(this);
}
void CKungFu::Accept(CAbsActor *pActor)
{
	pActor->Act(this);
}


演員

class CAbsActor		//演員
{
public:
	virtual void Act(CRole *pRole);
	virtual void Act(CKungFu *pKungFu);
	virtual void Act(CFunny *pFunny);
};
class CYoung : public CAbsActor		//年輕演員
{
public:
	virtual void Act(CKungFu *pKungFu);
	virtual void Act(CFunny *pFunny);
};
class COld : public CAbsActor		//年紀大演員
{
public:
	virtual void Act(CKungFu *pKungFu);
	virtual void Act(CFunny *pFunny);
};

//定義
//CAbsActor
void CAbsActor::Act(CRole *pRole)
{
	cout<<"只要是演員就可以演各種角色"<<endl;
}
void CAbsActor::Act(CKungFu *pKungFu)
{
	cout<<"演功夫角色"<<endl;
}
void CAbsActor::Act(CFunny *pFunny)
{
	cout<<"演諧星角色"<<endl;
}
//CYoung
void CYoung::Act(CKungFu *pKungFu)
{
	cout<<"年輕人最喜歡演功夫"<<endl;
}
void CYoung::Act(CFunny *pFunny)
{
	cout<<"年輕人愛搞笑"<<endl;
}

//COld
void COld::Act(CKungFu *pKungFu)
{
	cout<<"年紀大演功夫很累"<<endl;
}
void COld::Act(CFunny *pFunny)
{
	cout<<"年紀大笑點很怪"<<endl;
}


主程式

int main()
{
	CAbsActor *pActor = new COld;	//定義一個年紀大的演員
	CRole *pRole = new CKungFu;		//定義一個功夫角色
	pRole->Accept(pActor);		//演戲
	system("pause");
	return 0;
}


執行結果:「年紀大演功夫很累」。

缺點:角色子類別不可以太多,設計模式中訪問者模式的注意事項,這裡也要注意。

arrow
arrow

    kamory 發表在 痞客邦 留言(0) 人氣()