オンラインでソフトウェアに関するランダムなトピックを読んでいるときに、用語ダックタイピングに出会いましたが、完全には理解していませんでした。
「ダックタイピング」とは何ですか?
質問のセマンティクスの議論はかなり微妙です(そして非常に学術的です)が、一般的な考え方は次のとおりです。
ダックタイピング
(「アヒルのように歩き、アヒルのように鳴くなら、それはアヒルです。」)-はい!しかし、それはどういう意味ですか??!これは、例によって最もよく示されています:
例:動的に型付けされた言語
魔法の杖を持っていると想像してください。それには特別な力があります。ワンドを振って"Drive!"と言ったら、まあ、それは運転している!
それは他のもので動作しますか?よくわからないので、トラックで試してみます。うわー、それもドライブ!それから飛行機、電車、1 Woods(人々がゴルフボールを「ドライブ」するために使用するゴルフクラブの一種です)でそれを試します。 それらはすべてドライブ!
しかし、それは、たとえばティーカップで動作しますか?エラー:KAAAA-BOOOOOOM!それはあまりうまくいきませんでした。 ====>ティーカップは運転できません!!当たり前!?
これは基本的にアヒルのタイピングの概念です。これは購入前に試用システムです。それが機能する場合、すべてが順調です。しかし、それが失敗した場合、まあ、それはあなたの顔に爆発するでしょう。
言い換えれば、オブジェクトが何であるかではなく、オブジェクトができるに興味があります。
例:静的に型付けされた言語
オブジェクトが実際に何であったかに関心があった場合、マジックトリックはプリセットされた許可されたタイプでのみ動作します-この場合は車ですが、は駆動可能な他のオブジェクトでは失敗します:トラック、モペット、トゥクトゥクなど。魔法の杖が車でのみ動作するを期待しているため、トラックでは動作しません。
言い換えると、このシナリオでは、魔法の杖はオブジェクトis(車ですか?)ではなく、オブジェクトis(車、トラックなどが運転できます)。
トラックを運転させる唯一の方法は、何らかの方法でマジックワンドに両方のトラックおよび車を期待できる場合です(おそらく「共通のインターフェイスを実装する」ことによって)。それが何を意味するのかわからない場合は、今のところ無視してください。
概要:キーの取り出し
アヒルのタイピングで重要なのは、オブジェクトisではなく、オブジェクトが実際にdo、できることです。
タイプBird
のオブジェクトを取得し、そのwalk()
メソッドを呼び出す単純な関数を設計していると考えてください。あなたが考えることができる2つのアプローチがあります:
Bird
のみを受け入れるか、コードがコンパイルされないことを確認する必要があります。誰かが私の関数を使用したい場合、彼はBird
sのみを受け入れることに注意する必要がありますobjects
を取得し、オブジェクトのwalk()
メソッドを呼び出すだけです。したがって、object
がwalk()
である場合、それが正しい場合、それができない場合、私の関数は失敗します。したがって、ここではオブジェクトがBird
またはその他のものであることが重要ではありません。walk()
(これはduckタイピング)であることが重要ですダックタイピングはいくつかの場合に役立つかもしれないと考えなければなりません、例えばPythonはダックタイピングたくさん。
ウィキペディアにはかなり詳細な説明があります。
http://en.wikipedia.org/wiki/Duck_typing
ダックタイピングは、特定のクラスまたは特定のインターフェイスの実装からの継承ではなく、オブジェクトのメソッドとプロパティの現在のセットが有効なセマンティクスを決定する動的なタイピングのスタイルです。
重要な注意点は、アヒルのタイピングでは、開発者は実際の基礎となるタイプではなく、消費されるオブジェクトの部分に関心があることです。
私は古いイディオムを繰り返す多くの答えを見ます:
それがアヒルのように見え、カックがアヒルのように見える場合、それはアヒルです
そして、アヒルのタイピングでできることの説明、または概念をさらに難読化しているように見える例に飛び込みます。
私はあまり助けを見つけられません。
これは、私が見つけたアヒルのタイピングについてのわかりやすい英語の答えの最良の試みです。
ダックタイピングは、オブジェクトが何であるかではなく、何ができるかによって定義されることを意味します。
これは、オブジェクトのクラス/タイプにあまり関心がなく、オブジェクトで呼び出すことができるメソッドとオブジェクトで実行できる操作に関心があることを意味します。 タイプを気にせず、何ができるかを気にします。
ダックタイピングはタイプヒントではありません!
基本的に、「アヒルのタイピング」を使用するには、共通のインターフェースを使用して、特定のタイプではなく、より広い範囲のサブタイプ(サブタイプを意味するサブタイプを意味する場合、継承については説明しません)をターゲットにします。
情報を保存するシステムを想像できます。情報を読み書きするには、何らかのストレージと情報が必要です。
ストレージのタイプは、ファイル、データベース、セッションなどです。
インターフェイスは、ストレージタイプに関係なく使用可能なオプション(メソッド)を通知します。つまり、この時点では何も実装されていません。言い換えれば、インターフェースは情報を保存する方法について何も知りません。
すべてのストレージシステムは、まったく同じメソッドを実装することにより、インターフェイスの存在を知る必要があります。
interface StorageInterface
{
public function write(string $key, array $value): bool;
public function read(string $key): array;
}
class File implements StorageInterface
{
public function read(string $key): array {
//reading from a file
}
public function write(string $key, array $value): bool {
//writing in a file implementation
}
}
class Session implements StorageInterface
{
public function read(string $key): array {
//reading from a session
}
public function write(string $key, array $value): bool {
//writing in a session implementation
}
}
class Storage implements StorageInterface
{
private $_storage = null;
function __construct(StorageInterface $storage) {
$this->_storage = $storage;
}
public function read(string $key): array {
return $this->_storage->read($key);
}
public function write(string $key, array $value): bool {
return ($this->_storage->write($key, $value)) ? true : false;
}
}
そのため、情報の書き込み/読み取りが必要になるたびに:
$file = new Storage(new File());
$file->write('filename', ['information'] );
echo $file->read('filename');
$session = new Storage(new Session());
$session->write('filename', ['information'] );
echo $session->read('filename');
この例では、ストレージコンストラクターでDuck Typingを使用することになります。
function __construct(StorageInterface $storage) ...
それが役に立てば幸いです;)
私は一般的な答えをしていないことを知っています。 Rubyでは、変数やメソッドの型を宣言しません。すべては単なるオブジェクトの一種です。ルールは「クラスはタイプではない」です
Rubyでは、クラスは決して(OK、ほとんど決して)型ではありません。代わりに、オブジェクトのタイプは、そのオブジェクトが実行できることによってさらに定義されます。 Rubyでは、このカモタイピングを呼び出します。オブジェクトがアヒルのように歩き、アヒルのように話す場合、通訳はそれをあたかもアヒルのように扱って喜んでいます。
たとえば、曲情報を文字列に追加するルーチンを作成している場合があります。 C#またはJavaのバックグラウンドを持っている場合は、次のように書きたくなるかもしれません。
def append_song(result, song)
# test we're given the right parameters
unless result.kind_of?(String)
fail TypeError.new("String expected") end
unless song.kind_of?(Song)
fail TypeError.new("Song expected")
end
result << song.title << " (" << song.artist << ")" end
result = ""
append_song(result, song) # => "I Got Rhythm (Gene Kelly)"
Rubyのアヒルタイピングを採用すれば、もっと簡単なものを書くことができます。
def append_song(result, song)
result << song.title << " (" << song.artist << ")"
end
result = ""
append_song(result, song) # => "I Got Rhythm (Gene Kelly)"
引数のタイプを確認する必要はありません。 <<(結果の場合)またはタイトルとアーティスト(歌の場合)をサポートしている場合、すべてが機能します。そうでない場合、メソッドは例外をスローします(タイプをチェックした場合と同じように)。しかし、チェックがなければ、メソッドは突然はるかに柔軟になります。配列、文字列、ファイル、または<<を使用して追加するその他のオブジェクトを渡すことができます。これは機能します。
言語自体を見ると役立つ場合があります。しばしば助けになります(私は英語のネイティブスピーカーではありません)。
duck typing
で:
1)単語typing
は、キーボードで入力することを意味しません(私の心に残っているイメージのように)、それは「ものの種類は何か?
2)単語duck
は、その決定がどのように行われるかを表します。 「それがアヒルのように歩く場合...それはアヒルです」のように、それは一種の「ゆるい」決定です。物がアヒルかもしれないし、そうでないかもしれないので、それは「ゆるい」ですが、それが実際にアヒルであるかどうかは重要ではありません。重要なのは、アヒルでできることと、アヒルが示す行動を期待できることです。パン粉を食べさせることができ、物が私に向かってくるか、私に向かって突進するか、または後退するかもしれません...
アヒルのタイピング:
それが話し、アヒルのように歩くなら、それはアヒルです
これは通常、abduction(abductive推論または呼ばれるretroduction、より明確な定義だと思います):
from C(結論、見たもの)およびR(rule、 私たちが知っていること)、受け入れ/決定/仮定P(前提、property)言い換えれば、与えられた事実
...医学的診断の基礎
アヒルの場合:C =walks、talks、R =カモのように、P =それはカモです
プログラミングに戻る:
オブジェクトoにはmethod/propertymp1およびinterface/typeTrequire/definesmp1
オブジェクトoにはmethod/propertymp2およびinterface/typeTrequire/definesmp2
...
そのため、mp1...を単に受け入れるだけではなく、オブジェクトがmp1...、コンパイラ/ランタイムもアサーションoはタイプT
そして、それは上の例の場合ですか?ダックタイピングは本質的にタイピングなしですか?または、暗黙の型付けと呼ぶべきでしょうか?
アヒルのタイピング手法を使用したツリートラバーサル
def traverse(t):
try:
t.label()
except AttributeError:
print(t, end=" ")
else:
# Now we know that t.node is defined
print('(', t.label(), end=" ")
for child in t:
traverse(child)
print(')', end=" ")
動的型付け、静的型付け、およびアヒル型付けを混同するのは混乱していると思います。アヒルのタイピングは独立した概念であり、Goのような静的型付け言語でさえ、アヒルのタイピングを実装する型チェックシステムを持つことができます。型システムが(宣言された)オブジェクトのメソッドをチェックするが、型はチェックしない場合、アヒル型言語と呼ばれる可能性があります。