タグ付けされた質問 「solid」

一連の設計原則のニーモニック:単一責任、オープンクローズ、リスコフ置換、インターフェース分離、依存関係逆転

5
インターフェイス(OOP)のセマンティックコントラクトは、関数シグネチャ(FP)よりも有益ですか?
いくつかのことで言われて、あなたはそれらの極端にSOLID原則を取る場合、あなたは関数型プログラミングで終わります。この記事に同意しますが、インターフェイス/オブジェクトから関数/クロージャへの移行でいくつかのセマンティクスが失われると思うので、関数型プログラミングがどのように損失を軽減できるかを知りたいと思います。 記事から: さらに、インターフェイス分離の原則(ISP)を厳密に適用すると、ヘッダーインターフェイスよりもロールインターフェイスを優先する必要があることを理解できます。 デザインをどんどん小さなインターフェースに向けていくと、最終的には究極のロールインターフェース、つまり単一のメソッドを持つインターフェースに到達します。これは私によく起こります。以下に例を示します。 public interface IMessageQuery { string Read(int id); } に依存する場合IMessageQuery、暗黙の契約の一部は、呼び出しRead(id)が特定のIDのメッセージを検索して返すことです。 これを、同等の機能シグネチャである依存関係と比較してください int -> string。追加の手がかりがなければ、この関数は単純なものになりToString()ます。を実装IMessageQuery.Read(int id)したToString()場合、意図的に破壊されたと非難する可能性があります! それでは、機能的なプログラマは、名前の良いインターフェイスのセマンティクスを保持するために何ができるでしょうか?たとえば、単一のメンバーでレコードタイプを作成することは一般的ですか? type MessageQuery = { Read: int -> string }

5
Cでインターフェイス分離の原則を適用する方法は?
「M」と言うモジュールがあり、「C1」、「C2」、「C3」と言うクライアントがいくつかあります。モジュールMの名前空間、つまり、それが公開するAPIとデータの宣言を、次のような方法でヘッダーファイルに割り当てます。 どのクライアントでも、必要なデータとAPIのみが表示されます。モジュールの残りの名前空間はクライアントから隠されています。つまり、インターフェース分離の原則に準拠しています。 宣言は複数のヘッダーファイルで繰り返されません。つまり、DRYに違反しません。 モジュールMは、クライアントに依存しません。 クライアントは、モジュールMで使用されていない部分で行われた変更の影響を受けません。 既存のクライアントは、追加のクライアント(または削除)の影響を受けません。 現在、クライアントの要件に応じてモジュールの名前空間を分割することでこれに対処しています。たとえば、下の画像では、3つのクライアントに必要なモジュールの名前空間のさまざまな部分が示されています。クライアントの要件は重複しています。モジュールの名前空間は、「a」、「1」、「2」、「3」の 4つの独立したヘッダーファイルに分割されます。 ただし、これは前述の要件の一部、つまりR3およびR5に違反します。このパーティション化はクライアントの性質に依存するため、要件3に違反しています。また、新規クライアントの追加のモジュールの名前空間は、現在7つのヘッダファイルに分割して、新しいクライアントの追加と同様に、上記の画像の右側に見ることができ、このパーティションの変更および要件5.に違反し、 - 「 '、' b '、' c '、' 1 '、' 2 * '、' 3 * 'および' 4 '。ヘッダーファイルは2つの古いクライアントの変更を意味し、それにより再構築がトリガーされます。 Cのインターフェイス分離を非自発的な方法で実現する方法はありますか? はいの場合、上記の例をどのように扱いますか? 私が想像する非現実的な仮想ソリューションは次のようになります- モジュールには、名前空間全体をカバーする1つの太いヘッダーファイルがあります。このヘッダーファイルは、ウィキペディアページのようなアドレス可能なセクションとサブセクションに分かれています。各クライアントには、特定のヘッダーファイルが用意されています。クライアント固有のヘッダーファイルは、ファットヘッダーファイルのセクション/サブセクションへのハイパーリンクの単なるリストです。また、ビルドシステムは、モジュールのヘッダーでポイントするセクションのいずれかが変更された場合、クライアント固有のヘッダーファイルを「変更された」ものとして認識する必要があります。
15 c  interfaces  solid 

