10

静的に型付けされた環境(RubyとC#など)とは対照的に、動的に型付けされた環境では、どのOOP原則が適用されたり、異なって適用されたりしませんか?これは静的対動的な議論の呼びかけではありませんが、どちらか一方に適用され、他方には適用されない、どちらかの側に受け入れられた原則があるかどうか、または異なる方法で適用されるかどうかを確認したいと思います。「継承よりも構成を優先する」などのフレーズは、静的に型付けされたOOPの文献でよく知られています。それらは動的な側面にも同じように適用できますか?

たとえば、動的に型付けされた環境では、結合の粒度はメソッドのレベルを超えないように見えます。言い換えると、特定の関数呼び出しは、呼び出し元をその特定のインターフェイスに結合するだけです。これは、クラスが満たす可能性があります。言い換えると、特定のアヒルのように鳴くものはすべてです。

一方、Javaでは、結合の粒度はパッケージと同じくらい高くなる可能性があります。特定のメソッド呼び出しは、別のクラス/インターフェースとのコントラクトを確立するだけでなく、それをそのクラス/インターフェースのパッケージ/jar/アセンブリに結合します。

このような違いは、異なる原則とパターンを生み出しますか?もしそうなら、これらの違いは明確にされていますか?Ruby Pickaxeの本には、この方向に少し進むセクションがあります(Duck Typing / Classes Are n't Types)が、他に何かあるのではないかと思います。Rubyのデザインパターンを知っていますが、読んでいません。

編集-Liskovは静的環境と同じように動的環境に適用しないと主張されてきましたが、私はそれが適用されると考えずにはいられません。一方では、クラス全体との高レベルの契約はありません。しかし、特定のクラスへのすべての呼び出しは、リスコフが規定する方法で子クラスが満たす必要がある暗黙のコントラクトを構成するのではありませんか?次のことを考慮してください。「いくつかのバーのことをする」の呼び出しは、子クラスが参加する必要がある契約を作成します。これは「特殊なオブジェクトを基本クラスのように扱う」場合ではありませんか?

class Bartender
    def initialize(bar)
       @bar = bar
    end

    def do_some_bar_stuff
        @bar.open
        @bar.tend
        @bar.close
    end
end

class Bar
    def open
        # open the doors, turn on the lights
    end
    def tend
        # tend the bar
    end
    def close
        #clean the bathrooms
    end
end

class BoringSportsBar < Bar
    def open
        # turn on Golden Tee, fire up the plasma screen
    end

    def tend
        # serve lots of Bud Light
    end
end

class NotQuiteAsBoringSportsBar < BoringSportsBar
    def open
        # turn on vintage arcade games
    end
end

class SnootyBeerSnobBar < Bar
    def open
        # replace empty kegs of expensive Belgians
    end

    def tend
        # serve lots of obscure ales, porters and IPAs from 124 different taps
    end
end

# monday night
bartender = Bartender.new(BoringSportsBar.new)
bartender.do_some_bar_stuff

# wednesday night
bartender = Bartender.new(SnootyBeerSnobBar.new)
bartender.do_some_bar_stuff

# friday night
bartender = Bartender.new(NotQuiteAsBoringSportsBar.new)
bartender.do_some_bar_stuff
4

4 に答える 4

5

あなたが触れている本質的な違いは次のとおりです。

  • 言語グループ 1. object.method1、object.method2、object.method3 などの呼び出し時に呼び出される実際のメソッドは、オブジェクトの存続期間中に変更される可能性があります。

  • 言語グループ 2. object.method1、object.method2、object.method3 などの呼び出し時に呼び出される実際のメソッドは、オブジェクトの存続期間中は変更できません。

グループ 1 の言語は、動的型付けを持ち、コンパイル時にチェックされるインターフェイスをサポートしない傾向があり、グループ 2 の言語は、静的型付けを持ち、コンパイル時にチェックされるインターフェイスをサポートする傾向があります。

すべての OO 原則は両方に適用されると言えますが、

  • グループ 1 では、(コンパイル時ではなく実行時に) チェックを実装するための追加の (明示的な) コーディングが必要になる場合がありますインターフェイス合意のチェック (グループ 1 のコードをグループ 2 のようにしたい場合)

  • グループ 2 では、追加の状態フラグを使用してサブメソッドを呼び出すことによってメソッド呼び出しに対して呼び出される実際のメソッドの変更をモデル化するため、または添付されたいくつかのオブジェクトの 1 つへの参照でメソッドまたはメソッドのセットをラップするために、いくつかの追加のコーディングが必要になる場合があります。いくつかのオブジェクトのそれぞれが異なるメソッドの実装を持つメインオブジェクトへ (グループ 2 のコードをグループ 1 のコードのようにしたい場合)

  • グループ 2 言語の設計に対する制限そのものが、(理解ではなく) コミュニケーションの容易さがより重要になる大規模なプロジェクトに適しています。

  • グループ 1 言語では設計上の制限がないため、小規模なプロジェクトに適しています。コードが小さいという理由だけで、さまざまな設計配管の制約が満たされているかどうかをプログラマが一目で簡単に確認できます。

  • あるグループの言語から他のグループのようなコードを作成することは興味深く、勉強する価値がありますが、言語の違いのポイントは、実際には、さまざまな規模のチームにどれだけ役立つかということです (-私は信じています! :))

  • 他にも様々な違いがあります

  • 関連する正確な原則に応じて、ある言語または別の言語で OO 設計を実装するために多かれ少なかれ足が必要になる場合があります。


編集

あなたの元の質問に答えるために、私は調べました

http://c2.com/cgi/wiki?PrinciplesOfObjectOrientedDesign

http://www.dofactory.com/patterns/Patterns.aspx

実際には、システムではさまざまな正当な理由 (そしてもちろんいくつかの悪い理由) により、オブジェクト指向の原則が守られていません。正当な理由には、パフォーマンスの懸念が純粋な設計品質の懸念を上回る場合、代替構造/命名の文化的利点が純粋な設計品質の懸念を上回る場合、および特定の言語の標準的な方法ではない機能を実装するための余分な作業のコストがその利点を上回る場合が含まれます。ピュアなデザイン。

Abstract Factory、Builder、Factory Method、Prototype、Adapter、Strategy、Chain of Command、Bridge、Proxy、Observer、Visitor、さらには MVC/MMVM などの粒度の粗いパターンは、小規模なシステムではあまり使用されない傾向があります。コードが少ないため、そのような構造を作成する利点はそれほど大きくありません。

State、Command、Factory Method、Composite、Decorator、Facade、Flyweight、Memento、Template メソッドなどのより細かいパターンは、おそらくグループ 1 コードでより一般的ですが、多くの場合、いくつかの設計パターンはオブジェクト自体ではなく、オブジェクトのさまざまな部分に適用されます。一方、グループ 2 のコード パターンは、オブジェクトごとに 1 つのパターンで存在する傾向があります。

私見では、ほとんどのグループ 1 言語では、すべてのグローバル データと関数を一種のシングルトン「アプリケーション」オブジェクトと考えることが非常に理にかなっています。手続き型プログラミングと OO プログラミングの境界線があいまいになりつつあることはわかっていますが、この種のコードは多くの場合、「アプリケーション」オブジェクトのように聞こえます。:)

