45

「ダックタイピング」というフレーズが頻繁に使われ、1 つまたは 2 つのコード例に出くわすことさえあります。私は忙しすぎ自分の研究をすることができません。誰か簡単に教えていただけますか?

  • 「ダックタイプ」とオールドスクールの「バリアントタイプ」の違い、および
  • バリアント型入力よりもダック型入力を好む可能性がある例を示します。
  • ダックタイピングを使用して達成する必要があるものの例を教えてください。

アヒルのタイピングの図は The Register の厚意により掲載

私は、この「新しい」構造の力を疑うことで、野鳥のように見えるつもりはありません。また、研究を拒否することで問題を回避しているわけでもありません。最近それ。私にはタイピング (別名動的タイピング) がないように見えるので、すぐには利点がわかりません。

補遺: これまでの例をありがとう。'O->can(Blah)' のようなものを使用することは、リフレクション ルックアップを実行することと同等であり (おそらく安くはありません)、および/または (O は IBlah) コンパイラーがあなたをチェックすることはできますが、後者には私の IBlah インターフェイスとあなたの IBlah インターフェイスを区別できるという利点がありますが、他の 2 つにはありません。確かに、メソッドごとにたくさんの小さなインターフェースが浮かんでいると面倒になりますが、多くの個々のメソッドをチェックすることもできます...

...だからまた私はそれを得ていません。それは素晴らしい時間の節約になりますか、それとも新品の袋に入った同じ古いものですか? ダックタイピングが必要な例はどこにありますか?

4

10 に答える 10

32

ここでの回答のいくつかで、用語の誤った使用が見られ、人々が間違った回答を提供する原因となっています。

したがって、答えを述べる前に、いくつかの定義を提供します。

  1. 強く型付けされた

    プログラムの型安全性を強制する言語は、厳密に型指定されています。つまり、進行と呼ばれるものと保存と呼ばれるものの 2 つが保証されます。進歩とは基本的に、すべての「有効に型付けされた」プログラムが実際にコンピューターで実行できることを意味します。それらはクラッシュしたり、例外をスローしたり、無限ループで実行されたりする可能性がありますが、実際には実行できます。保存とは、プログラムが「有効に型付けされている」場合、常に「有効に型付けされている」こと、および変数 (またはメモリ位置) に割り当てられた型に準拠しない値が含まれないことを意味します。

    ほとんどの言語には「進行」プロパティがあります。しかし、「保存」性を満たさないものも多い。良い例は、C++ (および C も) です。たとえば、C++ では、メモリ アドレスが任意の型であるかのように動作するように強制することができます。これにより、基本的に、プログラマーはいつでも型システムに違反することができます。以下に簡単な例を示します。

    struct foo
    {
        int x;
        iny y;
        int z;
    }
    
    char * x = new char[100];
    foo * pFoo = (foo *)x;
    foo aRealFoo;
    *pFoo = aRealFoo;
    

    このコードにより、誰かが文字の配列を取り、それに「foo」インスタンスを書き込むことができます。C++ が強く型付けされている場合、これは不可能です。C#、Java、VB、lisp、ruby、python などのタイプ セーフな言語では、文字の配列を「foo」インスタンスにキャストしようとすると、例外がスローされます。

  2. 弱い型付け

    強く型付けされていないものは、弱く型付けされています。

  3. 静的型付け

    コンパイル時に型システムが検証される場合、言語は静的に型付けされます。静的に型付けされた言語は、C のように "弱い型付け" にすることも、C# のように厳密に型付けすることもできます。

  4. 動的型付け

    動的型付け言語は、実行時に型が検証される言語です。多くの言語では、静的型付けと動的型付けが何らかの形で混在しています。たとえば、C# では、多くのキャストをコンパイル時にチェックできないため、実行時に動的に検証します。その他の例としては、Java、VB、Objective-C などの言語があります。

    「lisp」、「ruby」、「世間話」など、「完全に」または「ほとんど」動的に型付けされる言語もいくつかあります。

  5. ダックタイピング

    ダックタイピングは、静的、動的、弱い、または強いタイピングと完全に直交するものです。基になる型 ID に関係なく、オブジェクトで動作するコードを作成する方法です。たとえば、次の VB.NET コード:

    function Foo(x as object) as object
        return x.Quack()
    end function
    

    「Quack」と呼ばれるメソッドが定義されていれば、「Foo」に渡されるオブジェクトのタイプに関係なく機能します。つまり、オブジェクトがアヒルのように見え、アヒルのように歩き、アヒルのように話す場合、それはアヒルです。ダックタイピングにはさまざまな形があります。スタティック ダック タイピング、ダイナミック ダック タイピング、ストロング ダック タイピング、ウィーク ダック タイピングが可能です。C++ テンプレート関数は、「弱い静的ダック タイピング」の良い例です。「JaredPar's」投稿の例は、「強力な静的ダック タイピング」の例を示しています。VB (または Ruby または Python のコード) での遅延バインディングにより、「強力な動的ダック タイピング」が可能になります。

  6. 変異体

    バリアントは、動的に型指定されたデータ構造であり、文字列、整数型、日付、com オブジェクトなど、さまざまな事前定義されたデータ型を保持できます。次に、バリアントに格納されたデータの割り当て、変換、および操作のための一連の操作を定義します。バリアントが厳密に型指定されているかどうかは、使用されている言語によって異なります。たとえば、VB 6 プログラムのバリアントは厳密に型指定されています。VB ランタイムは、VB コードで記述された操作がバリアントの型付け規則に準拠することを保証します。VB でバリアント型を介して IUnknown に文字列を追加すると、実行時エラーが発生します。ただし、C++ では、すべての C++ 型が弱く型付けされているため、バリアントは弱く型付けされています。

