継承のための設計はどのように余分なコストを引き起こすことができますか?[閉まっている]


12

だから私はsealed classインシャープから継承したいと思い、やけどを負った。ソースにアクセスできない限り、封印を解除する方法はありません。

それから、「なぜsealed存在するのか」と考えさせられました。4ヶ月前。次のような多くのことを読んだにもかかわらず、私はそれを理解できませんでした。

それ以来、すべてを消化しようとしましたが、それは私にとってはやりすぎです。最終的に、昨日私はそれをもう一度試しました。私はそれらすべてをもう一度スキャンし、さらにいくつかをスキャンしました:

最後に、熟考した後、新しいタイトルに基づいて元の質問を大幅に編集することにしました。

古い質問が広すぎると主観的でした。それは基本的に尋ねていました:

  1. 旧称:シールを使用する1つの正当な理由
  2. 本文:シールクラスを適切に変更する方法 継承を忘れますか?コンポジションを使用しますか?

しかし、今、理解すること(私は昨日なかった)すべてのsealed継承を阻止されず、そして私たちは、実際に使用する必要がありますすることができます継承上の組成を、私は私がした必要なものを実現実用例。

私はここに私の質問は正確に何を(実際には常にされている)であると思いMr.Mindorはチャットで私の提案どのように余分なコストの原因継承のために設計することができますか?


5
質問は非常に積極的に表現され、暴言のように聞こえます。関連性のある客観的な回答が必要な場合は、言い換える必要があります。
陶酔

11
フレームワーククラスの多くがなぜ封印されているのかをご覧くださいエリック・リッパート。彼はクラスを封印するいくつかの正当な理由を提供します。
ロバートハーベイ

9
その後、あなたは記事を読んでいませんでした。戻ってもう一度読んでください。それはこれに沸く:あなたは、クライアントがそれを拡張することで、あなたのクラスを壊すかもしれない無数の方法を予測することはできません。 あなたはあなたのクライアントを非難することができますが、あなたは彼らをサポートしなければならない人です。
ロバートハーベイ

4
@Cawas彼はそれを詳細に説明している彼がリンクした記事を読むことができました。彼は彼のコメントでその記事を要約していた。
セルビー

7
申し訳ありませんが、私はここで暴言を言っている人ではありません。あなたの質問への答えは非常に簡単です:継承をサポートしたくないときにクラスを封印します。それでおしまい。
ロバートハーベイ

回答:


27

これを理解するのはそれほど難しくありません。sealed彼らの生活を楽にし、たくさんのお金を節約し、彼らの評判を助けるために、マイクロソフトのために特別に作成されました。これは言語機能であるため、他の誰もが使用できますが、シールを使用する必要はおそらくないでしょう。

ほとんどの人は、クラスを拡張できないことに不満を抱いており、そうすれば、正しく動作させるのは開発者の責任であることを誰もがよく知っていると言います。同じ人々がWindowsの歴史についての手がかりを持たず、問題の1つがsealed解決しようとしていることを除いて、それは正しいです。

開発者が.Netコアクラスを拡張し(sealedが存在しなかったため)、完全に動作するようになったとします。はい、開発者向け。開発者は製品を顧客に提供します。開発者のアプリは素晴らしい作品です。顧客は満足しています。人生は素晴らしい。

Microsoftは、この特定の.Netコアクラスのバグ修正を含む新しいオペレーティングシステムをリリースしました。顧客は時代に遅れずについていくことを望み、新しいオペレーティングシステムをインストールすることを選択します。.Netコアクラスで修正されたバグを考慮しなかったため、顧客が非常に気に入っているアプリケーションは突然動作しなくなりました。

誰が責任を負いますか?

Microsoftの歴史に詳しい人は、Microsoftの新しいOSがWindowsライブラリを悪用したアプリケーションソフトウェアではなく、責任を負うことを知っています。そのため、問題を作成したアプリケーション会社ではなく、Microsoftが問題を修正することが義務付けられています。これが、Windowsコードが肥大化した理由の1つです。私が読んだことから、Windowsオペレーティングシステムコードには特定のif-thenが散らばっていて、特定のアプリケーションとバージョンが実行されているかどうかをチェックし、実行されている場合はプログラムを機能させるための特別な処理を行います。それは、Microsoftが別の会社の無能さを修正するために費やした大金です。

