OO視角的重構技巧-if\switch 的消除

weixin_34377065發表於2007-11-11
基本結構:
private void Invoke(Class obj)
{
    
if typeof(obj)="x" then dosomethingX();
    
if typeof(obj)="y" then dosomethingY();
}

凡是符合這種結構的,都可以將dosomething移到obj中作為obj的一個成員,然後實現xClass,yClass,再採用如下方式呼叫
private void Invoke(Class obj)
{
    obj.dosomething()
}

擴充套件結構:
private void Invoke(Class obj)
{
    
if obj.name="x" then dosomethingX()
    
if obj.name="y" then dosomethingY()
}

這種結構的特點就是不再依賴於具體的型別來決定呼叫何種函式,而是依賴的該類的某個屬性。
通常這樣的處理,可以採用“化屬性為分類”來處理,即重新設計xClass,yClass繼承Class,再在xClass,yClass中分別實現dosomething,結果仍成為
   
private void Invoke(Class obj)
{
    obj.dosomething()
}

擴充套件結構與基本結構極其類似,實際上,基本結構是對隱含的typename的屬性採用了簡單的分類處理,而擴充套件結構是針對objname進行了分類處理。

再次擴充套件:
private void Invoke(Class obj)
{
    
if obj.name="xx" then dosomethingXX()
    
if obj.type="yy" then dosomethingYY()
    
if obj.role="zz" then dosomethingZZ()
}

前面的都是針對有統一屬性的情況時的處理,大體上上面兩種情況,可以解決所有的switch問題,那麼,對於非統一屬性的處理如何辦理?這種仍然大量的if,寫起來不但鬱悶,維護也很麻煩,如果採用繼承細化分類,則會造成更大的困擾。從繼承的角度來說,實際上它的原理也就是在內部有一張表進行維護,根據不同的型別呼叫,會呼叫該表中指定的函式。

所以對於這樣的情況,只要實現一張表,就可以更精細化地控制。

首先要結構一個類似的結構:

public class obj_method
{
    
public string name;
    
public string type;
    
public string role;
    
    
public dosomethingMethodHandle* dosomegthingMethod;
}

於是,我們就可以建立任意的條件來控制物件呼叫哪一種方法,在dotnet中,使用delegate直接解決MethodHandle問題,C++中亦有函式指標,所以這也不是問題,那麼如果是java一類的語言,應該如何辦?答案是實現一個介面IMethod,即:
public interface IMethod
{
    
void dosomething;
}

眾所周所介面可以粘合類,所以,這裡也是一樣,Class本身要實現IMethod介面,obj_Method也要實用這樣一個介面,然後在Class的dosomething呼叫obj_method的dosomething就行了。最後,仍然可以採用如下呼叫:
private void Invoke(Class obj)
{
    obj.dosomething()
}


更加複雜的擴充套件:

這種情況下,一般需要考慮狀態機來實現一個邏輯管理了,並不是簡單的繼承或多型能輕易解決的。

總結
由此可以看出,真正避免if的方法是不存在的,但我們可以針對各種不同型別的情況,把需要自己寫if的工作轉移給框架或系統內部提供的機制來進行處理,這樣,可以大大減少呼叫端程式碼的邏輯複雜度,從而顯得程式碼更加清楚,實際上這是一種把細顆粒的邏輯放大成了類或物件之間的邏輯。通常情況下,呼叫端是實現邏輯表現最複雜的部分,所以為了平衡化,大多數時候是把呼叫端的邏輯複雜度往服務提供端轉移,從而使專案中的程式碼達到更高的一致性,並整體上降低耦合性。
   直觀地說,if/switch集中存在的地方往往就是一團疙瘩的地方,必要的邏輯疙瘩是不可解的,但是我們可以把它弄分散,弄平整,從而最大化保證整潔度。 
  

相關文章