わかりました....定義を理解したので、あなたの質問に答えることができます:

VB 6 のバリアントは、ダック タイピングの 1 つの形式を有効にします。ダックタイピングを行うには、亜種よりも優れた方法がありますが (Jared Par の例は最高の例の 1 つです)、バリアントを使用してダックタイピングを行うことができます。つまり、基になる型 ID に関係なく、オブジェクトを操作する 1 つのコードを記述できます。

ただし、バリアントでそれを行うと、実際には多くの検証が得られません。JaredPar が記述しているように、静的に型付けされたダック型メカニズムは、ダック型付けの利点に加えて、コンパイラからの追加の検証を提供します。それは本当に役に立ちます。

于 2008-11-14T08:47:15.720 に答える
19

簡単な答えは、バリアントは弱く型付けされているのに対し、ダック タイピングは強く型付けされているということです。

ダックタイピングは、「アヒルのように歩き、アヒルのように見え、アヒルのように振る舞うなら、それはアヒルだ」とうまく要約できます。コンピュータ サイエンスの用語では、duck を次のインターフェイスと見なします。

interface IDuck {
  void Quack();
}

それでは、ダフィーを調べてみましょう

class Daffy {
  void Quack() {
    Console.WriteLine("Thatsssss dispicable!!!!");
  }
}

この場合、Daffy は実際には IDuck ではありません。それでも、それはアヒルのように振る舞います。Daffy が実際にはアヒルであることは明らかなのに、なぜ Daffy に IDuck を実装させるのでしょうか。

ここでダック タイピングの出番です。これにより、IDuck と IDuck 参照のすべての動作を持つ任意の型の間でタイプ セーフな変換が可能になります。

IDuck d = new Daffy();
d.Quack();

完全な型安全性を備えた "d" で Quack メソッドを呼び出すことができるようになりました。この代入またはメソッド呼び出しでランタイム タイプ エラーが発生する可能性はありません。

于 2008-11-14T07:09:13.670 に答える
5

ダック タイピングは、動的タイピングまたは遅延バインディングの単なる別の用語です。実行時に実際に定義されるかどうかにかかわらず、任意のメンバー アクセス (obj.Anything など) で解析/コンパイルするバリアント オブジェクトは、ダック タイピングです。

于 2008-11-14T03:47:03.483 に答える
4

