すべてのメソッドが仮想ではないのか、各クラスに少なくとも1つのインターフェイスがないのはなぜですか?


8

これは.NETプラットフォームに関するより哲学的な質問ですが、他の言語でも役立つかもしれません。私は多くの単体テストを行っていますが、特に私がしばしば苦労しているサードパーティのコンポーネントを使用している場合はそうです。.NETでは、コンポーネントの設計者に、どのメソッドを仮想化するかどうかを選択するという大きな主張があります。1つはコンポーネントの使用方法(仮想化する意味がある)、もう1つはコンポーネントのモック機能です。Shimsを使用してサードパーティのコンポーネントをモックアップできますが、これは多くの場合、設計や複雑さを悪化させます。.NETでのすべてのメソッドの仮想化(JAVAには最初からあります)についての議論は、パフォーマンスに関するものでした。しかし、それはまだ問題ですか?なぜすべてのメソッドが.NET virtualではないのか、または各クラスに少なくとも1つのインターフェイスがないのはなぜですか?


1
ええ、数分後に誰かが私に反対票を投じたので(私の初めて)、その理由を知りたいのです。
アントンカルチク2016年

「広すぎる」としてこの質問を締めくくる投票者もあなたに反対票を投じたと思います。
David Arno

1
「プログラマスタックエクスチェンジは、ソフトウェア開発に関する概念的な質問に関心のあるプロのプログラマ向けの質疑応答サイトです」私は何か間違ったことを理解しましたか?
アントンカルチク2016年

5
私はこの質問が広すぎることに同意する傾向があります。「なぜ各クラスにバーチャルがないのですか?」なぜそうすべきなのでしょうか?テストについて言及します。多分あなたはそれを「なぜこのクラス(最も厄介な.netクラス)はこれらのメソッドがvirtualとマークされていないのですか?」に変更することによって質問を改善できますか?ただし答えがあることを見つけることがあり、「そうそう、.NET 1.0を設計するとき、彼らが本当にTDDの爆発を予想していなかった。」
完璧主義

4
意見のみに関心があるために広範にしたい場合、広すぎる意見ベースの2つの理由により、トピックから外れます。
イェルクWミッターク

回答:


7

Andersが言うように、その一部はパフォーマンスについて、一部は不十分に考えられたデザインをロックダウンして、継承するように設計されていないものを盲目的に継承する人々によって引き起こされるトラブルの範囲を減らすことです。

パフォーマンスは明らかなようです。ほとんどのメソッドは最新のハードウェアでは認識されませんが、仮想パイプラインゲッターは、CPUパイプラインで予測しやすい命令にますます依存する最新のハードウェアであっても、かなりのヒットを引き起こす可能性があります。

現在、すべてをデフォルトで仮想化する理由は、単体テストフレームワークという1つの要素のみによって推進されているようです。これは、フレームワークを適切に設計するのではなく、フレームワークに合うようにコードを変更するのは悪い理由だと私には思えます。

おそらく問題はここで使用されているフレームワークにあります。これらは、実行時にコードを構築するのではなく、シムを挿入して実行時に関数を置き換えることができるように改善する必要があります静的およびサードパーティの機能について言及する)

したがって、メソッドがデフォルトで仮想ではない理由は、Andersが述べたとおりであり、ユニットテストツールに合うように仮想化したいという希望は、カートを馬の前に置くことです。

これで、インターフェースを使用するか、コードベース全体を作り直して、直接有線のメソッド呼び出しではなくメッセージパッシングを介して通信するコンポーネント(またはマイクロサービス)を使用することで、これらすべてを軽減できます。テストのためにユニットの表面を拡大してコンポーネントにし、そのコンポーネントが完全に自己完結型である場合、それを単体テストするために実際にねじ込む必要はありません。


私はこれ以上同意できませんでした。
Robert Harvey

@gbjbaanb私は最後の文を理解していませんでした「テストのためにユニットの表面を拡大してコンポーネントにし、そのコンポーネントが完全に自己完結型である場合、それを単体テストするためにねじ込む必要はありません。 」
アントンカルチク2016年

すばらしい答え(私のものより良い、と私は感じます:)
David Arno '31年

1
@AntonKalcikつまり、ブラックボックスがあれば、モックが必要な依存関係があってはなりません。つまり、入力を発射して何が出てくるかを確認することでテストします。ボックスが小さく、他のサービスから切り離されるように設計した場合、これは簡単に起こります。問題は、クラスが他のクラスから簡単に分離できないため、モックが必要になることです。ユニットをクラスではなくコンポーネント(多分dll、またはマイクロサービス)と考える場合、この方法でテストできます。
gbjbaanb 2016年

パフォーマンスに関して:CPUは仮想メソッド呼び出しを簡単に予測できます(常に同じ場合)。大きな問題は、JITコンパイラーが仮想メソッド呼び出しをインライン化するのがはるかに難しくなるため、それ以上の最適化が妨げられることです。
2016年

6

質問には2つの要素があるので、順番に説明していきます。

  1. .NETメソッドがデフォルトで仮想化されないのはなぜですか?

    継承には、実際には多くの問題があります。したがって、デザインの一部として使用する場合は、慎重に検討する必要があります。メソッドをデフォルトで非仮想化することにより、クラスを設計するときに開発者が継承について考えることを奨励するという理論があります。それが実際に機能するかどうかはもちろん別の問題です。

  2. すべてのクラスがインターフェースを実装しないのはなぜですか?

    貧弱なデザインは簡単な答えです。非常に長い間、優れた実践として一般に認識されているにもかかわらず、悲しいことに多くの人々はDIとTDDを考慮してシステムを設計していません。

完全に継承可能ではなく、簡単にモック化されないクラスの組み合わせは、テストが難しいコードの「完全な嵐」を作り出します。誰もがDIとデザインからインターフェースへの原則を使用する日がくるまで、痛みを軽減するためにできることはあまりありません。


3
「貧弱なデザインは簡単な答えです」-私は同意する傾向がありますが、例外があります。データ転送オブジェクトまたはレコードオブジェクトを参照してください。単純なPOCO / POJO /ここに言語を挿入するオブジェクトの場合、interface正当な理由で実装しても意味がありません。interface本質的に栄光のタプルであるものに対して持っていることにはどのような価値がありますか?(多分POJO / POCO自体を使用することは設計上の欠陥です)
Dan Pantry

2
@ AntonKalcik、C#チームは非常に保守的な「重大な変更を導入しない」アプローチを言語に採用しており、インターフェイスを必須にすることは間違いなく重大な変更になります。したがって、C#に追加されたことが表示されないのではないかと思います。
David Arno

3
@DanPantry、単純なデータオブジェクトではモックが必要になることはほとんどないため、通常はインターフェースは必要ありません。
David Arno

2
C♯では、インターフェースはオブジェクトを定義し、クラスは抽象データ型を定義します。(クラスはオブジェクト指向のコードを書くためにC♯で使用するものであるという一般的な誤解です。まったく逆です:C♯のOOコードは、クラスまたは構造体をタイプとして使用することはでき、インターフェースのみを使用できます。クラスをタイプとして使用するとすぐに、あなたはもうOOをしていません。)したがって、インターフェースを使用しないことを設計ミスとは言いません。これは設計上の選択です。ADTとオブジェクトには、長所と短所があります。特定のドメインでより意味のあるものを選択する必要があります。
イェルクWミッターク

5
@JörgWMittag:As soon as you use classes as types, you are no longer doing OO -クラスタイプです。何??何を言っても、通常のクラス継承 OOです。今やDIが大流行しているからといって、それがOOではないと判断することはできません。
ロバートハーヴェイ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.