1

同じプログラムの 2 つのバージョンを作成しようとしています。

  • 高性能バージョン。と
  • ユーザーに何が起こっているかを知らせるより遅いバージョン。

IDEが通常/デバッグモードを実装する方法と完全に異なるわけではないと思います。

私の要件は、重要度の高い順に次のとおりです。

  1. 遅いバージョンは、パフォーマンスの高いバージョンと同じ結果を生成する必要があります。
  2. 遅いバージョンは、パフォーマンスの高いバージョンによって行われたパブリック関数呼び出しのサブセットをラップする必要があります。
  3. 低速バージョンの要件は、高性能バージョンのパフォーマンスに悪影響を与えるべきではありません。
  4. できればコードの複製は行わず、必要に応じて自動で複製します。
  5. コードベースのサイズの最小限の増加。と
  6. 理想的には、遅いバージョンは個別にパッケージ化できる必要があります (おそらく、パフォーマンスの高いバージョンへの一方向の依存があります)。

要件 2 ではクラスの実装の詳細にアクセスする必要があるため (パブリック関数が別のパブリック関数を呼び出す場合)、要件 6 は不可能かもしれないことを理解しています。

議論のために、次のパフォーマンス バージョンのプログラムを考えて、簡単なストーリーを伝えます。

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);
}

高性能バージョンのインターフェイスを変更でき、これが機能するため、このソリューションの優雅さが気に入っています。残念ながら、tellStory() の内部呼び出しがラップされていないため、要件 2 を満たすことができません。

シンプルですが、より冗長なソリューションが存在します。

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 が必要な場合はなおさらです。クラスツリーの奥。

また、クラスにパブリックコンストラクターがない場合、このアプローチはパッケージの分離を防ぎます (私は思います)。

また、基本クラスのすべてのメソッドをラップ メソッドでラップすることも検討しました。パフォーマンス バージョンには単純なラップ関数があります。ただし、特にラップメソッドが入力として呼び出しを必要とする場合、これがパフォーマンスバージョンのパフォーマンスに悪影響を与えるのではないかと心配しています。また、これが本質的にパフォーマンスの高いバージョンを遅いバージョンにリンクするという事実も嫌いです. 私の頭の中では、両方のバージョンがより一般的なスーパーバージョンの拡張であるのではなく、低速バージョンを高性能バージョンの拡張にする方法が必要であると考えています。

本当に明らかな何かが欠けていますか?組み込みの「anySuchMethod」などはありますか? プロキシ ソリューションの優雅さと詳細なソリューションの完全性を組み合わせたいと考えています。

4

1 に答える 1

1

追加のデバッグ コードを asserts(...) 内に配置してみてください。チェックモードで実行されていない場合、これは自動的に削除されます。こちらもご覧ください

それ以外の場合は、グローバル定数を作成します ( const bool isSlow = true/false;) どこでもインターフェイスを使用し、値に応じてインターフェイスの低速または高速の実装を返すファクトリ コンストラクターを使用しisSlowます。低速バージョンは、高速バージョンを拡張してその機能を再利用し、メソッドをオーバーライドして拡張することができます。
このようにして、少なくともクライアント側のコードでは、コードの肥大化を引き起こすミラーを使用する必要はありません。
の設定に応じて、ビルド時にすべての不要なコードがツリー シェイキングによって削除されますisSlow。依存性注入を使用すると、さまざまな実装を開発するこの方法を簡素化できます。

于 2015-04-06T08:15:52.193 に答える