使用sealedしても上記のシナリオが完全になくなるわけではありませんが、役立ちます。


4
@Cawas悪用するのはかなり難しい。Eric Lippert氏は、sealed継承されるように設計されているクラスがほとんどないという理由だけで、特に開封されない限り、メソッドなどのクラスがすべてデフォルトであると望んでいることを何度も述べています。クラスがそれをサポートするように構築されていない可能性がはるかに高く、封印されていないというマークを付ける方法がわからない場合は、その開発時間を費やしていることを確認してください。
セルビー

1
内部開発ソフトウェアに封印を使用している人は、おそらくそれを誤用しているか、完璧になりすぎて会社の時間を浪費しているだけだと思います。おそらく、特定のシナリオで意味を成すことができるケースが常にあるからだと思います。ただし、ライブラリタイプのソフトウェアを開発している人にとっては、Microsoftがそれが必要だと判断したのと同じ理由で、非常に役立つと思われます。
ダンク

2
@Cawas 広大なほとんどの開発者によって書かれたクラスの大半継承する必要はありません、とサポートの継承に書き込まれません。これは、typeを作成することにより、コードのリーダー/ユーザーに迅速かつ効果的に伝えることができsealedます。それが実際にデフォルトのオプションだった場合、誰かが型を継承していれば、それをサポートするために構築されていることを知ることになります。
セルビー

2
クラスを開封しても、クラスがそれをサポートするために構築されたという意味ではありません。それは、誰かがクラスから継承してそれを開封したかったということです。最良の場合、sealedを使用すると、開発者は既存の機能(sealedクラスと同等)を書き換えることになりますが、ほとんどの場合、ソースコードが利用可能な場合は、結果を考慮せずに単純にそれを開封します。開発者がこのクラスがなぜ封印されているのかを知りたいと思うかもしれないので、封印されていることが明確にそしてめったに設定されていない方が良いです。封印されたクラスが多すぎると、彼らはその理由を気にしません。
ダンク

1
また、セキュリティはそれを使用する理由です!ファイルにアクセスしようとするサンドボックスアプリケーションを考えてみてください。サンドボックスはファイル名文字列をテストするかもしれませんが、攻撃者がミュータブル Stringを送信し、セキュリティチェックの後、I / Oの前にミュータブルを変更できたらどうでしょうか?私は、このタイプのシナリオがJavaにStringマークされている理由であると信じておりfinal(に似ていますsealed)、同じロジックがC#の決定を支えていると思います。
ダリエン

23

sealedクラスの作成者が継承されるように設計していないことを示すために使用されます それ以下でも、それ以上でもありません。

多くのプログラマーにとって、インターフェースを適切に設計することはすでに困難です。潜在的に継承できるクラスを適切に設計すると、コードの難易度と行が追加されます。記述されたコード行が増えると、維持するコードが増えます。この困難さの増大を避けるためにsealed、クラスが継承されることを意図していないことを示すことができます。このコンテキストでは、クラスの封印は問題に対する完全に有効な解決策です

クラスCustomBoeing787がから派生した場合を想像してくださいAirliner。これCustomBoeing787Airliner、からの一部の保護されたメソッドをオーバーライドしますが、すべてではありません。開発者に十分な時間があり、それだけの価値がある場合、クラスを単体テストして、オーバーライドされていないメソッドが呼び出されるコンテキスト(たとえば、オブジェクトの状態を変更する保護されたセッター)でも、すべてが期待どおりに動作することを確認できます。同じ開発者が(1)時間がない場合、(2)上司/顧客にさらに数百または数千ドルを費やすことを望まない場合、または(3)追加のコードを記述したくない場合今すぐ必要(YAGNI)、それから彼は:

  • 潜在的なバグを気にするためにこのプロジェクトを後で維持するプログラマの懸念であることを考慮して、コードをそのままリリースするか、

  • またはsealed、このクラスが継承されることを意図していないことを明示的に将来のプログラマーに示すようにクラスをマークします。

