次のSOLIDは、技術スタックの上にフレームワークを書くことにつながりますか?


70

私はソリッドが好きで、開発中にそれを使用して適用するように最善を尽くします。しかし、SOLIDアプローチがコードを「フレームワーク」コードに変えるように感じざるを得ません。つまり、他の開発者が使用するフレームワークまたはライブラリを作成する場合に設計するコードです。

私は通常、2つのプログラミングモードを練習しました-要件とKISS(通常のプログラミング)で要求される内容を多かれ少なかれ作成するか、他の開発者が必要とする柔軟性を提供する非常に一般的で再利用可能なロジック、サービスなどを作成します(フレームワークプログラミング) 。

もしユーザーが本当にアプリケーションにxとyのことをしたいだけなら、SOLIDに従って抽象化のエントリーポイントの全体を追加するのは理にかなっています。と?これらの抽象化のエントリポイントを追加する場合、ユーザーの要件を本当に満たしていますか、それとも既存のフレームワークと技術スタックの上にフレームワークを作成して将来の追加を容易にしますか?どちらの場合、顧客または開発者の利益に貢献していますか?

これはJava Enterpriseの世界では一般的なことのように思われますが、ユーザーのUXに焦点を当てるのではなく、開発者にとってより良いUXになるようにJ2EEまたはSpringの上に独自のフレームワークを設計しているように感じますか?


12
大部分の短いプログラミングの経験則の問題は、それらが解釈、エッジケースの影響を受けることであり、そのような規則の単語の定義は、綿密な検査では不明確な場合があります。彼らは本質的に、異なる人々にとって多種多様なものを意味することができます。いくつかの非イデオロギーの実用主義があれば、通常は賢明な決定を下すことができます。
マークロジャース

1
固い原則に従うことは、何らかの形で大きな投資、多くの余分な作業を意味するように聞こえます。それはありません、それは実質的に無料です。また、コードの保守と拡張が容易になるため、将来的にはあなたや他の誰かに大きな投資を節約する可能性があります。「宿題をするか、顧客を幸せにするか」などの質問をします。これらはトレードオフではありません。
マーティンマート

1
@MartinMaat SOLIDのより極端な形態は、大きな投資を意味すると思います。すなわち。エンタープライズソフトウェア。エンタープライズソフトウェア以外では、選択したスタックに固執する可能性が非常に高いため、ORM、テクノロジースタック、またはデータベースを抽象化する理由はほとんどありません。同じ意味で、特定のフレームワーク、データベース、またはORMに結び付けることにより、スタックに結合されているため、SOLID原則を破ります。SOLIDのこのレベルの柔軟性は、ほとんどのジョブでは必要ありません。
Igneous01

1
内部プラットフォーム効果も参照してください。
Maxpm

1
ほとんどのコードをフレームワークのようなものに変えることは、まったくひどく聞こえません。過剰に設計された場合にのみひどくなります。しかし、フレームワークは最小限であり、意見を述べることができます。これがSOLIDに従うことの避けられない結果であるかどうかはわかりませんが、それは間違いなく起こりうる結果であり、あなたが受け入れるべきだと思います。
コンラッドルドルフ

回答:


84

あなたの観察は正しいです。SOLIDの原則は、再利用可能なライブラリまたはフレームワークコードを念頭に置いて作成されています。理にかなっているかどうかを尋ねることなく、それらすべてを盲目的にたどるだけの場合、おそらく必要以上に多くの労力を過剰にシステムに投資するリスクがあります。

これはトレードオフであり、いつ一般化するか、しないかについて正しい決定を下すにはある程度の経験が必要です。これへの可能なアプローチは、YAGNIの原則に固執することです-あなたのコードを「念のために」固めないでください-または、あなたの言葉を使用する:しないでください

他の開発者必要とする可能性のある柔軟性を提供する

代わりに、他の開発者が実際に必要する柔軟性を、必要応じてすぐ提供しますが、それ以前ではありません。

したがって、コードに関数またはクラスが1つある場合は、それを再利用できるかどうかがわからないので、今すぐフレームワークに入れないでください。再利用の実際のケースがあるまで待ち、「そのケースに十分なソリッド」にリファクタリングします。実際の再利用のケースで本当に必要なクラスに、より多くの構成可能性(OCPに準拠)や抽象化のエントリポイント(DIPを使用)を実装しないでください。再利用の次の要件が実際にあるときに、次の柔軟性を追加します。

