Liskov Substitution Principleは、IntrospectionまたはDuck Typingと互換性がありませんか?


11

アヒル型言語で通常行われているように、オブジェクトを自分自身で検査できる言語では、リスコフ置換原理を遵守できないことを正しく理解できますか?

例えば、Rubyで、クラスの場合B、クラスから継承Aし、すべてのオブジェクトのためxAx.class返すために起こっているAが、場合xの目的はBx.class返却するつもりはありませんA

LSPの声明は次のとおりです。

ましょうQ(x)はオブジェクトに関するプロパティ証明可能であるxは型のT。次いで、Q(y)は、オブジェクトのために証明可能であるべきであるY型のS Sは、のサブタイプであるT

たとえば、Rubyでは、

class T; end
class S < T; end

プロパティq(x) =で見られるように、この形式のLSPに違反していますx.class.name == 'T'


添加。答えが「はい」(イントロスペクションとLSPの互換性がない)の場合、私の他の質問は次のようになります。性質


更新。参考のために、ウェブで見つけたLSPの別の定式化を以下に示します。

基本クラスへのポインターまたは参照を使用する関数は、知らないうちに派生クラスのオブジェクトを使用できる必要があります。

そしてもう一つ:

SがTの宣言されたサブタイプである場合、タイプSのオブジェクトは、タイプTのオブジェクトとして扱われる場合、タイプTのオブジェクトとして動作するはずです。

最後のものには次の注釈が付いています。

LSPはすべて、オブジェクトの予期される動作に関するものであることに注意してください。オブジェクトの予想される動作が明確である場合にのみ、LSPを追跡できます。

これは元のものよりも弱いようで、観察できるかもしれませんが、それが形式化されること、特に予想される動作を誰が決めるかを説明したいと思います。

LSPはプログラミング言語のクラスのペアのプロパティではなく、クラスのペアと特定のプロパティのセットのプロパティであり、祖先クラスによって満たされていますか?実際には、これは、LSPを尊重するサブクラス(子孫クラス)を構築するために、祖先クラスの考えられるすべての使用法を知っている必要があることを意味しますか?LSPによると、先祖クラスは任意の子孫クラスに置き換えられるはずですよね?


更新。 私はすでに答えを受け入れましたが、質問を説明するためにRubyからもう1つの具体例を追加したいと思います。Rubyでは、クラスはClassクラスの子孫であるという意味で、各クラスはモジュールですModule。しかしながら:

class C; end
C.is_a?(Module) # => true
C.class # => Class
Class.superclass # => Module

module M; end
M.class # => Module

o = Object.new

o.extend(M) # ok
o.extend(C) # => TypeError: wrong argument type Class (expected Module)

2
ほとんどすべての現代言語はある程度の内省を提供しているので、質問はRubyに固有のものではありません。
ヨアヒムザウアー

わかりました、例としてRubyをあげました。おそらく、イントロスペクションを使用する他のいくつかの言語にはLSPの「弱い形式」がいくつかありますが、原則を正しく理解していれば、イントロスペクションと互換性がありません。
アレクセイ

タイトルから「Ruby」を削除しました。
アレクセイ

2
簡単な答えは、互換性があるということです。ここに私がほとんど同意するブログ投稿があります:ダック・タイピングのリスコフ置換原則
K.Steff

2
このコンテキストの@Alexeyプロパティは、オブジェクトの不変式です。たとえば、不変オブジェクトには、値が変化しないというプロパティがあります。適切な単体テストを見る場合、これらのプロパティを正確にテストする必要があります。
K.ステフ

回答:


29

ここだ実際の原則は

ましょうタイプのq(x)オブジェクトについて証明可能なプロパティになります。そして、オブジェクトのための証明可能であるべきタイプののサブタイプです。xTq(y)ySST

そして、素晴らしいウィキペディアの要約:

コンピュータープログラムでは、SがTのサブタイプである場合、タイプTのオブジェクトは、タイプSのオブジェクトに置き換えられます(つまり、タイプSのオブジェクトは、タイプTのオブジェクトに置き換えられます)。そのプログラムの望ましいプロパティ(正確性、実行されたタスクなど)。

そして、論文からのいくつかの関連する引用:

必要なのは、サブタイプの動作を制限する強力な要件です。オブジェクトが実際にそのタイプのサブタイプのメンバーである場合でも、オブジェクトの推定タイプの仕様を使用して証明できるプロパティは保持する必要があります...

