2013年10月29日 星期二

Design Pattern : Chain of Responsibility

Design Pattern : Chain of Responsibility

Chain of Responsibility 可將所有職責鍊在一起, 使用者可直接發動開頭的第一件職責, 第一件職責做完後就會去啟動第二件第三件 … etc

底下的例子為去洗車的時候,通常有三個步驟:洗車 -> 打蠟 -> 曬曬陽光做日光浴
如果是開去給洗車場洗的話,其實我們就不需要知道太多細節,可以去吃個晚餐再回來就好。
這時候就可以使用Chain of Responsibility

1.  Handler interface
public interface ICarWashHandler
{
  
ICarWashHandler NextStep { set; get; }
   
void Service<T>(T obj);
}

u  Handler可以看出職責鍊是如何運作的, 我們只要將下一個Handler存放在NextStep,在Service動作的時候做完本身的職責後,再呼叫NextStep.Service就可以一直往下做完整個職責鍊了。

2.  實作Handler

u   第一件職責: 洗車:CarShower

public class CarShower:ICarWashHandler
{
  
public ICarWashHandler NextStep{ get; set;}

   
public void Service<T>(T obj)
    {
      
object client = obj;
      
if (typeof(T).Name.ToString() == "Client")
       {
          
String msg = String.Format(
               
"Car washed! (Customer : {0} {1})",
                (client
as Client).Name,
                (client
as Client).PhoneNumber);
          
Console.WriteLine(msg);
       }
      
if(this.NextStep!=null)
       {
          
this.NextStep.Service(obj);
       }
      
else //Set the default NextStep
      
{
           
this.NextStep = new CarWaxing();
           
this.NextStep.Service(obj);
       }

    }
}

u  第二件職責: 打蠟:CarWaxing
public class CarWaxing:ICarWashHandler
{
  
public ICarWashHandler NextStep{get;set;}
  
public void Service<T>(T obj)
   {
     
object client = obj;
     
if (typeof(T).Name.ToString() == "Client")
      {
         
String msg = String.Format(
              
"Car waxing! (Customer : {0} {1})",
               (client
as Client).Name,
               (client
as Client).PhoneNumber);
         
Console.WriteLine(msg);
      }
     
if (this.NextStep != null)
      {
         
this.NextStep.Service(obj);
      }
     
else //Set the default NextStep
     
{
         
this.NextStep = new CarSunBathing();
         
this.NextStep.Service(obj);
      }
   }
}


u  第三件職責: 日光浴:CarSunBathing
public class CarSunBathing:ICarWashHandler
{
  
public ICarWashHandler NextStep { get; set; }
  
public void Service<T>(T obj)
   {
     
object client = obj;
     
if (typeof(T).Name.ToString() == "Client")
      {
         
String msg = String.Format(
            
"Car sunbathing! (Customer : {0} {1})",
             (client
as Client).Name,
             (client
as Client).PhoneNumber);
         
Console.WriteLine(msg);
      }
     
if (this.NextStep != null)
      {
         
this.NextStep.Service(client);
      }
     
else //Set the default NextStep
     
{
         
Console.WriteLine("Car is great now!");
      }
   }
}


3.      主程式
Client JB = new Client("JB", "0911-29X-XXX");
ICarWashHandler carWash1 = new CarShower();
carWash1.Service<
Client>(JB);

結果:
Car washed! (Customer : JB 0911-29X-XXX)
Car waxing! (Customer : JB 0911-29X-XXX)
Car sunbathing! (Customer : JB 0911-29X-XXX)
Car is great now!


4.      我們也可以手動指定每個職責的下一個為哪個職責,例如我老婆看車子很髒想要洗兩次:
Client Lily = new Client("Lily", "0930-8XX-XXX");
ICarWashHandler carWash2 = new CarShower();
carWash2.NextStep =
new CarShower(); //指定下一個責任為 (再洗車一次)
carWash2.Service<Client>(Lily);

