當Strategy Pattern碰到signature不一樣時,該怎麼設計
-
2011年8月16日 下午 02:51
如題,在設計的時候,當原本程式碼存在著 利用if/else,來new不同的instance,執行抽象意義相同的方法時,通常我們會透過Strategy Pattern,改呼叫interface的方法。但這通常的前提是兩個instance的方法signature相同,才能直接透過一致的interface去呼叫。
如果方法的signature不同時,該怎麼設計比較妥善呢?
舉個例子,例如民眾去申辦某個作業,如果是代理人去,需要雙證件(身份證跟駕照)。如果本人去,只需要身份證。
範例的程式碼如下:
namespace StrategySample { class Program { /// <summary> /// 如果是代理人來申辦,需要有身份證跟駕照 /// 如果是親自來申辦,只需要身份證 /// </summary> /// <param name="args"></param> static void Main(string[] args) { var agent = new Agent(); Person person = new Person() { IsAgent = true, EntrustAgent = agent }; bool result; IdCard id = new IdCard(person.Id); if (person.IsAgent) { DrivingLicense drivingLicense = new DrivingLicense(); result = person.EntrustAgent.ApplyDocument(id, drivingLicense); } else { result = person.ApplyDocument(id); } } } } public class Person { public bool IsAgent { get; set; } public string Id { get; set; } public Agent EntrustAgent { get; set; } internal bool ApplyDocument(IdCard id) { throw new NotImplementedException(); } } public class Agent { internal bool ApplyDocument(IdCard id, DrivingLicense drivingLicense) { throw new NotImplementedException(); } } public class IdCard { private string _id; public IdCard(string id) { // TODO: Complete member initialization this._id = id; } } public class DrivingLicense { }
常用資源參考:
小弟的blog: In 91,wiki: my wiki
所有回覆
-
2011年8月16日 下午 02:58
有想到的幾種作法:
- 針對signature不一樣的部分,一樣保留if/else。signature一樣的部分,則透過interface。
- 將參數與回傳值,定義成abstract class/super class/interface,讓signature一樣,再透過interface去呼叫ApplyDocument
- 將Person與Agent的ApplyDocument方法所需要的參數,改封裝到property。
也就是呼叫person.ApplyDocument()或agent.ApplyDocument()。
在person.ApplyDocument()的方法中,再調用person自己的property來做ApplyDocument的邏輯。
在agent.ApplyDocument()的方法中,再調用agent自己的property來做ApplyDocument的邏輯。
不曉得大家有沒更好的作法或是建議呢?
常用資源參考:
小弟的blog: In 91,wiki: my wiki -
2011年8月16日 下午 03:36
>>如果方法的signature不同時,該怎麼設計比較妥善呢?
是說呼叫方法不同,還是方法同名但傳入引數不同?(小弟對這專有名詞不是很瞭解)
以下小弟的做法,是去執行person.ApplyDocument(id,drivingLicense);
person可能為代理人,也有可能為本人,但兩者都是執行ApplyDocument(id,drivingLicense);此方法您可能會疑問,要是本人沒駕照的情況呢?
=>駕照的傳數傳null就好
以下是小弟的淺見,和版主討論討論
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.IO; using System.Xml; namespace StrategySample { class Program { /// <summary> /// 如果是代理人來申辦,需要有身份證跟駕照 /// 如果是親自來申辦,只需要身份證 /// </summary> /// <param name="args"></param> static void Main(string[] args) { DrivingLicense drivingLicense = new DrivingLicense();//駕照物件 //這是代理人 //Person person = new Agent("AAAA",drivingLicense);//不管是代理人還是本人都有自己的id身份證 //這是本人 Person person = new myself("mmmm", null);//本人沒有駕照 person.ApplyDocument(); Console.ReadKey(); } } } public interface Person { //民眾去申辦某個作業 void ApplyDocument(); } //代理人 public class Agent:Person { string id; DrivingLicense drivingLicense; //身份證、駕照 public Agent(string id, DrivingLicense drivingLicense) { this.id = id; this.drivingLicense = drivingLicense; } public void ApplyDocument() { Console.WriteLine("代理人要帶身份證和駕照"); } } public class myself : Person { string id; DrivingLicense drivingLicense; //身份證、駕照 public myself(string id, DrivingLicense drivingLicense) { this.id = id; this.drivingLicense = drivingLicense; } public void ApplyDocument() { Console.WriteLine("本人只要身份證就好"); } } public class IdCard { private string _id; public IdCard(string id) { this._id = id; } } //駕照 public class DrivingLicense { }
Shadowと愉快なコード達 -
2011年8月16日 下午 03:37
public class Person { public IdCard IdCard { get; set; } internal virtual bool ApplyDocument() { // Check IdCard //... throw new NotImplementedException(); } } public class Agent : Person { public DrivingLicense DrivingLicense { get; set; } internal override bool ApplyDocument() { // Base // ... // Check DrivingLicense // ... throw new NotImplementedException(); } }
學無止境 -
2011年8月16日 下午 03:46
public class Person { public IdCard IdCard { get; set; } internal virtual bool ApplyDocument() { // Check IdCard //... throw new NotImplementedException(); } } public class Agent : Person { public DrivingLicense DrivingLicense { get; set; } internal override bool ApplyDocument() { // Base // ... // Check DrivingLicense // ... throw new NotImplementedException(); } }
學無止境恩,謝謝Clark兄,我這例子舉的情況,看起來剛好可以用繼承來省掉一份code。
如果不是這例子的情況,Agent與Person『不能用繼承的情況』呢?
例如Agent的話,需要帶『委託書』與『切結書』。本人的話則是帶『身份證』。
常用資源參考:
小弟的blog: In 91,wiki: my wiki -
2011年8月16日 下午 03:49
>>如果方法的signature不同時,該怎麼設計比較妥善呢?
是說呼叫方法不同,還是方法同名但傳入引數不同?(小弟對這專有名詞不是很瞭解)
以下小弟的做法,是去執行person.ApplyDocument(id,drivingLicense);
person可能為代理人,也有可能為本人,但兩者都是執行ApplyDocument(id,drivingLicense);此方法您可能會疑問,要是本人沒駕照的情況呢?
=>駕照的傳數傳null就好
以下是小弟的淺見,和版主討論討論
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.IO; using System.Xml; namespace StrategySample { class Program { /// <summary> /// 如果是代理人來申辦,需要有身份證跟駕照 /// 如果是親自來申辦,只需要身份證 /// </summary> /// <param name="args"></param> static void Main(string[] args) { DrivingLicense drivingLicense = new DrivingLicense();//駕照物件 //這是代理人 //Person person = new Agent("AAAA",drivingLicense);//不管是代理人還是本人都有自己的id身份證 //這是本人 Person person = new myself("mmmm", null);//本人沒有駕照 person.ApplyDocument(); Console.ReadKey(); } } } public interface Person { //民眾去申辦某個作業 void ApplyDocument(); } //代理人 public class Agent:Person { string id; DrivingLicense drivingLicense; //身份證、駕照 public Agent(string id, DrivingLicense drivingLicense) { this.id = id; this.drivingLicense = drivingLicense; } public void ApplyDocument() { Console.WriteLine("代理人要帶身份證和駕照"); } } public class myself : Person { string id; DrivingLicense drivingLicense; //身份證、駕照 public myself(string id, DrivingLicense drivingLicense) { this.id = id; this.drivingLicense = drivingLicense; } public void ApplyDocument() { Console.WriteLine("本人只要身份證就好"); } } public class IdCard { private string _id; public IdCard(string id) { this._id = id; } } //駕照 public class DrivingLicense { }
Shadowと愉快なコード達
謝謝Shadow兄的回應,若是new Myself()或new Agent()的部分,是透過Factory Pattern去取回執行的instance,那建構式的部分該怎麼處理呢?
另外,您的例子裡面,如果是myself,建構式的參數,應該就不需要傳駕照進去了。
常用資源參考:
小弟的blog: In 91,wiki: my wiki -
2011年8月16日 下午 03:54
-
2011年8月16日 下午 04:00
public class Person { public IdCard IdCard { get; set; } public bool ApplyDocument() { // Check IdCard //... throw new NotImplementedException(); } } public class Agent { public Passport Passport { get; set; } public DrivingLicense DrivingLicense { get; set; } public bool ApplyDocument() { // Check Passport // ... // Check DrivingLicense // ... throw new NotImplementedException(); } } public interface IUser { bool ApplyDocument(); } public class PersonUserAdapter : IUser { private readonly Person _adaptee; public bool ApplyDocument() { return _adaptee.ApplyDocument(); } } public class AgentUserAdapter : IUser { private readonly Agent _adaptee; public bool ApplyDocument() { return _adaptee.ApplyDocument(); } } 把雜七雜八的都抽出去,只留下bool ApplyDocument()就好 ^^
學無止境 -
2011年8月16日 下午 04:00
我的想法是這樣的,提供一個介面IPerson,他具有身分證和駕照
實作後分為本人和代理人,代理人可向本人取得身分證和駕照進行申請,但兩者皆繼承IPerson
public interface IPerson { IdCard Card { get; set; } DrivingLicense License { get; set; } } public class Person : IPerson { public IdCard Card { get; set; } public DrivingLicense License { get; set; } } public class PersonAgent : IPerson { public PersonAgent(IPerson person) { this.Card = person.Card; this.License = person.License; } public IdCard Card { get; set; } public DrivingLicense License { get; set; } }
根據本人或代理人,提供不同的驗證方式,但入口皆為IPerson介面
public interface IVerify { void Verify(IPerson person); } public class PersonVerifier : IVerify { public void Verify(IPerson person) { Console.WriteLine("Self Apply, ID: {0}", person.Card.ID); } } public class AgentVerifier : IVerify { public void Verify(IPerson person) { Console.WriteLine("Agent Apply, ID: {0}, License: {1}", person.Card.ID,person.License.Name); } }
而將選擇驗證方式的策略,封裝至工廠之中public class VerifierFactory { public IVerify GetVerifier(IPerson person) { if (person is Person) { return new PersonVerifier(); } else if (person is PersonAgent) { return new AgentVerifier(); } else { return null; } } }
提供一個Business Logic層,作為申請的進入點,允許各種IPerson提出申請,
並根據不同的IPerson類別,提供相對應的驗證方式
public class VerifyService { public VerifyService() { this.VerifierFactory = new VerifierFactory(); } public VerifierFactory VerifierFactory { get; set; } public void VerifyApplyment(IPerson person) { IVerify verifier = this.VerifierFactory.GetVerifier(person); if (verifier!=null) { verifier.Verify(person); } else { throw new Exception("Verifier not found!"); } } }
實際執行
class Program { static void Main(string[] args) { VerifyService flow = new VerifyService(); // 1. 親自申請 IPerson person = new Person() { Card = new IdCard() { ID = 1 }, License = new DrivingLicense() { Name = "Kirk" } }; flow.VerifyApplyment(person); // 2. 代理人申請 IPerson agent = new PersonAgent(person); flow.VerifyApplyment(agent); } }
如此一來,當擴充Person時,只需要修改VerifierFactory及增加Verifier,主要商業邏輯不須更動更甚者可將工廠改為抽象工廠,那只需修改設定檔即可完成擴充,
這是我的一點點小想法,提供出來和大家討論看看

