2013年11月28日 星期四

Design Pattern : Singleton

Design Pattern : Singleton

Definition :
Ensure a class has only one instance and provide a global point of access to it


Singleton通常使用於只能建立某類別的單一個物件,例如 取號碼機(某段時間的號碼唯一)、戶政事務所發身分證(身分字號唯一), 目的是確保不會有多人同時建立或是同時修改這個物件。
我在先前有寫了一篇Singleton in MutiThread AP

裡面有三種利用Singleton建立取號機的方式,但是其實文章中取號的動作卻不是Thread-Safe 雖然程式碼中可以確保Multi-thread中是取得同一個取號機,但是取號的動作其實還必須lock才能確保取號的動作是Thread-Safe,否則還是有可能取道同樣的號碼。 (文章就不修改,留下不經一事不長一智的紀錄 XD

底下是利用Singleton取得每台汽車的CarUid 可以把它想成車牌號碼,必須是唯一的。


1.  Singleton Class :
=>
使用Thread-safe, lazy singleton
=>
注意在實際取得CarUid時,有使用lock以確保Thread-safe

public class CarUidSingleton
{
   
/// Car's Unique id
   
private string Uid = "000000";
   
   
private static readonly object block = new object();
   
   
public static CarUidSingleton GetInstance
    {
       
get
        { 
return InnerClass.instance; }
    }
   
public String GetUid()
    {
        
lock(block)
         { 
           
int _uid = 0;
           
if (int.TryParse(this.Uid, out _uid))
            {
                 _uid++;
                
this.Uid = _uid.ToString().PadLeft(6, '0');
            }
           
else
                 throw
new Exception("The Uid is not correct!");
           
return this.Uid;
        }
     }
    
private class InnerClass
    
{
        
static InnerClass() { }
        
internal static readonly CarUidSingleton instance = new CarUidSingleton();
     }
}

2.  主程式:
=>
剛好拿Async-AwiatTask兩種方式來實做Multi-thread的測試程式

        List<ICar> carList = new List<ICar>();
       
for(int i=0; i<5; i++)
        {

          
carList.Add(new Focus());
            carList.Add(
new Civic());
            carList.Add(
new Fit());
            carList.Add(
new V60());
            carList.Add(
new Accord());
        };

       
Parallel.ForEach(carList, car =>
            {
            
#region Async-Await
                 GetCarUidInfo_UseAsyncAwait(car);
///請參考3.
            
#endregion

             #region
Task
                 GetCarUidInfo_UseTask(car); ///
請參考4.
            
#endregion
           
}
         );
3.  Async-Await:

private
async static void GetCarUidInfo_UseAsyncAwait(ICar car)
{
    
String uid = await GetAsyncCarUid();
    
Console.WriteLine( String.Format("{0}({1})", car.Name, uid));
}

private async static Task<String> GetAsyncCarUid()
{
       
Thread.Sleep(1500);
       
return CarUidSingleton.GetInstance.GetUid();
}

4.  Task:

private static void GetCarUidInfo_UseTask(ICar car)
{
     
Task<String> _task = new Task<String>(
             () =>
CarUidSingleton.GetInstance.GetUid()
       );
       _task.Start();
       _task.ContinueWith(TaskEnded, car);
}

private static void TaskEnded(Task<String> task, object car)
{
      
String uid =  task.Result;
       
Console.WriteLine(
          
String.Format("{0}({1})", (car as ICar).Name, uid));
}


5.  結果:
Fit(000001)
Accord(000002)
Focus(000003)
Civic(000004)
V60(000005)
Fit(000006)
Accord(000007)
Accord(000008)

Focus(000024)
Civic(000025)






2013年11月27日 星期三

Design Pattern : Visitor

Design Pattern : Visitor

Definition :
Represent an operation to be performed on the elements of an object structure. Visitor lets you define a new operation without changing the classes of the elements on which it operates

乍看之下和Strategy模式很像,可以參考後面的延伸閱讀。



1.  Element : 可存放要處理的物件或參數,再由外部帶入一個Visitor 處理的方法是實作在Visitor
2.  ObjectStructure:存放Element集合,提供新增刪除元素,以及讓Client呼叫處理元素的方法。
3.  Visitor:提供方法注入於Element中做處理。

底下的程式碼為利用Visitor模式,對多台車子做試開、列出車子型號、列出訂車細節等不同的處理。

1.  Element
=>
須由外部帶入一個Visitor
public interface IElement:IDisposable
{
  
void Accept<T>(IVisitor<T> visitor);
}


2.  Visitor :
=>只提供一個訪問的方法,obj 為訪問中所需要的物件。

public abstract class Visitor<T>
{
  
public abstract void Visit(T obj); //T is element
}

3.  ObjectStructure
public interface IObjectStructure<T>
{

   
List<IElement> Elements { get; set; }
   
void Attach(IElement element);
   
void Detach(IElement element);
   
void Accept(IVisitor<T> visitor);
}

接下來開始實做類別的部分

4.  定義兩種Element :
=> _car物件是要帶給Visitor的參數。

public class CarElement : IElement
{
   
private ICar _car = null;
    
public CarElement(ICar car)
     {
       
this._car = car;
     }
    
public void Accept<T>(IVisitor<T> visitor)
     {
        visitor.Visit((T)
this._car);
     }

    
public void Dispose()
     {
       
this._car = null;
     }
}

public
class CarMidRangeElement:IElement
{
    
ICarMidRange _car = null;
    
public CarMidRangeElement(ICarMidRange car)
     {
       
this._car = car;
     }
    
public void Accept<T>(IVisitor<T> visitor)
     {
        visitor.Visit((T)
this._car);
     }
    
public void Dispose()
     {
       
this._car = null;
     }
}


5.  CarObjectStructure :
=>
提供存放Element的容器。
=>
提供可以Visit每個Element的方法。

public class CarObjectStructure<T> : IObjectStructure<T>
{

   
public List<IElement> Elements { get; set; }
   
public CarObjectStructure()
    {
      
this.Elements = new List<IElement>();
    }
   
public void Attach(IElement element)
    {
      
this.Elements.Add(element);
    }
   
public void Detach(IElement element)
    {
      
this.Elements.Remove(element);
    }
   
public void Accept(IVisitor<T> visitor)
    {
      
this.Elements.ForEach(x => x.Accept<T>(visitor));
    }
}

6.  Visitor : visitor可以提供business邏輯供注入,這邊實做了三種visitor

/// 試乘
public class DriveVisitor<T>:Visitor<T>
{
  
public override void Visit(T obj)
   {
     (obj
as ICar).Drive();
   }
}

/// 列出車子型號
public class ListNameVisitor<T> : Visitor<T>
{
 
public override void Visit(T obj)
  {
    
Console.WriteLine((obj as ICar).Name);
  }
}

/// 列出ICarMidRange車子的資訊
public class InfoVisitor<T> : Visitor<T>
{
 
public override void Visit(T obj)
  {
     
var midCar = (obj as ICarMidRange);
     
Console.WriteLine("{0} : 客戶({1}),業代({2})",
             midCar.Name, midCar.CustomerName, midCar.SalesName);
  }
}


7.  主程式:
u 
#region Element帶入ICar物件,存入CarObjectStructure,再利用visitor處理邏輯
ICar focus = new Focus();
ICar civic = new Civic();

//
建立ObjectStructure並放入Element
IObjectStructure<ICar> carStruct = new CarObjectStructure<ICar>();
carStruct.Attach(
new CarElement(focus));
carStruct.Attach(
new CarElement(civic));

//利用DriveVisitor對每個Element處理邏輯
IVisitor<ICar> visitor1 = new DriveVisitor<ICar>();
carStruct.Accept(visitor1);
//利用ListNameVisitor對每個Element處理邏輯
IVisitor<ICar> visitor2 = new ListNameVisitor<ICar>();
carStruct.Accept(visitor2);
carStruct =
null;
#endregion


結果:
I am driving Black Focus(FORD)
I am driving Black Civic(HONDA)
Focus
Civic


u  #region Element帶入ICarMidRange物件,存入CarObjectStructure,再利用visitor處理邏輯

ICarMidRange is300h = new Is300h();
is300h.CustomerName =
"小叮噹";
is300h.SalesName =
"二十一世紀車商";

//建立ObjectStructure並放入Element
IObjectStructure<ICarMidRange> carMidStruct = new CarObjectStructure<ICarMidRange>();
carMidStruct.Attach(
new CarMidRangeElement(is300h));

//利用DriveVisitor對每個Element處理邏輯
IVisitor<ICarMidRange> visitor3 = new DriveVisitor<ICarMidRange>();
carMidStruct.Accept(visitor3);
//利用InfoVisitor對每個Element處理邏輯
IVisitor<ICarMidRange> visitor4 = new InfoVisitor<ICarMidRange>();
carMidStruct.Accept(visitor4);
carMidStruct =
null;
#endregion


結果:
I am driving Black Is300h(Lexxx)
Is300h :
客戶(小叮噹),業代(二十一世紀車商)


8.  延伸閱讀:
StrategyVisitor