2015-04-06 50 views
1

我試圖寫同一個程序的兩個版本:鏢:包裝所有函數調用

  • 一個高性能版本;和
  • 是一個讓用戶知道發生了什麼的較慢版本。

我想這與IDE如何實現正常/調試模式並不完全不一樣。

我的要求,按照重要性遞減順序,如下:

  1. 緩慢的版本應該產生相同的結果作爲高性能版本;
  2. 慢版本應該包裝由高性能版本所做的公共函數調用的子集;
  3. 對較慢版本的要求不應該對高性能版本的性能產生不利影響;
  4. 最好不要複製代碼,但必要時自動複製;
  5. 代碼基大小增加最少;和
  6. 理想的是緩慢版本應該是能夠被單獨包裝(推測可能與一個在高性能版本單向依賴性)

我明白要求6可以是不可能的,因爲要求2需要訪問一個類實現細節(公用函數調用另一個公共函數的情況)。

爲了討論的緣故,請考慮以下程序的高性能版本來講述一個簡單的故事。

class StoryTeller{ 
    void tellBeginning() => print('This story involves many characters.'); 

    void tellMiddle() => print('After a while, the plot thickens.'); 

    void tellEnd() => print('The characters resolve their issues.'); 

    void tellStory(){ 
    tellBeginning(); 
    tellMiddle(); 
    tellEnd(); 
    } 
} 

一個天真的實現與反射鏡,例如以下內容:

class Wrapper{ 
    _wrap(Function f, Symbol s){ 
    var name = MirrorSystem.getName(s); 
    print('Entering $name'); 
    var result = f(); 
    print('Leaving $name'); 
    return result; 
    } 
} 

@proxy 
class StoryTellerProxy extends Wrapper implements StoryTeller{ 
    final InstanceMirror mirror; 

    StoryTellerProxy(StoryTeller storyTeller): mirror = reflect(storyTeller); 

    @override 
    noSuchMethod(Invocation invocation) => 
     _wrap(() => mirror.delegate(invocation), invocation.memberName); 
} 

我喜歡這個解決方案的風采,因爲我可以改變的高性能版本的界面,這只是工作。不幸的是,它不能滿足要求2,因爲tellStory()的內部調用沒有被包裝。

一個簡單但更詳細的溶液存在:

class StoryTellerVerbose extends StoryTeller with Wrapper{ 
    void tellBeginning() => _wrap(() => super.tellBeginning(), #tellBeginning); 
    void tellMiddle() => _wrap(() => super.tellMiddle(), #tellMiddle); 
    void tellEnd() => _wrap(() => super.tellEnd(), #tellEnd); 
    void tellStory() => _wrap(() => super.tellStory(), #tellStory); 
} 

此代碼可以很容易地自動生成的使用反射鏡,但它可以導致在代碼庫大小的大量增加,特別是如果高性能版本有一個廣泛的類層次結構,我想要一個const類似於在類樹中深入類的const變量。另外,如果任何類沒有公共構造函數,這種方法可以防止包的分離(我認爲)。

我也考慮過用wrap方法包裝基類的所有方法,而高性能版本有一個簡單的wrap函數。但是,我擔心這會對性能版本的性能產生不利影響,特別是如果wrap方法需要將調用作爲輸入。我也不喜歡這個內在地將我的性能版本鏈接到慢版本的事實。在我的腦海中,我認爲必須有一種方法可以讓慢版本成爲高性能版本的擴展,而不是兩個版本都是一些更通用的超版本的擴展。

我缺少的東西真的很明顯?是否有內置的「AnySuchMethod」或其他?我希望將代理解決方案的優雅與冗長解決方案的完整性結合起來。

+0

這是用於客戶端和/或服務器端的代碼嗎? – 2015-04-06 09:05:56

+0

這是用於客戶端代碼 – DomJack 2015-04-06 09:41:54

+0

然後,我會盡量避免反射。 Dart團隊正在更好地支持客戶端的反思,但目前使用反射時很難獲得合理的構建輸出大小。 – 2015-04-06 09:42:23

回答

1

您可以嘗試將額外的調試代碼置於asserts(...)中。當不在檢查模式下運行時,它會自動刪除。另請參見

否則只是做一個全局變量(const bool isSlow = true/false;)到處使用接口和工廠構造其返回緩慢或快速實現一個接口的依賴在isSlow值。 緩慢版本只能擴展快速版本以重用其功能並通過覆蓋其方法來擴展它。
這樣,您不必使用導致代碼膨脹的鏡像,至少對於客戶端代碼來說。
當您構建所有不必要的代碼時,會通過樹狀結構來移除,具體取決於isSlow的設置。 使用依賴注入有助於簡化開發不同實現的這種方式。

+1

認爲這將工作。只是想檢查我沒有失去明顯的東西。沒有意識到dart2js太聰明瞭。謝謝。 – DomJack 2015-04-06 09:53:52