もちろん、この作業方法では、既存の作業コードベースで常にある程度のリファクタリングが必要になります。そのため、ここでは自動テストが重要です。したがって、コードを最初からユニットテスト可能にするのに十分なSOLIDにすることは時間の無駄ではなく、そうすることはYAGNIと矛盾しません。自動テストは、「コードの再利用」の有効なケースです。これは、テスト中のコードだけでなく、本番コードでも使用されているコードが使用されるためです。ただし、テストを機能させるために実際に必要な柔軟性を追加してください。

これは実際には古い知恵です。昔SOLID用語が人気だ前に、誰かが私たちが書き込もうとする前に私に言った、再使用可能なコードを、私たちは書くべき使用可能なコードを。そして、私はまだこれが良い推薦だと思います。


23
余分な競合点:同じロジックを使用する3つのユースケースができるまで待ってから、コードをリファクタリングして再利用します。2つの要素でリファクタリングを開始すると、要件の変更や新しいユースケースが、作成した抽象化を破ってしまう状況に陥りやすくなります。また、リファクタリングは同じユースケースのものに限定してください:2つのコンポーネントは同じコードを持っているかもしれませんが、まったく異なることを行う可能性があり、それらのコンポーネントをマージすると、そのロジックをリンクしてしまい、後で問題を引き起こす可能性があります。
Nzall

8
私はこれに概ね同意しますが、「1回限りの」アプリに焦点が絞られているように感じます。コードを書いて、うまく動作します。ただし、「長時間のサポート」を備えたアプリがたくさんあります。いくつかのコードを書くと、2年後にビジネス要件が変わるため、コードを調整する必要があります。その時までに、他の多くのコードがそれに依存する可能性があります。その場合、SOLID原則により変更が容易になります。
R.シュミッツ

3
「再利用可能なコードを作成する前に、使用可能なコードを作成する必要があります」-非常に賢明です!
グラハム

10
仮説での作業は非常に難しく、将来のニーズがどうなるかを誤解する可能性が高いため、実際のユースケースが得られるまで待つことでSOLIDコードが改善されることはおそらく注目に値します。私たちのプロジェクトには、将来のニーズに合わせて物事が堅実で柔軟になるように設計された多くのケースがあります...将来のニーズは当時誰も考えていなかったものであることが判明したため、私たちは両方ともリファクタリングする必要がありましたまだ必要ありませんでした。リファクタリングに直面して維持するか、廃棄する必要がありました。
KRyan

2
また、通常、テスト可能なコードを記述する必要があります。これは、具体的な実装からテスト実装に切り替えることができるように、通常、抽象化の最初の層を持つことを意味します。
ウォルフラット

49

私の経験から、アプリを書くとき、次の3つの選択肢があります。

  1. 要件を満たすためだけにコードを記述し、
  2. 現在の要件を満たすだけでなく、将来の要件を予測する汎用コードを記述します。
  3. 現在の要件のみを満たすコードを作成しますが、他のニーズに合わせて後で簡単に変更できます。

最初のケースでは、ユニットテストのない密結合コードで終わるのが一般的です。書くのは簡単ですが、テストするのは難しいです。そして、後で要件が変更されたときに変更するのは当然のことです。

2番目のケースでは、将来のニーズを予測するために膨大な時間が費やされます。そして、多くの場合、これらの予想される将来の要件は決して実現しません。これはあなたが説明しているシナリオのようです。ほとんどの場合、これは労力の無駄であり、不必要に複雑なコードになりますが、予期しない要件が生じた場合でも変更するのは困難です。

最後のケースは、私の見解で目指すべきものです。TDDまたは同様の手法を使用して、コードをテストしていくと、疎結合のコードになります。これは簡単に修正できますが、すぐに記述できます。そして事は、これを行うことにより、当然のことながら、SOLID原則の多くに従います。小さなクラスと関数。インターフェイスと挿入された依存関係。そして、単一の責任を持つ単純なクラスが彼女の代替原則に反することはめったにないので、リスコフ夫人は一般的に幸せを保っています。