可能な限り多くのクラスを封印するのは良い考えですか、それともできるだけ少なくするのですか?私の経験から、決定的な答えはありません。一部の開発者は、使いsealedすぎる傾向があります。他の人は、クラスがどのように継承されるか、およびクラスが引き起こす可能性のある問題を気にしません。そのような使用法を考えると、問題は、プログラマーがインデントに2つまたは4つのスペースを使用すべきかどうかを判断することにある問題に似ています。正しい答えはありません。


わかりました。もう一度アグレッシブに聞こえたら申し訳ありませんが、ここで明確かつ断定的になりたいです... tl;drここに書く場合は、2つの方法のいずれかであなたが書いたものしか理解できません。それはどちらかだ「いいえ、単一の良い理由がない」、または「私が正当な理由がないと思うが、私も知りません。」。私にとって「決定的な答えはありません」はそれらのいずれかです。
クレゴックス

6
sealedクラスの作成者が継承されるように設計していないことを示すために使用されます。それが唯一の正当な理由です。
ロバートハーベイ

それは、明かりのない自転車をあなたに与えて強制するのと同じ理由です。どういうわけか、明かりを追加することはできません。
クレゴックス

2
@Cawasどうして?その文脈では、自転車メーカーが自転車にライトがないことを確認すること重要ではありませんが、多くの場合、タイプのユーザーが実装が特定の正確な実装であることを確認すること重要です。必要な制約を実施します。場合によっては、実際に必要ではない制約を追加することがあります。その例を提供しました。制約が1つの場所で誤って使用されているからといって、決して制約をかけるべきではないという意味ではありません。
セルビー

1
また、セキュリティはそれを使用する理由です!ファイルにアクセスしようとするサンドボックスアプリケーションを考えてみてください。サンドボックスはファイル名文字列をテストするかもしれませんが、攻撃者がミュータブル Stringを送信し、セキュリティチェックの後、I / Oの前にミュータブルを変更できたらどうでしょうか?私は、このタイプのシナリオがJavaにStringマークされている理由であると信じておりfinal(に似ていますsealed)、同じロジックがC#の決定を支えていると思います。
ダリエン

4

クラスがシールされると、そのタイプを使用するコードは、クラスが処理していることを正確に知ることができます。

C#は今でstringありsealed、あなたはそれを継承することはできませんが、聞かせてのは、それはそうではなかったことを第二のために想定しています。もしそうなら、誰かがこのようなクラスを書くことができます:

public class EvilString// : String
{
    public override string ToString()
    {
        throw new Exception("I'm mean");
    }
    public override bool Equals(object obj)
    {
        return false;
    }
    public override int GetHashCode()
    {
        return 0;
    }
}

これは、デザイナーstringが真実であると知っていたあらゆる種類のプロパティに違反している文字列クラスになります。彼らはEqualsメソッドが推移的であることを知っていましたが、そうではありません。ちなみに、このequalsメソッドは対称ではありません。文字列はそれに等しいかもしれませんが、その文字列には等しくないでしょう。ToStringメソッドがstringnull以外の値に対して例外をスローしないという仮定のもとでコードを書いたプログラマーがいるはずです。これで、その仮定に違反することができます。

これを行うことができる他のクラスがいくつもあり、違反する可能性のある他のあらゆる種類の仮定があります。言語機能を削除した場合sealed次に、言語設計者は、すべてのライブラリコードでこのようなことの会計処理を開始する必要があります。使用したいタイプの拡張タイプが「適切に」書かれていることを確認するために、あらゆる種類のチェックを実行する必要があります。これは非常に費用のかかる作業です(開発時でも実行時でも)。クラスを封印できることにより、これは不可能であり、意図的に拡張するように設計されたタイプを除き、独自のタイプの実装の詳細についての主張に違反することはありません。拡張するように設計されたタイプの場合、言語コードはそれらをどのように扱うかについてはるかに防御的である必要があることがわかります。


しかし、あなたはあなたのコードのその仮定違反することができます。Stringのソースを取得して編集しているわけではありません。設計者は、リーフ1つ、ブランチ1つ、クライアント1人が独自のリスクと裁量でこれを使用しているため、これを考慮する必要はありません。他のクライアントがそうすることを好まない限り、そこから広まりません。等々。
クレゴックス