タイプ仕様には、次の情報が含まれます。-
タイプの名前。
-タイプの値スペースの説明。
-タイプのメソッドごとに:
---その名前。
---その署名(通知された例外を含む);
---事前条件と事後条件の観点からの動作。

質問に続きます:

アヒル型言語で通常行われているように、オブジェクトを自分自身で検査できる言語では、リスコフ置換原理を遵守できないことを正しく理解できますか?

番号。

A.classクラスを返します。
B.classクラスを返します。

より具体的なタイプで同じ呼び出しを行い、互換性のある結果を得ることができるため、LSPが保持されます。問題は、動的言語では、結果に基づいてそれらが存在することを期待できることです。

しかし、静的に構造化された(アヒル)型付き言語を考えてみましょう。この場合、A.class必要な制約Aまたはのサブタイプを持つ型を返しAます。これにより、サブタイプの任意のサブタイプが、その制約を満たすタイプの結果となるAメソッドT.classを提供する必要があるという静的な保証が提供されます。

これにより、ダックタイピングをサポートする言語でLSPが保持され、RubyのようなものでのLSP違反は、言語設計の非互換性よりも通常の動的な誤用によるものであるという強い主張が得られます。


1
「より具体的なタイプで同じ呼び出しを行い、互換性のある結果を得ることができるため、LSPは保持されます」。結果が同一の場合、LSPが正しく理解していれば保持されます。与えられた制約に関して、「週」形式のLSPがあり、すべてのプロパティの代わりに、与えられた制約のみが満たされることを要求するかもしれません。いずれにせよ、私は任意の参照をいただければ幸いです。
アレクセイ

@Alexeyは、LSPの意味を含めるために編集されました。Aが予想される場所でBを使用できる場合、LSPが成立します。Rubyの.classがこれに違反する可能性があるとあなたがどう思うか興味があります。
テラスティン

3
@Alexey-プログラムにfail unless x.foo == 42サブタイプが含まれ、サブタイプが0を返す場合、それは同じことです。これはLSPの障害ではなく、プログラムの正常な動作です。多態性はLSPの違反ではありません。
テラスティン

1
@Alexey-もちろん。それがプロパティであると仮定しましょう。その場合、サブタイプに同じセマンティック動作を許可しないため、コードはLSPに違反します。しかし、動的な言語やカモ型の言語にとって特別なことではありません。違反の原因となる言語設計には何もありません。あなたが書いたコードはそうです。LSPは、プログラムの数学的特性ではなく、プログラム設計の原則であることを忘れないでください(そのため、定義に含める必要があります)。
テラスティン

6
@Alexey:x.class == Aに依存する何かを記述する場合、言語ではなくLSP違反するのはコードです。ほとんどすべてのプログラミング言語で、LSPに違反するコードを記述することは可能です。
アンドレスF.

7

LSPのコンテキストでは、「プロパティ」は型(またはオブジェクト)で観察できるものです。特に、「証明可能な財産」について語っています。

このような「プロパティ」には、foo()戻り値を持たないメソッドが存在する可能性があります(そしてドキュメントに設定されたコントラクトに従います)。

この用語を「プロパティ」と混同しないようにしてください。「プロパティ」は「classRubyのすべてのオブジェクトのプロパティ」です。このような「プロパティ」は「LSPプロパティ」になりますが、自動的に同じではありません!

さて、あなたの質問に対する答えは、「プロパティ」をどの程度厳格に定義するかに大きく依存します。あなたが言う場合は、「クラスのプロパティはAつまり.class、オブジェクトの種類を返します」、そしてB実際に行い、そのプロパティを持っています。

ただし、「property」を「.classreturns A」と定義した場合、明らかにそのプロパティBはありませ

ただし、2番目の定義はあまり有用ではありません。定数を宣言するためのラウンドアバウト方法を本質的に見つけたからです。


プログラムの「プロパティ」の定義は1つしか考えられません。特定の入力に対して特定の値を返します。より一般的には、別のプログラムでブロックとして使用すると、特定の入力の他のプログラムは与えられた値。この定義では、「.classオブジェクトのタイプを返す」という意味がわかりません。を意味する場合x.class == x.class、これは興味深いプロパティではありません。
アレクセイ

1
@Alexey:LSPのコンテキストで「プロパティ」が何を意味するのかを明確にして質問を更新しました。
ヨアヒムザウアー