ここで実際に適用されないSOLIDの唯一の側面は、オープン/クローズの原則です。ライブラリとフレームワークにとって、これは重要です。自己完結型アプリの場合、それほど多くはありません。実際には、「SLID」に続くコードを記述する場合です。簡単に記述(および読み取り)し、テストしやすく、保守しやすいです。


このサイトで私のお気に入りの答えの1つです!
-TheCatWhisperer

1)は3)よりもテストが難しいと結論付ける方法はわかりません。確かに変更を加えるのは難しいですが、なぜテストできないのですか?どちらかといえば、ひたむきなソフトウェアは、より一般的なソフトウェアよりも要件に対するテストが簡単です。
ミスターリスター

@MrLister 1. 2つの手をつないで、1は3よりもテストが難しい。定義は、「後で他のニーズに合わせて簡単に変更できるように」書かれていないことを意味するため
マークブース

1
+0; IMVHOあなたは(一般的な方法ではありますが) 'O'(オープン-クローズ)の動作方法を誤解しています。たとえばcodeblog.jonskeet.uk/2013/03/15/…を参照してください-小規模なコードベースであっても、自己完結型のコード単位(クラス、モジュール、パッケージなど)を持ち、単独でテストして追加できます/必要に応じて削除。そのような例の1つは、ユーティリティメソッドのパックです。バンドルする方法に関係なく、それらは「クローズ」つまり自己完結型であり、「オープン」つまり何らかの方法で拡張可能である必要があります。
vaxquis

