実際に開閉原理を遵守する方法


14

私は、オープンクローズド原則の意図を理解しています。変更せずに拡張しようとするように指示することで、変更中に既に機能しているものを壊すリスクを減らすことを目的としています。

しかし、この原則が実際にどのように適用されるかを理解するのに苦労しました。私の理解では、それを適用するには2つの方法があります。変更前と変更後:

  1. 前:できるだけ抽象化して「未来を予測する」プログラムを作成します。たとえば、将来システムにsが追加されたdrive(Car car)場合、メソッドは変更する必要 Motorcycleがあるため、おそらくOCPに違反します。ただし、この方法drive(MotorVehicle vehicle)は将来変更する必要性が低いため、OCPに準拠しています。

    ただし、将来を予測し、システムにどのような変更が加えられるかを事前に知ることは非常に困難です。

  2. 変更後:変更が必要な場合、現在のコードを変更する代わりにクラスを拡張します。

練習#1を理解するのは難しくありません。しかし、適用方法を理解するのに苦労しているのは実践#2です。

例(YouTubeのビデオから取得しました):CreditCardオブジェクトを受け入れるクラスにメソッドがあるとします:makePayment(CraditCard card)。1日Voucherがシステムに追加されます。このメソッドはそれらをサポートしていないため、変更する必要があります。

そもそもメソッドを実装するとき、未来とプログラムをより抽象的な用語で予測することに失敗しました(たとえばmakePayment(Payment pay)、今、既存のコードを変更する必要があります)。

練習#2では、変更するのではなく拡張することで機能を追加する必要があります。どういう意味ですか?既存のコードを単に変更するのではなく、既存のクラスをサブクラス化する必要がありますか?コードの書き換えを避けるために、何らかのラッパーを作成する必要がありますか?

または、原則は「機能を正しく変更/追加する方法」に言及するのではなく、「最初に変更を加える必要がないようにする方法」(つまり、プログラムを抽象化する方法)に言及していますか?



1
Open / Closed Principleは、使用するメカニズムを決定するものではありません。通常、継承は間違った選択です。また、将来のすべての変更から保護することは不可能です。将来を予測しようとしないことが最善ですが、変更が必要になったら、同じ種類の将来の変更に対応できるように設計を変更します。
ドーバル

回答:


14

設計原則は、常に相互にバランスを取る必要があります。あなたは未来を予測することはできません、そしてほとんどのプログラマーは彼らがしようとすると恐ろしくそれをします。そのため、3つルールがあります。これは主に複製に関するものですが、他の設計原則のリファクタリングにも適用されます。

インターフェイスの実装が1つだけの場合、拡張がどこで行われるかが明確でない限り、OCPについてあまり気にする必要はありません。実際、このような状況で過剰に設計しようとすると、しばしば明確さが失われます。一度拡張すると、それが最も簡単で明確な方法であれば、リファクタリングしてOCPフレンドリーにします。3番目の実装に拡張するときは、多少の努力が必要な場合でも、OCPを考慮してリファクタリングするようにしてください。

実際には、実装が2つしかない場合、3つ目を追加するときのリファクタリングは通常それほど難しくありません。維持するのが面倒になるのは、そのポイントを超えて成長させるときです。


1
回答ありがとうございます。あなたが言っていることを理解しているかどうかを見てみましょう:あなたが言っていることは、クラスを変更することを余儀なくされたに主 OCPを気にする必要があるということです。意味:初めてクラスを実装するとき、将来を予測するのは難しいので、OCPについてあまり心配するべきではありません。初めて拡張/変更する必要があるときは、将来的に柔軟性を高めるために少しリファクタリングすることをお勧めします(OCPを追加)。そして、クラスを3回拡張/変更する必要がある場合は、OCPに準拠するようにリファクタリングを行う必要があります。これはどういう意味ですか?
アビブコーン

1
それがアイデアです。
カールビーレフェルト

2

あなたは未来を見過ぎていると思います。オープン/クローズに準拠した柔軟な方法で現在の問題を解決します。

drive(Car car)メソッドを実装する必要があるとしましょう。言語に応じて、いくつかのオプションがあります。

  • オーバーロード(C ++)をサポートする言語の場合は、 drive(const Car& car)

    後で必要になるかもしれませんがdrive(const Motorcycle& motorcycle)、それは干渉しませんdrive(const Car& car)。問題ない!

  • オーバーロードをサポートしない言語(Objective C)の場合、メソッドに型名を含めます-driveCar:(Car *)car

    ある時点でが必要になる可能性がありますが-driveMotorcycle:(Motorcycle *)motorcycle、再度、干渉しません。