3
@Cawasそして、予期しない時間に例外がスローされ、結果としてリソースがリークした場合、またはクラスが不確定な状態のままになり、不適切に機能する場合は?このケースは少しばかげていますが、誰かが賢明だと思う実装を書いたとしても、特定の値では動作しない、または動作しない場合があります。言語コードが機能しない場合、彼らは文句を言います。言語がサポートできないことを人々にさせないことが望ましいです。
セルビー

いずれにせよ、コンパイラの構築について話している。sealed私たちが使用するために公開されていない多くの機能が組み込まれています。なぜそんなに狭い範囲外でそれを使用するのかについてはまだ説明しておらず、コンパイラコードと同じくらい狭いので、sealed存在さえも説明していません。
クレゴックス

2
@Cawas string型はコンパイラコードではありません。これは言語コードの一部です。全然違う。いずれにせよ、私は1つの例を選んだだけです。BCL全体でほとんどのシールドクラスを選択し、それを例として使用できます。
セルビー

4
@Cawas:ここで不足しているのは、カスタマーサポートの側面だと思います。クラスを継承可能にすると、顧客はそのクラスを継承し、その後、クラスが壊れたときに電話をかけます。彼らは自分のリスクでそのクラスから継承することを一日中伝えることができますが、あなたはそれを継承することを許可したので、あなたはまだ呼び出しを受けるでしょう、彼らはそうすることが安全であると仮定します。
ロバートハーベイ

3

シールを使用する正当な理由はありますか?

え?しばしばあるわけではない。私は、私は不変のタイプ(または他のいくつかの制限)がある場合にのみ密封され使用されます本当に 本当に不変のままにする必要がありますをし、私が信頼相続は、システムに微妙な、catastropicバグを導入することなく、その要件をfurfillすることはできません。

シールを使用する正当な理由があり、それをすべてのデフォルトとして使用する必要があると仮定します。継承で何をしますか?

デフォルトではないものには継承を使用します。たとえ合成が継承よりも好まれたとしても(そうであるとしても)、それは継承が破棄されることを意味するものではありません。継承には、設計とコードの保守性に導入されるいくつかの固有の問題があり、コンポジションは(一般的に)問題がほとんどなく同じことをほとんど行います。そして、それは、構成が好まれても、継承が問題の特定のサブセットに適していることを意味します。

意地悪すぎるつもりはありませんが、SOLIDと単体テストについて聞いたことがあれば、岩の下から抜け出して実際にコードを書いてください。物事は明確ではなく、「常にXを実行する」、「Yを使用しない」、「Zが最適」が正しい方法になることはめったにありません(質問の意見に基づいて引き寄せられるようです)。コードを書くsealed、他のトレードオフと同様に、良い場合と悲しみを引き起こす場合があります


私にとって、これと、ロバートが詳しく説明したくない「クライアントへの継承をサポートする必要がないようにすること」が最も有望な答えです...使用するケースsealedについて詳しく説明してください。
クレゴックス

@Cawas-うまくいくかどうかわかりません。私はそれを行うことはめったにありませんが、何かが不変であるという保証がパフォーマンスの最適化、設計の簡素化のためにオブジェクトから継承できない価値があるというトレードオフです。
テラスティン

カッコいい。ダンクはすでにそれをダンクしました!:P答えてくれてありがとう。そして、私の専門知識についての「微妙なヒント」を手にしたのはあなただけでした。人々は私がもっと知っていると思っていたようですが、私は知りません... たぶん私はおそらく私がそうするべきであるほど手続き的にではなく、何らかの方法でそれらの概念を適用するでしょう。
クレゴックス

3

まず、Microsoftがクラスを封印する理由に関するEric Lippertの投稿を読んでください。

http://blogs.msdn.com/b/ericlippert/archive/2004/01/22/61803.aspx

次に、sealedキーワードは、それが行うことを正確に行うために存在します-継承を防ぎます。継承を防ぎたい状況はたくさんあります。コレクションを持っているクラスを考えてください。コードが特定の方法で動作するそのクラスに依存している場合、誰かがそのクラスのメソッドまたはプロパティの1つをオーバーライドして、アプリケーションが失敗することは望ましくありません。

第三に、sealedを使用すると、継承よりも合成が促進されます。継承よりも合成を使用するということは、リスコフの置換原則を破ることができず、オープン/クローズの原則に従っていることを意味します。 sealedしたがって、ooプログラミングの5つの最も重要な原則のうちの2つに従うことを支援するキーワードです。私はそれが非常に貴重な機能になると思います。