ダックタイピングを必要とするものはおそらく何もありませんが、特定の状況では便利です。サードパーティのライブラリからシールド クラス Duck のオブジェクトを取得して使用するメソッドがあるとします。そして、メソッドをテスト可能にしたいとします。また、Duck には非常に大きな API (ServletRequest のようなもの) があり、そのうちの小さなサブセットだけを気にする必要があります。どのようにテストしますか?

1 つの方法は、メソッドが何かを鳴らすようにすることです。次に、単に鳴き声のモック オブジェクトを作成できます。

于 2008-11-16T05:00:44.507 に答える
3

ダックタイピングに関するウィキペディアの記事の最初の段落を読んでみてください。
ウィキペディアのダックタイピング

メソッド Run() を定義するインターフェイス (IRunnable) を持つことができます。
次のようなメソッドを持つ別のクラスがある場合:
public void RunSomeRunnable(IRunnable rn) { ... }

ダック型に適した言語では、Run() メソッドを持つ任意のクラスを RunSomeRunnable() メソッドに渡すことができました。
静的に型付けされた言語では、RunSomeRunnable に渡されるクラスは IRunnable インターフェイスを明示的に実装する必要があります。

「アヒルのようにRun()したら」

バリアントは、少なくとも .NET のオブジェクトに似ています。

于 2008-11-14T03:57:17.317 に答える
2

@ケント・フレドリック

あなたの例は、明示的なインターフェースを使用することで、ダックタイピングなしで確実に実行できます...はい、そうではありませんが、不可能ではありません。

そして個人的には、アヒルのタイピングに頼るよりも、インターフェイスで明確に定義されたコントラクトを使用する方が、品質の高いコードを適用するのにはるかに優れていることがわかります...しかし、それは私の意見であり、それを一粒の塩で受け取ります。

public interface ICreature { }
public interface IFly { fly();}
public interface IWalk { walk(); }
public interface IQuack { quack(); }
// ETC

// Animal Class
public class Duck : ICreature, IWalk, IFly, IQuack
{
    fly() {};
    walk() {};
    quack() {};
}

public class Rhino: ICreature, IWalk
{
    walk();
}

// In the method
List<ICreature> creatures = new List<ICreature>();
creatures.Add(new Duck());
creatures.Add(new Rhino());   

foreach (ICreature creature in creatures)
{
    if (creature is IFly)        
         (creature as IFly).fly();        
    if (creature is IWalk) 
         (creature as IWalk).walk();         
}
// Etc
于 2008-11-14T04:30:29.903 に答える
2

ダックタイピングを使用して達成する必要があることの例についてのリクエストに関しては、そのようなものは存在しないと思います。再帰を使用するか、反復を使用するかを考えるのと同じように考えます。場合によっては、一方が他方よりもうまく機能することがあります。

私の経験では、ダック タイピングを行うと、コードが読みやすくなり、把握しやすくなります (プログラマーとリーダーの両方にとって)。しかし、より伝統的な静的型付けにより、多くの不要な型付けエラーが排除されることがわかりました。どちらかが他のものより優れていると客観的に言う方法はありません。

静的型付けに慣れている場合は、それを使用してください。ただし、少なくともダック タイプアウトを試してください (可能であれば、重要なプロジェクトで使用してください)。

より直接的に答えるには:

...だからまた私はそれを得ていません。それは素晴らしい時間の節約になりますか、それとも新品の袋に入った同じ古いものですか?

両方です。あなたはまだ同じ問題に取り組んでいます。あなたはそれを別の方法でやっているだけです。時間を節約するために本当に必要なのはそれだけの場合もあります (別の方法で何かを考えなければならない理由が他にない場合でも)。

全人類を絶滅から救う万能薬なのか?いいえ、そうでないと言う人は誰でも熱狂的です。

于 2008-11-16T04:04:28.613 に答える
1

ダックタイピングのポイントは使い方だと思います。エンティティがどうなるかを事前に宣言する代わりに、エンティティのメソッド検出とイントロスペクションを使用して、それをどうするかを知ることできます (どこで何をするかを知っています)。