4
テスト駆動開発では、SOLIDに従う必要がありますか?
TDDの実務者から、TDDの利点の1つは開発者にSOLID原則(単一責任、オープンクローズ、Liskov置換、インターフェイス分離、依存関係反転)を強制することであると多くのことを聞きます。しかし、私にとっては、いくつかのテスト(主に単体テスト)を記述するだけで十分であり、SOLIDに従うことが重要です(したがって、テスト可能なアーキテクチャを作成します)。 TDDは、開発者に単体テストを書くよりも積極的にSOLIDをフォローするように強制しますか?

3
RxJavaクラスFlowableは、正当に460個のメソッドを持つことができますか?
JavaのReactiveX (RxおよびReactive Extensionsとも呼ばれます)の実装であるRxJavaを始めたばかりです。本当に私を襲った何かがの巨大な大きさだったRxJavaのフロアブルクラス:それは460個のメソッドを持っています! 公平であるために: オーバーロードされたメソッドが多数あり、メソッドの総数が大幅に増えます。 おそらくこのクラスは分割されるべきですが、RxJavaに関する私の知識と理解は非常に限られています。RxJavaを作成した人々は確かに非常にスマートであり、それらは作成するために選択するための有効な引数を、おそらくを提供することができますフロアブル剤を非常に多くの方法で。 一方: RxJavaはのJava実装であるMicrosoftの反応性の拡張機能、およびそれがさえ持っていないフロアブルクラスを、ので、これは盲目的に既存のクラスを移植し、Javaでそれを実施する場合ではありません。 [ 更新:イタリック体の前のポイントは事実正しくありません。マイクロソフトの観察可能な 400以上のメソッドを持つクラスは、RxJavaのための基礎として使用された観察可能なクラス、および流動性は、に似て観察可能な大量のデータのためではなく、ハンドル背圧。そのため、RxJavaチームは既存のクラスを移植していました。この投稿は、元のデザインに挑戦されている必要があり、観察可能マイクロソフトによってクラスではなく、RxJavaのフロアブルクラスを。] RxJavaはわずか3年以上前であるため、これは(Javaの初期リリースの場合のように)優れた(SOLID)クラス設計原則に関する知識が不足しているためにコードが誤って設計されている例ではありません。 Flowableほどの大きさのクラスでは、その設計は本質的に間違っているように見えますが、そうではないかもしれません。このSEの質問に対する1つの答えクラスメソッドの数の制限は何ですか?答えは「必要な数のメソッドを用意する」ことです。 明らかに、言語に関係なくそれらをサポートするためにかなりの数のメソッドを必要とするクラスがいくつかあります。それらは簡単に小さなものに分解されず、かなりの数の特性と属性を持っているからです。例:文字列、色、スプレッドシートセル、データベース結果セット、HTTPリクエスト。これらのことを表現するためのクラス用のメソッドをおそらく数十個用意することは、理不尽に思えません。 しかし、Flowableには460個のメソッドが本当に必要なのでしょうか、それとも非常に巨大なために、必ずしもクラスの設計が悪い例ですか? [明確にするには:この質問は、具体的RxJavaのに関し、フロアブルクラスではなく、神は、一般的にオブジェクト。]