最初の部分はすでに質問のコメントで指摘されています。第二部は、本当の因果関係のない単なる推論です。第三の部分は、すでに他の場所に近づいていても、何かを持っているかもしれません。私はそのことに焦点を当てます。
クレゴックス

3番目のポイントは、継承よりも合成を推奨しません。オプションとしての継承を完全に排除します。たとえそれが全体的な最良の選択であったとしてもです。
マイケルショー

コードベースを制御するかどうかによって異なります。クラスを封印すると、必要に応じて後でいつでも封印を解除できます。
スティーブン

2

この質問には客観的な答えはなく、すべてあなたの視点に依存していると思います。

一方では、一部の人々はすべてを「オープン」にしたいと考えており、すべてが正しく機能していることを確認する負担は、クラスを拡張する人にあります。これが、クラスがデフォルトで継承に対して開かれている理由です。そして、なぜJavaメソッドはデフォルトですべて仮想であるのか。

一方、デフォルトではすべてを閉じて、それを開くためのオプションを提供したい人もいます。この場合、基本クラスの作成者は、「オープン」にするすべてのものを、考えられるあらゆる方法で安全に使用する必要があります。これが、C#でデフォルトですべてのメソッドが非仮想であり、オーバーライドされるように仮想と宣言する必要がある理由です。また、これらの人々のために、「オープン」デフォルトを回避する方法である必要があります。クラスをシール/ファイナルにするようなものです。クラスから派生した誰かが自分のクラスの設計に大きな問題を抱えているからです。


これはもっと似ています!これは正当な理由かもしれません...「人々はそれを閉じたいので」。また、質問で私が言おうとしていることもあります。C++の非仮想クラスはオーバーライドできるように見えます。sealedできません。それらからどのように継承できますか?私が読んだのは、できないことだけです。今まで。知りません。私は最初に封印されたことを聞いてから、このテーマについて4か月間行ったり来たりしています。そして、封印されたクラスで何かを変更する必要があるとき、私はまだそれを正しく行う方法を知りません。そのため、「開くオプション」がありません!
クレゴックス

4
これが、C ++クラスメソッドがデフォルトで仮想ではない理由ではありません。メソッドを仮想化すると、メソッドルックアップテーブルに追加のオーバーヘッドが必要になり、C ++には、機能が必要なときにのみオーバーヘッドを支払うという哲学があります。
マイケルショー

@Ptolemy OK、削除しました。私は人々がそれについて文句を言うことを知っていたので、私はそれを書かなかったはずです。
陶酔

ねえ、私は完全にあなたは、人々は、デフォルトでは、閉じたクラスを作成し、積極的にそれらを開くように選択する必要がしたいことを主張したいならば、あなたはストローでクラッチに持ってしようとしていることを理解する
マイケル・ショー

IMOは、APIに関しては、プライベート/保護/パブリックの議論と非常によく似ています:クラスを制御するのは誰か、クラスを継承したいのは誰か、2つの間の通信、新しいバージョンが出る頻度に大きく依存します。
ダリエン

-2

C#で封印されている問題は、かなり最終的なことです。7年間のC#商用開発で​​使用した唯一の場所は、Microsoft .NETクラスです。フレームワーククラスを拡張して調整された動作を実装する正当な理由があり、クラスが封印されていたため、 。

使用可能なすべての方法を考えてクラスを作成することは不可能であり、それらのいずれも正当なものではないと判断することはできません。同様に、フレームワークのセキュリティが継承されないクラスに依存している場合、セキュリティ設計の欠陥があります。

ですから、結論として、封印されたものは有用な目的には役立たないという見解をしっかりと持っています。

これまでのところ、封印された場所を正当化する3つの議論があったことがわかります。

  1. 引数1は、人々がクラスを継承し、実際に内部を正しく理解せずにそのクラスの内部へのアクセスを増やしたときにバグの原因を排除することです。

  2. 引数2は、Microsoftクラスを拡張し、Microsoftがそのクラスにバグ修正を実装したが、そのバグに依存しているエクステンションがコードが壊れている場合、Microsoftは非難を取得し、それを防ぐことができます

  3. 引数3は、クラスの開発者として、クラスの設計の一環として、クラスを拡張できるようにするかどうかの選択です。