プリミティブがプリミティブではなく、代わりにオブジェクトであるオブジェクト指向言語では、おそらくより実用的です。

私はそれを要約する最良の方法だと思います.バリアント型では、エンティティは何でもある/なり得るものであり、それが何であるかは不明ですが、エンティティは何かのようにしか見えませんが、尋ねることでそれが何であるかを理解できます. .

ダックタイピングなしではもっともらしいとは思えないものがあります。

sub dance { 
     my $creature = shift;
     if( $creature->can("walk") ){ 
         $creature->walk("left",1);
         $creature->walk("right",1); 
         $creature->walk("forward",1);
         $creature->walk("back",1);
     }
     if( $creature->can("fly") ){ 
          $creature->fly("up"); 
          $creature->fly("right",1); 
          $creature->fly("forward",1); 
          $creature->fly("left", 1 ); 
          $creature->fly("back", 1 ); 
          $creature->fly("down");
     } else if ( $creature->can("walk") ) { 
         $creature->walk("left",1);
         $creature->walk("right",1); 
         $creature->walk("forward",1);
         $creature->walk("back",1);
     } else if ( $creature->can("splash") ) { 
         $creature->splash( "up" ) for ( 0 .. 4 ); 
     }
     if( $creature->can("quack") ) { 
         $creature->quack();
     }
 }

 my @x = ();  
 push @x, new Rhinoceros ; 
 push @x, new Flamingo; 
 push @x, new Hyena; 
 push @x, new Dolphin; 
 push @x, new Duck;

 for my $creature (@x){

    new Thread(sub{ 
       dance( $creature ); 
    }); 
 }

他の方法では、関数の型制限を設定する必要があります。これにより、さまざまな種が切り捨てられ、さまざまな種に対してさまざまな関数を作成する必要があり、コードを維持するのが本当に地獄になります。

そして、それは良い振り付けをしようとするという点で本当に最悪です.

于 2008-11-14T03:48:32.750 に答える
1

バリアント (少なくとも私が VB6 で使用したもの) は、明確に定義された、通常は静的な型の単一の変数を保持します。たとえば、int、float、または string を保持できますが、バリアント int は int として使用され、バリアント float は float として使用され、バリアント文字列は string として使用されます。

ダックタイピングでは代わりに動的タイピングを使用します。ダックタイピングでは、特定のコンテキストで int、float、または string がサポートする特定のメソッドをたまたまサポートする場合、変数は int、float、または string として使用できる場合があります。

バリアントとダックタイピングの例:

Web アプリケーションの場合、データベースではなく LDAP からユーザー情報を取得したいとしますが、データベースと ORM に基づく Web フレームワークの残りの部分でもユーザー情報を使用できるようにしたいとします。

バリアントの使用: 運が悪い。UserFromDbRecord オブジェクトまたは UserFromLdap オブジェクトを含むバリアントを作成できますが、UserFromLdap オブジェクトは、FromDbRecord 階層からのオブジェクトを期待するルーチンでは使用できません。

ダック タイピングの使用: UserFromLdap クラスを取得し、UserFromDbRecord クラスのように動作させるいくつかのメソッドを追加できます。FromDbRecord インターフェース全体を複製する必要はありません。使用する必要のあるルーチンに十分なだけです。これを正しく行えば、非常に強力で柔軟なテクニックになります。間違ったやり方をすると、非常に紛らわしく脆弱なコードが生成されます (DB ライブラリーまたは LDAP ライブラリーのいずれかが変更された場合に破損する可能性があります)。

于 2008-11-14T03:52:32.927 に答える
0

ダックタイピングでできることはすべて、インターフェースでもできます。ダックタイピングは高速で快適ですが、エラーが発生する可能性があると主張する人もいます (2 つの異なるメソッド/プロパティに同じ名前が付けられている場合)。インターフェイスは安全で明示的ですが、人々は「なぜ明白なことを述べるのですか?」と言うかもしれません。残りは炎です。誰もが自分に合うものを選び、誰も「正しい」わけではありません。

于 2010-02-12T11:26:05.070 に答える