2
@Alexey:論文を調べても、特定の定義や「プロパティ」は見つかりません。これはおそらく、この用語が一般的なCSの意味で使用されているためです。それは他の測定「オブジェクトのフィールド」とは何の関係もありません
ヨアヒムザウアー

4
@Alexey:これ以上何を話せるかわかりません。「プロパティはオブジェクトの品質または属性である」という定義を使用します。「色」は、物理的な可視オブジェクトのプロパティです。「密度」は材料の特性です。「指定されたメソッドを持つ」とは、クラス/オブジェクトのプロパティです。
ヨアヒムザウアー

4
@Alexey:入浴中に赤ちゃんを投げていると思います:一部のプロパティでは、LSPを保持できないからといって、それが役に立たない、または「どの言語でも保持できない」という意味ではありません。しかし、その議論はここまで行きます。
ヨアヒムザウアー

5

私が理解しているように、イントロスペクションについてLSPと互換性のないものはありません。基本的に、オブジェクトが別のメソッドと同じメソッドをサポートしている限り、この2つは交換可能である必要があります。それはあなたのコードが期待するならば、あるAddressオブジェクトを、それはだ場合、それは問題ではないCustomerAddressか、WarehouseAddress限り、両方が(例えば)を提供するよう、getStreetAddress()getCityName()getRegion()getPostalCode()。確かに、異なる種類のオブジェクトを受け取り、イントロスペクションを使用して必要なメソッド(オブジェクトDestinationAddressを受け取り、配送Shipment先住所をとして提示するクラスなど)を提供するある種のデコレータを作成できますAddressが、これは必須ではなく、確かにそうではありませんLSPの適用を妨げません。


2
@Alexey:同じメソッドをサポートするオブジェクトは「同じ」です。これは、同じ名前、同じ数と引数の型、同じ戻り値の型、同じ副作用(呼び出し元のコードから見える場合)を意味します。メソッドの動作は完全に異なる場合がありますが、契約を順守している限りは問題ありません。
TMN

1
@Alexey:しかし、なぜ私はそのような契約を結ぶのでしょうか?その契約はどのような実際の使用を果たしますか?私は、このようなAの契約を持っていた場合、私は単にのすべてののoccurance置き換えることができるx.class.nameとの'A' 効果的作り、x.class.name 役に立ちません
ヨアヒムザウアー

1
@Alexey:繰り返しますが、別のクラスを拡張してもフルフィルできないコントラクトを定義できるからといって、LSPが壊れることはありません。それは単に、拡張不可能なクラスを構築したことを意味します。「提供されたコードブロックが有限時間で終了する場合に戻る」メソッドを定義すると、履行できない契約もあります。プログラミングが役に立たないという意味ではありません。
ヨアヒムザウアー

2
@Alexey x.class.name == 'A'は、カモタイピングのアンチパターンであるかどうかを判断しようとしています。結局、カモタイピングは、「カモのように動き回ると、カモだ」ということから来ています。したがって、それが実行Aする契約のように動作Aし、それをA
尊重

1
@Alexey明確な定義が提示されました。オブジェクトのクラスは、その動作やコントラクトの一部ではなく、あなたがそれを呼び出したいものでもありません。「プロパティ」を「x.myFieldなどのオブジェクトフィールド」と混同していますが、指摘されているように、それは同じではありません。このコンテキストでは、プロパティは型不変式のような数学的プロパティに似ています。さらに、アヒルのタイピングが必要な場合は、正確なタイプをチェックするアンチパターンです。それでは、LSPとアヒルのタイピングの問題は何ですか?;)
アンドレスF.

4

Barbara Liskovの元の論文を調べた後、ほぼすべての言語でLSPを満足させるために、Wikipediaの定義を完成させる方法を見つけました。

まず第一に、「証明可能」という言葉は定義において重要です。ウィキペディアの記事では説明されておらず、「制約」はそれを参照せずに他の場所で言及されています。

論文の最初の重要な引用文は次のとおりです。

必要なのは、サブタイプの動作を制限する強力な要件です。オブジェクトが実際にそのタイプのサブタイプのメンバーである場合でも、オブジェクトの推定タイプの仕様使用して証明できる プロパティは保持する必要があります...

次に、2番目のタイプ仕様について説明します。

型指定には、次の情報が含まれます。

  • タイプの名前。
  • タイプの値スペースの説明。
  • タイプのメソッドごとに:
    • その名前;
    • その署名(通知された例外を含む);
    • 前提条件および事後条件に関するその動作。