引数1は、エンドプログラマが私のコードの使用方法を知らないという引数のようです。そうかもしれませんが、オブジェクトインターフェイスへのアクセスを許可した場合でも、オブジェクトを使用し、使用方法を理解せずに呼び出しを行うことは事実です。 。これは、封印の必要性の弱い正当化です。

arg 2の事実ベースはどこから来たのか理解しています。特に、Microsoftがwin32 api呼び出しに追加のOSチェックを行って、不正な呼び出しを特定すると、Windows NTと比較してWindows XPの一般的な安定性が向上しますが、短期的にはWindows XPのリリース時に多くのアプリが破損しました。マイクロソフトは、これらのチェックに「ルール」を入れることでこれを解決しました。アプリXがこのルールに違反する場合は無視します。これは既知のバグです

引数2は貧弱な引数です。なぜなら、。壊れた動作に依存する回避策が壊れていることを意味します。継承を使用してこの問題を回避した場合でも、他の追加ラッパーコードを使用した場合でも、コードが修正されると、回避策は無効になります。

引数3は、自己を正当化する実質を持つ唯一の引数です。しかし、まだ弱いです。これは、コードの再利用に反するだけです。


2
/ meはMicrosoftにメールを送信します。クラスXの封印を解除して、.NETの新しいリリースを出荷してください。よろしくお願いします。..先ほど言ったように、商業開発の中で封印されたクラスを見たのはそれだけで、封印を解除することはできませんでした。
マイケルショー

6
It is impossible to write a class thinking about every possible way it could be used-それがまさに多くのフレームワーククラスが封印されている理由です...誰かがクラスを拡張する可能性のあるあらゆる方法について考える必要がなくなります。
ロバートハーベイ

5
封印を解除できた場合、封印してもあまり意味がありませんか?
ロバートハーベイ

2
@Ptolemy人々があなたのクラスを拡張できるようにすることは機能です。開発に多大な労力を費やすもの。これは、クラスの作成者が潜在的な拡張機能で努力を正当化するのに十分な利益を見ることができる場合にのみ正当化する価値がある機能です。彼らが努力を正当化できず、したがって、タイプとそのユーザーが拡張機能をサポートするように構築されていない場合、それを防ぐことが重要です。そうしないと、技術的にタイプを拡張することができますが、あなたはそうします。
セルビー

2
@Cawas:あなたは難しいだけです。
ロバートハーベイ


-8

私の質問から明らかなように、私はここの専門家ではありません。しかし、私は存在する理由sealedさえ見当たりません。

コードアーキテクトがインターフェイスを設計するのを支援するために作成されたようです。野生に出て、多くの人がそれを継承するクラスを書きたい場合、そのクラス内の何かを変更すると、あまりにも多くの人のためにそれを壊す可能性があります。だから私たちはそれを封印することができ、誰も継承することはできません。"問題が解決しました"。

まあ、「スポットをカバーし、別のものを発見する」ように見えます。そして、それは問題を解決することすらしていません-それはただツールを継承することです継承です。他のツールと同様に、多くの使用法があり、不安定なクラスから継承することはリスクです。そのリスクを負った人は誰でもよく知っているはずです。

キーワードがあるかもしれないstableので、人々はそれを継承する方が安全だと知っているでしょう。


2
「問題は解決しました」-まさに。
ロバートハーベイ

@RobertHarvey次の段落で説明するように、解決していません。
クレゴックス

8
クラスを拡張できないため、あなたはただ怒っています。それはsealed、フレームワーク設計者にとって有用なツールではないという意味ではありません。フレームワークのクラスはブラックボックスにする必要があります。クラスを適切に使用するために、クラスの内部について知る必要はありません。しかし、それはまさに継承が要求するものです。
ロバートハーベイ

役に立たないとは言いませんでした。私はその使用法が見当たらないと言い、理由を指摘します。お願いします、それを使う正当な理由を教えてください!「閉じたデザインパターンにしたいから」だけでも構いません。その理由を明確に示している人は誰もいません。
クレゴックス

7
正当な理由は、封印されたクラスで継承をサポートする必要がないことです。
ロバートハーベイ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.