web-dev-qa-db-ja.com

階層モデルでの構造型付けは必要ですか?

これは、言語設計で使用される概念をフレームワークの形で抽象化することを目的とした抽象化プロジェクトと呼ばれるプロジェクトに焦点を当てた一連の質問の一部です。

使いやすさに関連する別のページが表示されます here 。フレームワークと投稿する適切な場所に関する問い合わせに関連するメタトピックは、 ここ にあります。

私は静的な高級言語(C#、VB.NETなど)内の概念を説明するフレームワークを書いています。その一部として、それらの概念を説明する構造の統合が含まれます。

私が持っていた1つの質問は、型パラメーター(パラメーターを持つコンストラクターは明確な追加となるでしょう)を通じて実装することを目的とする構造タイピングと呼ばれるものについてでした。これは、C#もVB.NETも実装しない概念です。

私が今日持っている質問は、階層モデルでの構造タイピングは必要ですか?

私が考えることができる1つの小さな例は、パターンに依存する開発モデルですが、必ずしもこのパターンを指定するためのインターフェースを備えている必要はありません。これは、共通の祖先がなく、構造が類似している複数の基本タイプにコードを使用する場合に役立ちます。

アイテムのリストを名前でコンソールに表示する場合は、その名前プロパティを定義する共通の最下位の祖先に対して1つのメソッドを作成する必要があります。例として、Person、Control、およびTypeはすべて異なるメソッドを使用します。

実装すると、結果のコードは次のようになります。

public static void DisplayNames<T>(IEnumerable<T> namedItems)
    where T has
    {
        ///<summary>Returns the item's name</summary>
        string Name { get; }
    }
{
    foreach (var namedItem in namedItems)
        Console.WriteLine(namedItem.Name);
}

'['および ']'は、曖昧性解消の理由で使用されます(それ以外の場合は、タイプパラメーターの構造の本体をインターフェイスの構造から識別しようとするのは困難です)。

提案は大歓迎です。

上記の回答のほとんどは、最初の質問が述べたように、構造的タイピングが何であるかについて本質的により多くのコンテキストを提供し、それが必要であるかどうかにあまり焦点を当てていなかったことを考えると、この回答をimplicitとしてマークし、言語の対象読者に基づいてマークします。

特定のプログラミングパターンでは、静的型付けモデルの上に構造型型付けモデルが必要です。ただし、同時に、特定の実装または現在の実装は、使用中の例であるため、関連するコンテキストを当面の質問に追加しません。彼らはexplicitlyそれが可能であると述べていますが、それがどのように可能であるかについては関係ありませんは、元の質問で最初に要求されたneedアスペクトに関連しています。

したがって、次のように質問に答えます。explicitタイプベースの実装制約を提供する開発環境、および2つの別々に開発されたライブラリが続く環境では、必要に応じて必要同じパターンですが、共通の祖先を共有したり、同じインターフェイスを実装したりすることはありません。ここで、構造型が機能します。

@ back2dosで示されているように、両方のタイプのタイピングを促進する言語が与えられれば、ユースケースはより簡単になりますが、それ自体はneedの側面に答えず、それがそれかどうかの質問に答えます行うことができます。

コンパイル時の安全性とアヒルのタイピングを組み合わせようとしているようです。

C++テンプレートは既にこれを自動的に実行しています。テンプレートのパラメーター化された型で何でも呼び出すことができ、テンプレートの型がその呼び出しをサポートしていない場合はコンパイルに失敗します。物事は別のテンプレート機能によって一般的に保たれます。特定のテンプレートインスタンス化の特定の機能を使用しない場合(特定のパラメーター化されたタイプを意味します)、コンパイルされない定義が存在することは問題ではありません。使用しない場合はエラーではありません。例:

class MyClass 
  {
  public:
     MyClass() {}
     std::string GetName() const { return "MyClass"; }
  };

template<typename T>
class TMyTemplate
  {
  public:
    TMyTemplate()
      {
      std::cout << myClassInstance_.GetName();
      }

    void ConceptMemberFunction()
      {
      std::cout << myClassInstance_.ConceptFunction()
      }

  private:
    T myClassInstance_;
  };


  int main(void)
    {
    TMyTemplate<MyClass>  myTemplate; // Compiles just fine.
    // myTemplate.ConceptMemberFunction(); // Compile-time error
    }

テンプレートのこの機能は、コンパイル時の「コンセプトチェック」に使用できます( boost :: Concept_check を参照)-コンセプトに必要な特定の機能を「使用」するテンプレートを作成してインスタンス化します-コンパイルは失敗しますクラスが必要なすべての機能をサポートしていない場合。

これらはあなたがあなたの質問で言及している概念ですか?

2

まあ、それはcan組み合わせることができます。特にhaXeはこれを示しています。

これにはいくつかの利点があります。

  • カップリングを下げます。抽象化のターゲットは、それが抽象化されていることを知る必要はありません。 (当然のことながら、アダプターを使用することもできますが、ほとんどの言語ではそれは大変な作業です)。
  • クラスオブジェクトを抽象化できます。 haXeでは、次のようにすることができます。
    typedef Singleton<T> = { function getInstance():T }:このタイプに一致するすべてのクラス(またはインスタンスまたは匿名オブジェクト)を抽象化できます。

これにはいくつかの欠点があります。

  • (インターフェースと比較して)高速な方法で実装することは非常に困難です。静的言語にコンパイルされると、haXeはリフレクションを使用してそのようなタイプを操作しますが、これは非常にコストがかかります。
  • 比較的弱い契約です。つまり:float xおよびyプロパティをポイントとして持つすべてを解釈することは合法ですか?ダックタイピングの擁護者はおそらく企業に「はい」と答えます。よくわかりません。

確認すべきことの1つは、関数型は実際には特殊な種類の構造型であり、関数値の抽象化を可能にすることです。

インターフェイスには、構造タイプよりも優れた利点があります。インターフェイスは構造を記述するだけではありません。 roleについて説明します。クラスがインターフェースを実装することを選択した場合、それはそのような役割を果たすことが適切であることを意味します。それにはある種の信頼性があり、構造型では得られません。何かがごちゃごちゃしているからといって、アヒルとして使うのは必ずしも適切ではありません。
関数型の場合、これは非常に明白になります。あなたが取る場合SomeType->Bool関数型として、これは関数の役割を説明しません。この関数は、操作(コレクションからのアイテムの削除など)を実行し、失敗したかどうかを通知する場合がありますが、オブジェクトに対して実行されるテストであり、合格または不合格の場合もあります。

構造サブタイピングは、言語によって提供される他のすべての型セマンティクスを回避する抽象化で型安全を維持するための優れたツールと言えます。これは強力なツールであり、注意して使用する必要があります。

0
back2dos