質問

OOP 原則が存在する場合、静的に型付けされた環境 (たとえば、Ruby と C#) とは対照的に、動的に型付けされた環境では適用されない、または異なる適用方法は何ですか?これは静的か動的かという議論を求めるものではありません。むしろ、その分裂のどちらかの側に、一方には適用され他方には適用されない、または異なる方法で適用される受け入れられた原則があるかどうかを確認したいと考えています。「継承より合成を優先する」のようなフレーズは、静的に型付けされた OOP の文献でよく知られています。それらは動的側面にも同様に適用できますか?

たとえば、動的に型付けされた環境では、結合の粒度はメソッドのレベルを超えることはないようです。言い換えれば、特定の関数呼び出しは、呼び出し元をその特定のインターフェイスに結合するだけです。 どれでも クラスはおそらく満足できるでしょう、あるいは別の言い方をすれば、あの特定のアヒルのように擬似的なものであれば何でも満足させることができます。

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

このような違いにより、異なる原則やパターンが生じるのでしょうか?もしそうなら、これらの違いは明確に表現されていますか?というセクションがあります ルビーのつるはし この方向に少し進んだ本 (Duck Typing/Classes Aren'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
役に立ちましたか?

解決

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

  • 言語グループ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?オブジェクト指向設計の原則

そして

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

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

抽象ファクトリ、ビルダー、ファクトリ メソッド、プロトタイプ、アダプタ、ストラテジー、コマンド チェーン、ブリッジ、プロキシ、オブザーバ、ビジター、さらには MVC/MMVM などの粒度の粗いパターンは、小規模システムではあまり使用されない傾向があります。コードが少ないため、そのような構造を作成するメリットはそれほど大きくありません。

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

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

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

他のヒント

私は個人的には、両方の動的および静的型付け言語では動作しませんOOPの原則が原則ではない、と言ってみましょう。

ここでは、言ったことは一例です。

インタフェース分掌の原則( http://objectmentor.com/resources/articles/isp。 PDF には)クライアントが自分のニーズを満たしているほとんどの特定のインターフェイスに依存すべきであると述べています。クライアントコードは、クラスCの2つのメソッドを使用する必要がある場合は、Cは、インターフェイスを実装する必要があり、I、唯一のこれらの2つの方法を含む、クライアントはこの原則は、インタフェースはインタフェースので、(必要でない動的型付けの言語に無関係であるというC.よりも私を使用します。定義型、および型の変数はタイプレスされた言語で必要とされていません)。

[編集]

第二の例 - 依存性逆転原理( http://objectmentor.com/resources/articles /dip.pdfする)。この原則は、「インターフェースや抽象関数やクラスに依存するのではなく、具体的な関数やクラスの際に戦略」であると主張します。ここでも、動的型付け言語のクライアントコードで何に依存しない - それはちょうどメソッドのシグネチャを指定する - 。それによって、この原理を未然に防ぐ

第三の例 - リスコフの置換原則( http://objectmentor.com/resources/articles/ lsp.pdfする)。この原則のためのテキストブックの例では、Rectangleクラスをサブクラス広場クラスです。実際のオブジェクトが正方形であるので、高さも変更された場合、次に長方形変数上にsetWidth()メソッドを呼び出すクライアントコードは驚きです。ここでも、変数の型、少ない動的型付け言語では、Rectangleクラスは、クライアントコードで言及されることはありませんので、そのような驚きは生じないだろう。

私はこのすべての「ラジカル」ビューを持っている:私の意見では、数学に裏打ちされた、OOPは、何か面白いの問題のために静的型付けの環境では動作しません。私は、抽象関係が関与している意味するものとして興味深い定義します。これは、(「共分散の問題」を参照)を簡単に証明することができます。

この問題の核心は、OOPの概念はそれを約束することである関係がカプセル化を壊すことなく実装することができない、抽象化をモデル化する方法であり、静的な型付けによる配信契約プログラミングと組み合わせます。ただ、参照するには任意の共変バイナリ演算子を試してみてください。「未満」またはC ++で「追加」を実装しよう。あなたは簡単にベースの抽象化をコーディングすることができますが、それを実装することはできません。

動的システムにおいてそうOOは、実際に、元のSmalltalkのようなプロトタイプベースのシステムは、実際にすべての静的型付け制約付きで符号化することができない作業モデルを提供します。具体的には、動作を気にするいかなるハイレベル定式化タイプとノーカプセル化はありません

質問に別の方法を答えるために:非常に問題の根本的な仮定がinstrinsically欠陥があります。それは、単純なプログラミング作業以外のものを処理するのに十分な電力でそれの任意のモデルが存在しないため、一貫性のある理論ではないので、OOは、任意のコヒーレントな原則を持っていません。どのような違いは、あなたがあきらめるものです:あなたは、カプセル化をあきらめ動的システムでは、静的なシステムであなただけのすべての静的型付けのシステムがこれらの事をサポートしているので(関数型プログラミング、テンプレートなど)を働いていたモデルに切り替える

インターフェースは、あなたが直接、他の誰かのAPIに依存している場合は特に、オーバーヘッドのいくつかのレベルを追加することができます。シンプルなソリューション - 。誰かのAPIに依存しません。

それは理想的な世界に存在して望んだのインタフェースに各オブジェクトの話を持っています。あなたがこれを行う場合は、小さな範囲を持っている小さなインターフェースになってしまいます。インタフェースが変更されたときにそうすることによって、あなたはコンパイル時の失敗を得るでしょう。

あなたは時にインターフェースの変更行う必要があるでしょう。

あなたのインタフェースであるより小さく、より具体的な、より少ない「簿記」。

静的型付けの本当の利点の一つは、静的に、既にあなたが名前を必要とし、名前が<10個の文字である必要がある場合、作成...検証され、あなたが呼び出すことができるどのような方法を知っているが、その値オブジェクトを保証されていません検証することをカプセル化する名前のクラス。(必ずしもすべてのI / Oの側面かかわらは - それは純粋な値の型を保つ)、およびコンパイラはあなたではなく、実行時に検証することよりも、コンパイル時にエラーをキャッチすることができます。

あなたは、静的な言語を使用するつもりなら、あなたの利点にそれを使用します。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top