ところで、ボブおじさんでさえ、ある点でそのようになっています。システムのすべてのモジュールへの変更。理想的には、新しいコードを追加し、ほとんど、あるいはまったく古いコードを変更することにより、新しい動作を追加することができます「。 < -これは明らかに、彼らがこれまでに変更されることを意図または固定されている場合は、小さなアプリケーションに適用され(そして、IMVHO、通常はそうです、特に修正については笑います
-vaxquis

8

あなたが持っている視点は、個人的な経験によって歪められます。これは、個別に正しい事実の滑りやすい勾配ですが、一見正しいように見えても、結果の推論は正しくありません。

  • フレームワークは、小規模プロジェクトよりも範囲が広いです。
  • 悪い習慣は、より大きなコードベースで対処するのがかなり難しくなります。
  • フレームワークの構築(平均)には、小規模なプロジェクトを構築するよりも熟練した開発者が必要です。
  • より良い開発者は、より良い実践(SOLID)に従います。
  • その結果、フレームワークは優れた実践に対するニーズが高くなり、優れた実践精通した開発者によって構築される傾向があります。

これは、フレームワークや小さなライブラリとやり取りするとき、やり取りするグッドプラクティスコードはより大きなフレームワークでより一般的に見つかることを意味します。

この誤acyは非常に一般的です。たとえば、私が治療を受けたすべての医師は慢でした。したがって、私はすべての医師が慢であると結論付けます。これらの誤aは、常に個人的な経験に基づいた包括的な推論を行うことに苦しんでいます。

あなたの場合、小さなライブラリではなく、大きなフレームワークで優良なプラクティスを主に経験している可能性があります。あなたの個人的な観察は間違っていませんが、それは事例証拠であり、普遍的に適用可能ではありません。


プログラミングの2つのモード-要件とKISS(通常のプログラミング)で要求される内容を多少正確に作成するか、他の開発者が必要とする柔軟性を提供する非常に汎用的で再利用可能なロジック、サービスなどを作成します(フレームワークプログラミング)

ここでこれをいくらか確認しています。フレームワークとは何かを考えてください。それはアプリケーションではありません。他の人があらゆる種類のアプリケーションを作成するために使用できる一般化された「テンプレート」です。論理的には、フレームワークは、誰でも使用できるように、より抽象化されたロジックで構築されていることを意味します。

フレームワークビルダーは、後続のアプリケーションの要件がわからないため、ショートカットを取得できません。フレームワークを構築することで、コードを他の人が使用できるようになります。

ただし、アプリケーションビルダーは、製品の提供に重点を置いているため、論理的な効率を損なう可能性があります。彼らの主な目標は、コードの仕組みではなく、ユーザーのエクスペリエンスです。

フレームワークの場合、エンドユーザーはコードとやり取りする別の開発者です。コードの品質はエンドユーザーにとって重要です。
アプリケーションの場合、エンドユーザーは開発者ではなく、コードを操作しません。コードの品質は重要ではありません。

開発チームのアーキテクトがグッドプラクティスのエンフォーサーとしてしばしば行動するのはまさにこのためです。それらは、製品の配信から削除された1つのステップです。つまり、アプリケーション自体の配信に焦点を当てるのではなく、コードを客観的に見る傾向があります。


これらの抽象化のエントリポイントを追加する場合、ユーザーの要件を本当に満たしていますか、それとも既存のフレームワークと技術スタックの上にフレームワークを作成して将来の追加を容易にしますか?どちらの場合、顧客または開発者の利益に貢献していますか?

これは興味深い点であり、それは(私の経験では)人々が依然として優れた実践を避けることを正当化しようとする主な理由です。

以下のポイントを要約すると:(現在知られている)要件が不変であり、コードベースに変更/追加が加えられない場合にのみ、優れたプラクティスをスキップすることを正当化できます。 ネタバレ注意:それはめったにありません。
たとえば、特定のファイルを処理するために5分間のコンソールアプリケーションを作成するときは、良い方法を使用しません。今日はアプリケーションを使用するだけであり、将来更新する必要がないため(別のアプリケーションが必要になった場合は、別のアプリケーションを作成する方が簡単です)。

4週間で見事にアプリケーションをビルドでき、6週間で適切にビルドできるとしましょう。一見したところ、見苦しくそれを構築する方が良いようです。顧客はアプリケーションを迅速に入手でき、開発者の賃金に費やす時間を短縮する必要があります。勝ち勝ちですよね?

ただし、これは先を考えずに行われた決定です。コードベースの品質のため、見苦しく構築されたものに大きな変更を加えるには2週間かかりますが、適切に構築されたものに同じ変更を加えるには1週間かかります。将来これらの変更の多くが発生する可能性があります。

さらに、変更が予期せず構築されたコードベースで当初考えていたよりも多くの作業を必要とする傾向があるため、開発時間が2週間ではなく3週間になる可能性があります。

そして、バグを探すのに時間を浪費する傾向もあります。これは多くの場合、最終製品が期待どおりに機能するという仮定の下でぼんやりと作業をしているため、時間の制約やログの実装をまったく望まないためにロギングが無視されているプロジェクトの場合です。

メジャーアップデートである必要さえありません。私の現在の雇用主では、迅速かつ汚いビルドされたいくつかのプロジェクトを見てきました。また、要件の誤解のために最も小さなバグ/変更を行う必要があり、モジュールの後にモジュールをリファクタリングする必要があるという連鎖反応につながりました。これらのプロジェクトのいくつかは、最初のバージョンをリリースする前に崩壊しました(そして、維持できない混乱を残しました)。

ショートカットの決定(迅速で汚いプログラミング)は、要件が正確であり、変更する必要がないことを最終的に保証できる場合にのみ有益です。私の経験では、それが当てはまるプロジェクトに出会ったことはありません

余分な時間をグッドプラクティスに投資することは、将来への投資です。既存のコードベースが優れたプラクティスに基づいて構築されている場合、将来のバグと変更は非常に簡単になります。変更が2、3回行われただけで既に配当を支払っています。


1
これは良い答えですが、グッドプラクティスを放棄すると言っているのではなく、どのレベルの「グッドプラクティス」を追求するのかを明確にする必要があります。すべてのプロジェクトでORMを抽象化するのは良い習慣ですか?後で別のプロジェクトに交換する必要があるかもしれませんか?私はそうは思わない、私が受け入れたいと思う一定のレベルのカップリングがある(すなわち、私は選択されたフレームワーク、言語、ORM、データベースに縛られている)。SOLIDを過激主義の程度まで追いかけた場合、実際に選択したスタックの上に独自のフレームワークを実装しているだけでしょうか?
-Igneous01

OPの経験を「誤acy」として否定しています。建設的ではありません。
max630

@ max630私はそれを否定していません。回答の大部分を費やして、OPの観測が有効である理由を説明しました。
18

1
@ Igneous01 SOLIDはフレームワークではありません。SOLIDは抽象化であり、フレームワークでより一般的に見られます。あらゆる種類の抽象化(SOLIDを含む)を実装する場合、常に合理性があります。抽象化のためだけに抽象化することはできません。過度に一般化されており、従うのが難しいコードを考えるのに何年も費やします。合理的に疑わしいものだけが将来的にあなたに役立つだろうと抽象化します。ただし、たとえば現在のデータベースサーバーに縛られていると思われるという落とし穴に陥らないでください。明日、どの新しいデータベースがリリースされるかわかりません。
18

@ Igneous01言い換えれば、すべてを抽象化したくないということで正しい考えが得られますが、その方向にあまりにも少し傾いている感じがします。開発者は、現在の要件が明確に設定されていると想定し、その(希望的な)前提に基づいてアーキテクチャ上の決定を下すことが非常に一般的です。
フラット

7

SOLIDは単純なコードをフレームワークコードにどのように変換しますか?私は決してSOLIDのスタントではありませんが、ここであなたが何を意味するのかは本当に明らかではありません。

  • KISSは、S ingle Responsibility Principle の本質です。
  • 中には何もありませんOペン/クローズドの原則(私は理解して、少なくともとしてそれを、見ジョンスキートをうまく一つのことを行うために書くコードに反します)。(実際、コードの焦点がより厳しくなればなるほど、「閉じた」部分がより重要になります。)
  • Lの iskov置換原則あなたは、人々が自分のクラスをサブクラス化させる必要があり言っていません。クラスサブクラス化する場合、サブクラスはスーパークラスのコントラクトを満たす必要があると書かれています。それはちょうど良いオブジェクト指向設計です。(また、サブクラスがない場合は適用されません。)
  • KISSもの本質であるI nterface棲み分け原理。
  • Dの ependency反転原理は、私がリモートで適用見ることができる唯一の一つですが、私はそれが広く誤解や誇張だと思います。GuiceまたはSpringですべてを注入する必要があるという意味ではありません。これは、実装の詳細に依存せず、必要に応じて抽象化する必要があることを意味します。

ボブ・マーティンの学校ではなく、ギャング・オブ・フォーとプログラミングのジョシュ・ブロックの学校を思いついたからです。しかし、「SOLID」=「技術スタックにさらにレイヤーを追加する」と考えるなら、それは間違っていると思います。


PS「開発者にとってより良いUX」の利点を簡潔に売らないでください。コードはその寿命のほとんどをメンテナンスに費やします。開発者はあなたです


1
SRPについて-コンストラクターを持つクラスはSRPに違反していると主張できます。これは、その責任をファクトリーにオフロードできるためです。OCPに関しては-これは実際にはフレームワークレベルの問題です。外部で使用するためのインターフェイスを公開すると、それを変更できないためです。インターフェースがプロジェクト内でのみ使用される場合、独自のコード内で契約を変更する権限があるため、契約を変更することができます。ISPについて-インターフェースは個々のアクションごとに定義する必要がある(つまりSRPを保持する)ことを主張でき、外部ユーザーの懸念もあります。
Igneous01

3
1)可能ですが、聞いてみる価値のある人は誰もいません。2)1つのプロジェクトが、内部インターフェイスを自由に変更することが悪いアイデアになるほど急速に成長することに驚くかもしれません。3)1)と2)の両方を参照してください。3つの原則すべてを読み過ぎていると思います。しかし、コメントは本当にこれらの議論に取り組む場所ではありません。それぞれを個別の質問として提示し、どのような回答が得られるかを確認することをお勧めします。
デビッドモール

4
@ Igneous01そのロジックを使用すると、ゲッターとセッターを放棄することもできます。変数セッターごとに個別のクラスを作成し、ゲッターごとに1つのクラスを作成できるからです。IE:class A{ int X; int Y; } class A_setX{ f(A a, int N) { a.X = N; }} class A_getX{ int f(A a) { return X; }} class A_setY ... etc.あなたはあなたの工場の主張で、あまりにもメタの観点からそれを見ていると思います。初期化はドメインの問題の側面ではありません。
アーロン

@アーロンこれ。人々はSOLIDを使用して悪い議論をすることができますが、それは悪いことをすること=「SOLIDに従う」という意味ではありません。
デビッドモール
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.