2
現実の世界-リスコフ代替原理
背景:私はメッセージングフレームワークを開発しています。このフレームワークでは次が可能になります。 サービスバスを介したメッセージの送信 メッセージバス上のキューにサブスクライブする メッセージバスのトピックを購読する 現在RabbitMQを使用していますが、近い将来にMicrosoft Service Bus(オンプレミス)に移行することを知っています。 インターフェイスと実装のセットを作成して、ServiceBusに移行するときに、クライアントコード(パブリッシャーまたはサブスクライバー)を修正せずに新しい実装を提供するだけでよいようにします。 ここでの問題は、RabbitMQとServiceBusが直接翻訳できないことです。たとえば、RabbitMQはExchangeとトピック名に依存していますが、ServiceBusは名前空間とキューに関するものです。また、ServiceBusクライアントとRabbitMQクライアントの間に共通のインターフェイスはありません(たとえば、両方にIConnectionがある場合がありますが、インターフェイスは異なります-共通の名前空間からではありません)。 そのため、次のようにインターフェイスを作成できます。 public interface IMessageReceiver{ void AddSubscription(ISubscription subscriptionDetails) } 2つのテクノロジーの翻訳不可能な特性により、上記のインターフェースのServiceBusとRabbitMQの実装には異なる要件があります。したがって、IMessageReceiverのRabbitMq実装は次のようになります。 public void AddSubscription(ISubscription subscriptionDetails){ if(!subscriptionDetails is RabbitMqSubscriptionDetails){ // I have a problem! } } 私にとって、上記の行は、リスコフの代替可能性の規則を破ります。 SubscriptionがIMessageConnectionを受け入れるように、これを反転させることを検討しましたが、RabbitMq SubscriptionにはRabbitMQMessageConnectionの特定のプロパティが必要になります。 だから、私の質問は: これがLSPに違反することを修正できますか? 場合によっては避けられないこと、または何かが足りないことに同意しますか? うまくいけば、これは明確で話題になっています!

4
単一責任パターンはクラスに対してどの程度具体的である必要がありますか?
たとえば、コンソールとの間のあらゆる種類の入出力メソッドを備えたコンソールゲームプログラムがあるとします。でしょうが、すべてのシングルでそれらを保つためにスマートにinputOutputクラスなど、より具体的なクラスにそれらを打破startMenuIO、inGameIO、playerIO、gameBoardIO各クラスが1-5程度のメソッドを持っていることなど、? また、同じメモで、それらを分解する方が良い場合は、IO名前空間に配置して、呼び出しをもう少し冗長にするのが賢明でしょうIO.inGameか?

3
実際に開閉原理を遵守する方法
私は、オープンクローズド原則の意図を理解しています。変更せずに拡張しようとするように指示することで、変更中に既に機能しているものを壊すリスクを減らすことを目的としています。 しかし、この原則が実際にどのように適用されるかを理解するのに苦労しました。私の理解では、それを適用するには2つの方法があります。変更前と変更後: 前:できるだけ抽象化して「未来を予測する」プログラムを作成します。たとえば、将来システムにsが追加されたdrive(Car car)場合、メソッドは変更する必要 Motorcycleがあるため、おそらくOCPに違反します。ただし、この方法drive(MotorVehicle vehicle)は将来変更する必要性が低いため、OCPに準拠しています。 ただし、将来を予測し、システムにどのような変更が加えられるかを事前に知ることは非常に困難です。 変更後:変更が必要な場合、現在のコードを変更する代わりにクラスを拡張します。 練習#1を理解するのは難しくありません。しかし、適用方法を理解するのに苦労しているのは実践#2です。 例(YouTubeのビデオから取得しました):CreditCardオブジェクトを受け入れるクラスにメソッドがあるとします:makePayment(CraditCard card)。1日Voucherがシステムに追加されます。このメソッドはそれらをサポートしていないため、変更する必要があります。 そもそもメソッドを実装するとき、未来とプログラムをより抽象的な用語で予測することに失敗しました(たとえばmakePayment(Payment pay)、今、既存のコードを変更する必要があります)。 練習#2では、変更するのではなく拡張することで機能を追加する必要があります。どういう意味ですか?既存のコードを単に変更するのではなく、既存のクラスをサブクラス化する必要がありますか?コードの書き換えを避けるために、何らかのラッパーを作成する必要がありますか? または、原則は「機能を正しく変更/追加する方法」に言及するのではなく、「最初に変更を加える必要がないようにする方法」(つまり、プログラムを抽象化する方法)に言及していますか?

3
インターフェイス分離の原則の2つの矛盾する定義–どちらが正しいですか?
ISPに関する記事を読むとき、ISPには2つの矛盾する定義があるようです。 最初の定義(参照によると1、2、3)、ISPは、インターフェイスを実装するクラスは、彼らが必要としない機能を実装することを強制されるべきではないと述べています。したがって、太いインターフェイスIFat interface IFat { void A(); void B(); void C(); void D(); } class MyClass: IFat { ... } より小さなインターフェースに分割しISmall_1、ISmall_2 interface ISmall_1 { void A(); void B(); } interface ISmall_2 { void C(); void D(); } class MyClass:ISmall_2 { ... } このよう以来、私のMyClass唯一の方法、それが必要とする(実装することが可能であるD()とのC()またのためのダミー実装を提供することを余儀なくされることなく、) A()、B()およびC(): しかし、2番目の定義に従った(参照1、 2は、ナザールMerzaによって答えは、ISPはと述べている)MyClient上のメソッドを呼び出しますMyService上の方法を知っておくべきではないMyService、それは必要としないこと。つまり、とMyClientの機能のみが必要な場合、代わりにC()D() class MyService { public …

1
継承階層でリスコフ置換原理を検証する方法は?
この答えに触発された: リスコフの代替原則では 、 サブタイプでは前提条件を強化できません。 サブタイプでは事後条件を弱めることはできません。 スーパータイプの不変式は、サブタイプに保存する必要があります。 履歴制約(「履歴ルール」)。オブジェクトは、そのメソッド(カプセル化)によってのみ変更可能と見なされます。サブタイプはスーパータイプには存在しないメソッドを導入する可能性があるため、これらのメソッドの導入により、スーパータイプでは許可されないサブタイプの状態変更が可能になります。これは履歴制約により禁止されています。 これらの4つのポイントに違反するクラス階層を誰かが投稿し、それに応じてそれらを解決する方法を誰かが投稿することを望んでいました。 階層内の4つのポイントのそれぞれを識別する方法と、それを修正する最良の方法について、教育目的で詳細な説明を探しています。 注: 私は人々が作業するためのコードサンプルを投稿したいと思っていましたが、問題自体は障害のある階層を特定する方法に関するものです:)

2
LinkedListを拡張するスタック。Liskov Substitution Principleの違反?
クラスLinkedListは、add_first()、add_last()、add_after()、remove_first()、remove_last()、remove()などの関数とともに存在します 現在、push()、pop()、peek()、またはtop()などの機能を提供するクラスStackがあり、これらのメソッドを実装するためにLinkedListクラスメソッドを拡張しています。これは、リスコフ代替原理の違反ですか? たとえば、リンクリストのn番目の位置にノードを追加する場合のadd_after()の場合を考えます。これは基本クラスで実行できますが、Stackクラスでは実行できません。ここで事後条件が弱くなっていますか、それともadd_after()メソッドを変更してスタックの一番上に追加しますか? また、違反ではない場合、これは悪い設計ですか?そして、LinkedListクラスを使用してStack機能をどのように実装しますか?

4
SOLIDをフォローする場合、ファイルの読み取りと書き込みには2つの異なる責任がありますか?
SOLIDの調査を始めたばかりで、ファイルの読み取りとファイルへの書き込みが同じ責任であるかどうかはわかりません。 ターゲットは同じファイルタイプです。アプリケーションで.pdfを読み書きしたい。 それが違いを生む場合、アプリケーションはPythonです。

4
モックはオープン/クローズの原則に違反しますか?
少し前に、私は見つけることができないStack Overflowの回答で、パブリックAPIをテストする必要があることを説明した文を読み、著者はインターフェイスをテストする必要があると言いました。また、メソッドの実装が変更された場合、テストケースを変更する必要はありません。これを行うと、テスト中のシステムが機能することを確認する契約が破られるため、著者は説明しました。つまり、メソッドが機能しない場合はテストが失敗しますが、実装が変更されたためではありません。 私たちがock笑について話すとき、これは私の注意を呼びました。モックはテスト対象システムの依存関係からの期待呼び出しに大きく依存しているため、モックはインターフェイスではなく実装と密接に結合されています。 モックとスタブの調査中、いくつかの記事は、依存関係からの期待に依存しないため、モックの代わりにスタブを使用する必要があることに同意しています。 私の質問は次のとおりです。 モックはオープン/クローズの原則に違反しますか? 最後の段落のスタブを支持する議論に欠けているものはありますか? もしそうなら、いつスモックを作成するのに適したユースケースになり、スタブを使用するのに適したユースケースになりますか?

3
依存関係の逆転の原則:「高レベルのポリシー」と「低レベルの詳細」を他の人に定義する方法は?
私は、依存関係の逆転の原則を(ほとんどは後輩の)同僚に説明しようとしています。ソフトウェアの「高レベルポリシー」と「低レベル詳細」を定義するにはどうすればよいですか?たとえば、ソフトウェアが複数のビジネスアプリケーションのワークフローを自動化する場合、ワークフローの自動化が高レベルのポリシーであり、ビジネスアプリケーションが詳細であると言うのはなぜですか?

1
消費者を単体テストするための唯一のソリューションは、サードパーティのコードをラップすることですか?
私は単体テストを行っており、クラスの1つでメソッドの1つからメールを送信する必要があるため、コンストラクター注入を使用Zend_Mailして、Zendフレームワークにあるクラスのインスタンスを注入します。 現在、一部の人々は、ライブラリが十分に安定しており、頻繁に変更されない場合、それをラップする必要はないと主張します。したがって、それZend_Mailが安定しており、変更されず、私のニーズに完全に適合すると仮定すると、そのためのラッパーは必要ありません。 次にLogger依存する私のクラスを見てみましょうZend_Mail: class Logger{ private $mailer; function __construct(Zend_Mail $mail){ $this->mail=$mail; } function toBeTestedFunction(){ //Some code $this->mail->setTo('some value'); $this->mail->setSubject('some value'); $this->mail->setBody('some value'); $this->mail->send(); //Some } } ただし、ユニットテストでは、一度に1つのコンポーネントをテストする必要があるため、Zend_Mailクラスをモックする必要があります。さらに、クラスが抽象ではなく結石に依存するようになったため、依存関係反転の原則に違反してLoggerいます。 Loggerラップせずに単独でテストするにはどうすればよいZend_Mailですか? コードはPHPにありますが、答えはそうである必要はありません。これは、言語固有の機能というよりも設計上の問題です

3
SOLID原則の適用
私は、SOLIDの設計原則にまったく慣れていません。私はそれらの原因と利点を理解していますが、SOLID原則を使用するための実践的な演習としてリファクタリングしたい小規模なプロジェクトにそれらを適用できません。完全に機能するアプリケーションを変更する必要はありませんが、とにかくそれをリファクタリングして、将来のプロジェクトの設計経験を積みたいと思います。 アプリケーションには次のタスクがあります(実際にはそれ以上のことですが、簡単にしましょう):データベーステーブル/列/ビューなどの定義を含むXMLファイルを読み取り、作成するために使用できるSQLファイルを作成する必要がありますORACLEデータベーススキーマ。 (注:なぜそれが必要なのか、なぜXSLTを使用しないのかなどについて議論することは控えてください。理由はありますが、トピックから外れています。) はじめに、テーブルと制約のみを見ることにしました。列を無視する場合、次のように記述できます。 制約はテーブルの一部(より正確にはCREATE TABLEステートメントの一部)であり、制約は別のテーブルを参照する場合もあります。 最初に、アプリケーションがどのように見えるかを説明します(SOLIDを適用しない): 現時点では、アプリケーションには、テーブルが所有する制約へのポインターのリストと、このテーブルを参照する制約へのポインターのリストを含む「テーブル」クラスがあります。接続が確立されるたびに、逆方向の接続も確立されます。このテーブルには、各制約のcreateStatement()関数を順に呼び出すcreateStatement()メソッドがあります。このメソッド自体は、名前を取得するために所有者テーブルと参照先テーブルへの接続を使用します。 明らかに、これはSOLIDにはまったく適用されません。たとえば、循環的な依存関係があり、必要な「追加」/「削除」メソッドといくつかの大きなオブジェクトデストラクタに関してコードが肥大化しました。 そのため、いくつか質問があります。 依存性注入を使用して循環依存関係を解決する必要がありますか?もしそうなら、Constraintはそのコンストラクタで所有者(およびオプションで参照される)テーブルを受け取るべきだと思います。しかし、単一のテーブルの制約のリストをどのように実行できますか? Tableクラスが自身の状態(テーブル名、テーブルコメントなど)とConstraintsへのリンクの両方を保存する場合、これらの1つまたは2つの「責任」は、単一責任の原則と考えられますか? ケース2.が正しい場合、リンクを管理する論理ビジネスレイヤーに新しいクラスを作成するだけですか?その場合、1は明らかに関連しなくなります。 「createStatement」メソッドは、Table / Constraintクラスの一部である必要がありますか、それともそれらを移動する必要がありますか?もしそうなら、どこへ?各データストレージクラス(つまり、テーブル、制約など)ごとに1つのマネージャークラスですか?または、リンクごとにマネージャークラスを作成します(3と同様)。 これらの質問に答えようとするたびに、どこかで輪になって走っています。 列やインデックスなどを含めると、問題は明らかにはるかに複雑になりますが、単純なテーブル/制約のことで手伝ってくれれば、残りは自分で解決できるかもしれません。

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