結果:
Car washed! (Customer : Lily 0930-8XX-XXX)
Car washed! (Customer : Lily 0930-8XX-XXX)
//洗了兩次
Car waxing! (Customer : Lily 0930-8XX-XXX)
Car sunbathing! (Customer : Lily 0930-8XX-XXX)
Car is great now!


5.      另外如果遇到某個無法處理目前要求的Handler Handler(職責)也可以選擇不處理直接丟給下一個。
例如 消光的車洗車後不用打蠟,這時候只要在打蠟的Handler中加上判斷就行了。
6.      雖然Chain of Responsibility降低耦合,但是只要職責一多,Performance就會被拖累。


2013年10月28日 星期一

Design Pattern : FlyWeight

Design Pattern : FlyWeight

FlyWeight Pattern 中文名稱為享元模式,使用FlyWeight可以共享Factory產生的物件,
換句話說,用一個FlyWeight Class存放Factory產生的物件。

例如一個DOC檔中有很多重複的Word 只是它們的字型設定不一樣。 我們可以把這些Word儲放在一個字典,使用的時候再從字典拿出來,另行設定字型即可,避免浪費時間和空間去儲存同樣的Word

底下的例子,為某家車廠提供了兩台試駕車,各經銷商可自行加上其他配備讓客戶試駕。

1.  試駕車的Interface
public interface ITestCar
{
  Color Color { get; set; }
 
String Name { get; set; }
 
String Equipment { get; set; }
  void Drive();
}

實作兩台試駕車 (另一台FocusTsB省略)
public class FocusTsA : ITestCar
{
   
public Color Color
    {
       
get{ return Color.Red; }
       
set { }
    }
   
public string Name
    {
        
get{ return "Focus試駕車A"; }
        
set { }
    }

   
public string Equipment{ get; set;}
   
   
public void Drive()
    {
        
Console.WriteLine("Driving {0} {1} (備註:{2})",
          
this.Color.ToString(), this.Name, (this.Equipment ?? ""));
    }
}

2.  FlyWeight Class

public
class TestCarsFlyWeight
{
    private List<ITestCar> testCarList;
   
public TestCarsFlyWeight()
    {
        testCarList =
new List<ITestCar>();
       
//Create some Object
       
this.Create();
    }
    
   
private void Create()
    {
       
ITestCar carA = new FocusTsA();
       
ITestCar carB = new FocusTsB();
       
this.testCarList.Add(carA);
       
this.testCarList.Add(carB);
    }
   
public void Add(ITestCar _tsCar)
    {
        
this.testCarList.Add(_tsCar);
    }
  public void Remove(ITestCar _tsCar)
    {
        
this.testCarList.Remove(_tsCar);
    }
   
public ITestCar Get(String _name)
    {
       
if(this.testCarList!=null && this.testCarList.Count >0)
        {
          
var car = testCarList.Find(x => x.Name == _name);
          
if (car != null)
           {
              
return car as ITestCar;
           }
          
else
          
{
              
throw new Exception("查無該試駕車輛");
           }
        }
       
else
       
{
          
throw new Exception("尚未加入任何試駕車輛");
        }
     }
}

u  我這邊是用一個List<T>存放物件,實際上可用任何容器。
u  FlyWeight可由內部建立重用物件,或由外部加入。

3.  主程式

TestCarsFlyWeight testCarFactory = new TestCarsFlyWeight();

///
客戶要試駕測試車A
testCarFactory.Get("Focus試駕車A").Drive();
///客戶要試駕測試車B
testCarFactory.Get("Focus試駕車B").Drive();
///客戶要試駕測試車B,且要求的業務要是美女 XD
ITestCar testCar = testCarFactory.Get("Focus試駕車B");
testCar.Equipment =
"美女業務陪同";
testCar.Drive();



結果:
Driving Red Focus
試駕車A (備註:無)
Driving Blue Focus
試駕車B (備註:無)
Driving Blue Focus
試駕車B (備註:美女業務陪同)


4.  結束。