Iterator のような非常に細かい設計パターンは、グループ 1 言語に組み込まれる傾向があります。

于 2009-12-17T00:32:00.613 に答える
3

個人的には、動的型付け言語と静的型付け言語の両方で機能しない OOP 原則は原則ではないということから始めましょう。

つまり、ここに例があります:

Interface Segregation Principle ( http://objectmentor.com/resources/articles/isp.pdf ) では、クライアントはニーズを満たす最も具体的なインターフェイスに依存する必要があると述べています。クライアント コードでクラス C の 2 つのメソッドを使用する必要がある場合、C はこれら 2 つのメソッドのみを含むインターフェイス I を実装する必要があり、クライアントは C ではなく I を使用します。この原則は、インターフェイスが不要な動的型付け言語では無関係です (インターフェイス定義された型であり、変数に型がない言語では型は必要ありません)

[編集]

2 番目の例 - 依存性逆転の原則 ( http://objectmentor.com/resources/articles/dip.pdf )。この原則は、「具体的な関数やクラスではなく、インターフェイスまたは抽象関数やクラスに依存する戦略」であると主張しています。繰り返しになりますが、動的に型付けされた言語では、クライアント コードは何にも依存せず、メソッド シグネチャを指定するだけなので、この原則が不要になります。

3 番目の例 - Liskov Substitution Principle ( http://objectmentor.com/resources/articles/lsp.pdf )。この原則の教科書の例は、Rectangle クラスをサブクラス化する Square クラスです。そして、Rectangle 変数で setWidth() メソッドを呼び出すクライアント コードは、実際のオブジェクトが Square であるため、高さも変更されると驚きます。繰り返しますが、動的に型付けされた言語では、変数は型を持たず、Rectangle クラスはクライアント コードで言及されないため、そのような驚きは発生しません。

于 2009-12-16T22:50:13.023 に答える
1

私はこれらすべてについて「過激な」見解を持っています。数学に裏打ちされた私の意見では、OOP は興味深い問題に対して静的に型付けされた環境では機能しません。抽象的な関係が絡んでいるという意味で興味深いと定義します。これは簡単に証明できます (「共分散問題」を参照)。

この問題の核心は、OOP の概念が抽象化をモデル化する方法であると約束し、静的型付けによって提供されるコントラクト プログラミングと組み合わせると、カプセル化を壊さずにリレーションを実装できないことです。共変二項演算子を試してみてください。C++ で「より小さい」または「加算」を実装してみてください。基本抽象化は簡単にコーディングできますが、実装することはできません。

動的システムでは、高レベルの形式化された型や煩わしいカプセル化がないため、オブジェクト指向は実際に機能します。特に、元の Smalltalk のようなプロトタイプベースのシステムは、静的型付けの制約ではまったくエンコードできない作業モデルを実際に提供します。

質問に別の方法で答えると、まさに質問の基本的な仮定は本質的に欠陥があります。オブジェクト指向には一貫した原則がありません。単純なプログラミング タスク以外を処理するのに十分な能力を備えたモデルが存在しないため、オブジェクト指向は一貫した理論ではないからです。異なるのは何を放棄するかです: 動的システムではカプセル化を放棄し、静的システムでは機能するモデル (関数型プログラミング、テンプレートなど) に切り替えるだけです。これは、静的に型付けされたすべてのシステムがこれらをサポートしているためです。

于 2010-12-30T09:21:35.157 に答える