6

Martin Odersky の著書 Programming in Scala の抽象モジュールに関するセクションと、彼の論文 Scalable Component Abstractions を調べていました。

http://lampwww.epfl.ch/~odersky/papers/ScalableComponent.pdf

私の要点は、モジュールをオブジェクトの代わりに抽象クラスにすることです(または、Java のような古典的な静的なグローバル モジュール)。

abstract class myModule{ 
    // this is effectively an abstract module, whose concrete 
    // components can be configured by subclassing and instantiating it
    class thing{}
    class item{}
    object stuff{}
    class element{}
    object utils{}
}

異なる具体的な特性を持つモジュールの複数のサブクラスとインスタンスをインスタンス化できます。これにより、状況に応じてモジュールをさまざまに構成できます (たとえば、テスト中にデータベース コンポーネントを置き換えたり、開発環境で IO コンポーネントを置き換えたりするなど)、複数のモジュールをそれぞれ独自のモジュール スコープの変更可能な状態のセットでインスタンス化できます。

私が理解できることから、基本的なレベルでは、外側のクラスがモジュールとして機能できるように、ネストされたクラスを持つことができるというのは難しい要件です。

他の実際的な要件は、クラス定義を複数のファイルに分散できることです。これは、多数のクラスを含むモジュールは、単一のソース ファイルでほとんどが受け入れるよりも多くのコード行になる可能性があるためです。

Scala は Traits を使ってこれを行います。Traits は、複数のソース ファイルにまたがる抽象モジュール クラス全体に広がる優れた機能の中心ではありません。C# にはpartial classes、同じ機能を提供する があり、ネストされたクラスも使用できます。おそらく、他のいくつかの言語では、ネストされたクラスや、クラスを複数のファイルに分割するための同様のサポートがあります。

この種のパターンは、C# やその他の言語のどこかで発生しますか? 多くの言語での大規模なプロジェクトは、抽象モジュールが解決しようとしている問題に直面していると思います。この「抽象モジュールとしての抽象クラス」が機能せず、使用されない理由はありますか? 私には、同じ機能を提供するさまざまな DI フレームワークよりもはるかにクリーンなソリューションのように思えます。

4

2 に答える 2

8

通常の比較は ML モジュールとの比較です。ここでは、Scala トレイト (または抽象クラス) が ML シグネチャの役割を果たし、その具体的な実装 (通常は Scala オブジェクト) が ML 構造の役割を果たします。ここでの ML モジュールの説明により、接続がかなり明確になります。

Scala と ML の類似性は意図的なものであり、Scala コンパイラのソースを見ると、Scala オブジェクトが「モジュール」を一部に含む名前を使用して参照されることが多いことがわかります。

于 2012-06-08T08:39:22.150 に答える
2

説明する抽象モジュールには、次のコア プロパティがあります。

  • これは同封されたモジュールです。つまり、モジュールとの対話を可能にするすべてのインターフェースを提供します。
  • モジュールで操作を指定できます
  • モジュールの一部であるタイプを指定できます -- 通常、これらのタイプは上記のモジュールによって操作されます
  • 実装は提供されません -- これは抽象的な部分です。多くの具体的な実装が存在する可能性があり、プログラムで実際に使用されるものは、ニーズに最も適しているため、プログラムによって指定および選択されます。

複数のソースファイルを使用してモジュールを指定できる機能は、コア要件ではありませんが、確かに便利です。

最も基本的な形式では、モジュールは抽象データ型 (キューなど) を記述します。データ型と対話するために使用できる操作と、対話に必要な補助型です。

より複雑な形式では、サブシステム全体 (ネットワークなど) を記述することができます。

命令型言語では、通常、同じ目的でインターフェースを使用します。

  • 同封されています
  • 操作を指定できます
  • インターフェイスの一部であるタイプを指定できます
  • 実装なし

おっしゃったように、モジュールに大きなインターフェイスがある場合 (たとえば、サブシステムを記述している場合)、通常、リッチ インターフェイスを実装するクラスを 1 つのファイルに記述することは実際的ではありません。言語が同じクラスを別々のソースに分割することをサポートしていない場合 (より正確には、異なるソース ファイルから同じクラスの別々の部分を「接着」すること)、解決策は通常、囲まれた要件を失い、シリーズを提供することです。それらの間の相互作用を指定するインターフェースの - したがって、サブシステムの API を取得します (これは、最も純粋な意味での API です。これは、サブシステムと対話するためのインターフェースであり、まだ実装されていません)。

いくつかの点で、この後者のアプローチは、囲まれた型よりも一般的 (それで達成できるという意味で一般的) になる可能性があります: さまざまな作成者からのさまざまなサブタイプ (インターフェイスを介して指定) の実装を提供できます:サブタイプは、相互に作用するために指定されたインターフェースのみに依存するため、このミックス アンド マッチ アプローチは機能します。

ほとんどの関数型プログラミング言語の強みの 1 つは、パラメーター化されたデータ型です。これにより、dayatype を別のパラメーター (整数のキューなど) としてインスタンス化できます。同じ柔軟性は、Java/C# の Generics (および C++ のテンプレート) によって実現されます。もちろん、正確な意味と表現力は、型システムに基づいて言語間で異なる場合があります。

この全体の議論は別の形式の依存性注入 (DI) であり、型の具体的な実装とそのサポート部分の間の強い依存関係を、(実装に選択させるのではなく) 必要な部分を明示的に提供することによって緩和しようとします。型は、それらの部分のどの実装がその目標を達成するのに最適かをよりよく理解している可能性があります-たとえば、機能をテストするためのモック実装を提供します。

DI が解決しようとする問題は、命令型言語に限定されています。関数型言語でも同じ依存関係の問題が発生する可能性があります。抽象モジュールの実装は、サブタイプの特定の実装を使用することを選択する場合があります (したがって、それらの実装に結合します)。サブタイプの実装をパラメーターとして受け取る代わりに (これが DI の目的です)

于 2012-06-12T13:39:08.840 に答える