そのため、LSPは特定の型仕様に関してのみ意味を持ち、適切な型仕様(たとえば空の仕様)については、おそらくどの言語でも満たすことができます。

私が考えるTelastynの答えは、「制約」が明示的に言及されたので、私は探していたものに近いこと。


テラスティン、もしあなたがあなたの答えにこれらの引用を加えることができたら、私はあなた自身のものよりもあなたのものを受け入れることを望みます。
アレクセイ

2
マークアップはまったく同じではないので、エンフェシスを少し変更しましたが、引用符は含まれていました。
テラスティン

申し訳ありませんが、ヨアヒム・ザウアーはすでに「証明可能な」プロパティについて言及していますが、これはあなたが嫌い​​なものです。一般に、既存の回答を修正しただけです。正直なところ、私は...あなたが探しているかわからない
アンドレスF.

いいえ、それは「何から証明可能」と説明されていませんでした。あまりにも多くの知識を許可すればx.class.name = 'A'、このプロパティはxクラス全体で証明可能ですA型指定は定義されていない、と非公式に何らかの指示が与えられたかのLSPとの正確な関係は、いずれかではなかったです。私はすでにリスコフの論文で探していたものを見つけ、上記の質問に答えました。
アレクセイ

あなたが大胆に言った言葉が鍵だと思います。スーパータイプがx、その型のいずれかに対してをx.woozle生成することを文書化する場合、生成されundefinedない型は適切なサブタイプになりx.woozleませんundefined。スーパータイプがについて何も文書化していない場合、スーパータイプx.woozleでの使用x.woozleがたまたま生じるという事実undefinedは、サブタイプで何ができるかについて何も意味しないでしょう。
supercat

3

ウィキペディアのLSPに関する記事を引用すると、「代替性はオブジェクト指向プログラミングの原則です。」これは、プログラムの設計の原則および一部です。に依存するコードを記述する場合x.class == A、LSPに違反しているのはコードです。この種の壊れたコードはJavaでも可能であり、アヒルの入力は必要ありません。

カモタイピングで本質的にLSPを壊すものはありません。あなたの例のように、あなたがそれを誤用する場合のみ。

追加の考え:オブジェクトのクラスを明示的にチェックすることは、とにかくアヒルのタイピングの目的に反しないか


アンドレス、LSPの定義を教えてください。
アレクセイ

1
@Alexey LSPの正確な定義は、サブタイプに関してウィキペディアに記載されています。非公式の定義はLiskov's notion of a behavioral subtype defines a notion of substitutability for mutable objects; that is, if S is a subtype of T, then objects of type T in a program may be replaced with objects of type S without altering any of the desirable properties of that program (e.g., correctness).です。オブジェクトの正確なクラスは、「プログラムの望ましいプロパティ」の1つではありません。そうしないと、アヒルのタイピングだけでなく、一般的なサブタイピングに反して、Javaのフレーバーが含まれます。
アンドレスF.

2
@Alexeyまた、あなたの例がx.class == ALSP ダックタイピングの両方に違反していることに注意してください。実際のタイプを確認する場合、ダックタイピングを使用しても意味がありません。
アンドレスF.

アンドレス、この定義は私が理解するほど正確ではありません。どのプログラム、特定のプログラム、または何か?望ましい特性とは何ですか?クラスがライブラリ内にある場合、異なるアプリケーションは異なるプロパティが望ましいと考えるかもしれません。(どちらか:私はLSPが与えられたプログラミング言語のクラスのペアの財産だと思ったので、コードの行は、LSPに違反する可能性がどのように表示されていないAB)を満たすLSPか。LSPが他の場所で使用されているコードに依存している場合、どのコードが許可されているかは説明されません。ここで何かを見つけたいと思います: cse.ohio-state.edu/~neelam/courses/788/lwb.pdf
Alexey

2
@Alexey LSPは、特定のデザインを保持します(または保持しません)。デザインで探すべきものです。一般的な言語のプロパティではありません。実際の定義ほど正確にはなりません:Let q(x) be a property provable about objects x of type T. Then q(y) should be provable for objects y of type S where S is a subtype of Tx.classここで興味深いプロパティの1つではないことは明らかです。そうしないと、Javaのポリモーフィズムも機能しません。「x.classの問題」にダックタイピングに固有のものはありません。今のところ同意しますか?
アンドレスF.
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.