(轉)一個虛構的故事---委託和事件之委託

一個蠻有意思的講法。
1 委託

從前,在南方的一個異國他鄉,有一個叫Peter的勤勞的工人,他對老闆(boss)百依百順,然而他的boss卻是個卑鄙多疑的傢伙,他堅持要求 Peter不斷彙報工作進展。由於Peter不希望被boss盯着幹活,於是他向boss承諾隨時彙報工作進度。Peter通過如下所示的類型化的引用(typed reference)定期回調boss來實現這個承諾:

class Worker {
   public void Advise(Boss boss) {this.boss = boss; }
   public void DoWork() {
   Console.WriteLine("Worker: work started");
   if( boss != null ) boss.WorkStarted();

   Console.WriteLine("Worker: work progressing");
   if( boss != null ) boss.WorkProgressing();

   Console.WriteLine("Worker: work completed");
   if( boss != null ) {
   int grade = boss.WorkCompleted();
   Console.WriteLine("Worker grade= " + grade);
   }
   }
   Boss boss;
}

class Boss {
   public void WorkStarted() { }
   public void WorkProgressing() { }
   public int WorkCompleted() {
   Console.WriteLine("It's about time!");
   return 2;
   }
}

class Universe {
   static void Main() {
   Worker peter = new Worker();
   Boss boss = new Boss();
   peter.Advise(boss);
   peter.DoWork();

   Console.WriteLine("Main: worker completed work");
   Console.ReadLine();
   }
}

1.1 接口

現在,Peter成了一個特殊人物,他不但能夠忍受卑鄙的boss,和周圍的世界(universe)也建立了緊密的聯繫。Peter感到universe 對他的工作進程同樣感興趣。不幸的是,如果不爲universe添加一個特殊的Advise方法和特殊的回調,除了保證boss能夠被通知外,Peter 並不能向universe通知工作進度。Peter希望能從那些通知方法的實現中分離出潛在的通知列表,爲此,他決定將方法分離到一個接口中:

interface IWorkerEvents {
   void WorkStarted();
   void WorkProgressing();
   int WorkCompleted();
}

class Worker {
   public void Advise(IWorkerEvents events) {this.events = events; }
   public void DoWork() {
   Console.WriteLine("Worker: work started");
   if( events != null ) events.WorkStarted();

   Console.WriteLine("Worker: work progressing");
   if(events != null ) events.WorkProgressing();

   Console.WriteLine("Worker: work completed");
   if(events != null ) {
   int grade = events.WorkCompleted();
   Console.WriteLine("Worker grade= " + grade);
   }
   }
   IWorkerEvents events;
}

class Boss : IWorkerEvents {
   public void WorkStarted() { }
   public void WorkProgressing() { }
   public int WorkCompleted() {
   Console.WriteLine("It's about time!");
   return 3;
   }
}

1.2 委託

不幸的是,由於Peter忙於說服boss實現這個接口,以至於沒有顧得上通知universe也實現該接口,但他希望儘可能做到這一點,至少他已經抽象了對boss的引用,因此,別的實現了IWorkerEvents接口的什麼人也可以得到工作進度通知。

然而, Peter的boss仍然極其不滿。“Peter!”boss咆哮者,“你爲什麼要通知我什麼時候開始工作、什麼時候正在進行工作?我不關心這些事件,你不但強迫我實現這些方法,你還浪費了你的寶貴的工作時間等我從事件中返回。當我的實現需要佔用很長時間時,你等我的時間也要大大延長!你難道不能想想別的辦法不要老是來煩我嗎?”

因此,Peter意識到儘管在很多情況下接口很有用,但在處理事件時,接口的粒度還不夠精細。他希望能做到僅僅通知監聽者真正感興趣的事件。爲此,Peter決定把接口中的方法分解爲若干個獨立的委託函數,每一個都好象是隻包含一個方法的微型接口:

delegate void WorkStarted();
delegate void WorkProgressing();
delegate int WorkCompleted();

class Worker {
   public void DoWork() {
   Console.WriteLine("Worker: work started");
   if( started != null ) started();

   Console.WriteLine("Worker: work progressing");
   if( progressing != null ) progressing();

   Console.WriteLine("Worker: work completed");
   if( completed != null ) {
   int grade = completed();
   Console.WriteLine("Worker grade= " + grade);
   }
   }
   public WorkStarted started;
   public WorkProgressing progressing;
   public WorkCompleted completed;
}

class Boss {
   public int WorkCompleted() {
   Console.WriteLine("Better...");
   return 4;
   }
}

class Universe {
   static void Main() {
   Worker peter = new Worker();
   Boss boss = new Boss();

   // 注意:我們已將Advise方法替換爲賦值運算符
   peter.completed = new WorkCompleted(boss.WorkCompleted);
   peter.DoWork();
   Console.WriteLine("Main: worker completed work");
   Console.ReadLine();
   }
}

1.3 靜態訂閱者

利用委託,Peter達到了不拿boss不關心的事件去煩他的目標,然而Peter還是不能夠使universe成爲其訂閱者之一。因爲universe是一個全封閉的實體,所以將委託掛鉤在實例成員上不妥的(設想一下Universe的多個實例需要多少資源)。相反,Peter需要將委託掛鉤到靜態成員上,因爲委託也完全支持靜態成員:

class Universe {
   static void WorkerStartedWork() {
   Console.WriteLine("Universe notices worker starting work");
   }

   static int WorkerCompletedWork() {
   Console.WriteLine("Universe pleased with worker's work");
   return 7;
   }

   static void Main() {
   Worker peter = new Worker();
   Boss boss = new Boss();

   // 注意:在下面的三行代碼中,
   // 使用賦值運算符不是一個好習慣,
   // 請接着讀下去,以便了解添加委託的正確方式。
   peter.completed = new WorkCompleted(boss.WorkCompleted);
   peter.started = new WorkStarted(Universe.WorkerStartedWork);
   peter.completed = new WorkCompleted(Universe.WorkerCompletedWork);
   peter.DoWork();

   Console.WriteLine("Main: worker completed work");
   Console.ReadLine();
   }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章