これによりdrive(Car car)、修正することはできませんが、他の車両タイプに拡張することはできます。このミニマリストの将来計画により、今日の仕事を終わらせることができますが、将来自分自身をブロックすることはありません。

必要な最も基本的なタイプを想像しようとすると、無限回帰につながる可能性があります。セグエ、自転車、またはジャンボジェットを運転する場合はどうなりますか。人々が移動してモビリティに使用するすべてのデバイスを説明できる単一の汎用抽象タイプをどのように構築しますか?


クラスを変更して新しいメソッドを追加すると、Open-Closed Principleに違反します。また、あなたの提案は、リスコフ代替原理を運転可能なすべての車両に適用する能力を排除し、オブジェクト指向の最も強力な部分の1つを本質的に排除します。
ダンク

@Dunk私は、厳密なMeyerのオープン/クローズの原則ではなく、ポリモーフィックなオープン/クローズの原則に基づいて答えを出しました。新しいインターフェイスをサポートするためにクラスを更新することは許可されています。この例では、車のインターフェイスはオートバイのインターフェイスとは別にされています。これらは、実装クラスがサポートできる自動車とオートバイの個別の運転抽象クラスとして形式化できます。
ジェフリートーマス

@Dunk Liskov-Substitution Principleは便利ですが、無料ではありません。元の仕様が自動車のみを必要とする場合、より一般的な自動車を作成することは、追加の費用、時間、および複雑さの価値がないかもしれません。さらに、より一般的な車両が計画外のサブクラスの処理に完全に適しているとは考えられません。オートバイのインターフェイスを車両インターフェイス(車のみを処理するように設計されている)にシューホーンを付けるか、オートバイを処理するように車両を変更する必要があります(実際の開閉の違反)。
ジェフリートーマス

Liskov-Substitutionの原則は無料ではありませんが、コストもあまりかかりません。また、通常、メインアプリケーションで別のサブクラスが継承されない場合でも、これまでに何度もコストがかかるよりもはるかに多くの返済が行われます。LSPを適用すると、自動テストがはるかに簡単になります。これはすでに成功しています。また、あなたがアプリケーションを構築していて、将来の改訂でそれが必要になる可能性が高いと感じていない場合は、絶対に独り占めしてすべてがLSPを必要とするとは思わないはずですアプリケーションまたはそのドメインについて十分に理解してください。
ダンク

1
OCPの定義に関して。私が働いていた業界かもしれませんが、通常の営利会社よりも高いレベルの検証を必要とする傾向がありますが、一般的にファイル/クラスが変更された場合は、ファイル/クラスを再テストするだけでなく、回帰テストでそのファイル/クラスを使用します。したがって、誰かが多態性のオープン/クローズは問題​​ないと言っても問題ではありません。
ダンク

2

私は、オープンクローズド原則の意図を理解しています。変更せずに拡張しようとするように指示することで、変更中に既に機能しているものを壊すリスクを減らすことを目的としています。

また、既存のオブジェクトの動作を変更しないことにより、そのメソッドに依存するすべてのオブジェクトを壊さないことも重要です。オブジェクトが振る舞いの変更をアドバタイズすると、他のオブジェクトがその振る舞いを期待していることを正確に知らずに、オブジェクトの既知の予期される振る舞いを変更するため、リスクが生じます。

どういう意味ですか?既存のコードを単に変更するのではなく、既存のクラスをサブクラス化する必要がありますか?

うん。

「クレジットカードのみを受け入れる」は、そのパブリックインターフェイスを介して、そのクラスの動作の一部として定義されます。プログラマは、このオブジェクトのメソッドはクレジットカードのみを使用することを世界に宣言しています。彼女はちょっと明確なメソッド名ではなく、それを使ってそれをやった。システムの残りの部分はこれに依存しています。

当時は理にかなっていたかもしれませんが、今すぐ変更する必要がある場合は、クレジットカード以外のものを受け入れる新しいクラスを作成する必要があります。

新しい動作=新しいクラス

余談として -未来を予測する良い方法は、あなたが方法を与えた名前を考えることです。makePaymentのような本当に一般的なサウンドメソッド名を、メソッド内の特定のルールを使用して、正確にどのような支払いができるかについて指定しましたか?それはコード臭です。特定のルールがある場合、これらはメソッド名から明確にする必要があります-makePaymentはmakeCreditCardPaymentである必要があります。初めてオブジェクトを作成するときにこれを行います。他のプログラマーがそのオブジェクトに感謝​​します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.