単独責任の難しい原則

背景



過去数幎にわたっお、私は倚数のむンタビュヌに参加しおきたした。 それらのそれぞれで、私は応募者に唯䞀の責任の原則以䞋SRPに぀いお尋ねたした。 そしお、ほずんどの人は原則に぀いお䜕も知りたせん。 そしお、定矩を読むこずができた人でさえ、圌らが仕事でこの原則をどのように䜿っおいるかを蚀うこずはほずんどできたせんでした。 圌らは、SRPが自分が曞いたコヌドや同僚のコヌドレビュヌにどのように圱響するかを蚀うこずができたせんでした。 たた、SOLID党䜓ず同様に、SRPはオブゞェクト指向プログラミングにのみ関連しおいるずいう誀解もありたした。 たた、よく知られおいるフレヌムワヌクで掚奚されおいるスタむルでコヌドが蚘述されたずいう理由だけで、倚くの堎合、人々はこの原則に違反する明らかなケヌスを特定できたせんでした。

Reduxは、ガむドラむンがSRPに違反しおいるフレヌムワヌクの代衚的な䟋です。



SRPの問題



私は、この原則の䟡倀、それがもたらす利点から始めたいず思いたす。 たた、この原則はOOPだけでなく、手続き型プログラミング、関数型、さらに宣蚀型にも適甚されるこずに泚意しおください。 埌者の代衚ずしおのHTMLは、特にReactやAngularなどのUIフレヌムワヌクによっお制埡されおいる堎合は特に、分解するこずができたす。 さらに、この原則は他の゚ンゞニアリング分野にも適甚されたす。 そしお、工孊だけでなく、軍事分野でもそのような衚珟がありたした。「分割しお埁服する」こずです。これは、抂しお同じ原則の具䜓化です。 耇雑さは殺し、それを郚分に分割し、あなたは勝ちたす。

他の゚ンゞニアリング分野に関しおは、ここハブで、開発された航空機がどのように゚ンゞンを故障させ、パむロットのコマンドでリバヌスに切り替えなかったかに぀いおの興味深い蚘事がありたした。 問題は、シャヌシの状態を誀っお解釈しおいたこずです。 ゚ンゞンコントロヌラヌは、シャヌシを制埡するシステムに䟝存する代わりに、シャヌシにあるセンサヌ、リミットスむッチなどを盎接読み取りたす。 たた、゚ンゞンがプロトタむプの航空機に搭茉される前に、゚ンゞンが長い認蚌を受ける必芁があるこずも蚘事で蚀及されたした。 たた、この堎合のSRP違反は、シャヌシの蚭蚈を倉曎するずきに、゚ンゞンコントロヌラヌのコヌドを倉曎しお再認蚌する必芁があるずいう事実に明らかに぀ながりたした。 さらに悪いこずに、この原則に違反するこずは飛行機ずパむロットの人生にほずんど䟡倀がありたした。 幞いなこずに、私たちの日垞のプログラミングはそのような結果を脅かすものではありたせんが、良いコヌドを曞くずいう原則を無芖するべきではありたせん。 理由は次のずおりです。



  1. コヌドの分解により、コヌドの耇雑さが軜枛されたす。 たずえば、問題の解決策ずしお埪環的耇雑床4のコヌドを蚘述する必芁がある堎合、そのような2぀の問題を同時に解決する方法には耇雑床16のコヌドが必芁になりたす。仕事に察する金額になりたすが、ずにかく傟向はほが同じになりたす。
  2. 分解されたコヌドの単䜓テストは簡玠化され、より効率的になりたす。
  3. 分解されたコヌドは、倉曎に察する抵抗が少なくなりたす。 倉曎を行うずき、ミスをする可胜性は䜎くなりたす。
  4. コヌドの構造が改善されおいたす。 ファむルやフォルダヌに配眮されたコヌド内の䜕かを怜玢するこずは、1぀の倧きなフットクロスよりもはるかに簡単です。
  5. 定型コヌドをビゞネスロゞックから分離するず、コヌド生成をプロゞェクトに適甚できるようになりたす。


そしお、これらのすべおの兆候は䞀緒になり、これらは同じコヌドの兆候です。 たずえば、十分にテストされたコヌドず十分に構造化されたコヌドを遞択する必芁はありたせん。



既存の定矩は機胜したせん



定矩の1぀は、「コヌドクラスたたは関数を倉曎する理由は1぀だけであるべきです」です。 この定矩の問題は、SOLIDの2番目の原則グルヌプであるOpen-Close原則ず競合するこずです。 その定矩「コヌドは拡匵のために開かれ、倉曎のために閉じられなければなりたせん。」 倉曎の理由の1぀ず、倉曎の完党な犁止。 ここで䜕を意味するのかをより詳现に明らかにするず、原則間に矛盟はないこずがわかりたすが、ファゞヌ定矩の間には間違いなく矛盟がありたす。



2番目の、より盎接的な定矩は、「コヌドには1぀の責任のみが必芁です」です。 この定矩の問題は、すべおを䞀般化するこずは人間の本性であるずいうこずです。



たずえば、鶏を飌育しおいる蟲堎があり、その時点で蟲堎の責任は1぀だけです。 そのため、アヒルも繁殖させるこずが決定されたした。 本胜的には、2぀の責任があるこずを認めるのではなく、逊鶏堎ず呌びたす。 そこに矊を远加するず、これがペットファヌムになりたす。 次に、そこでトマトたたはキノコを栜培し、さらに䞀般化された次の名前を考えたす。 同じこずが、倉曎の「1぀の理由」にも圓おはたりたす。 この理由は、想像力で十分に䞀般化できたす。



別の䟋は、宇宙ステヌション管理者クラスです。 圌は他に䜕もしたせん、圌は宇宙ステヌションを管理するだけです。 このクラスを1぀の責任でどのように気に入っおいたすか

たた、求職者がこの技術に粟通しおいるずきにReduxに぀いお蚀及したので、質問もしたす。兞型的なSRPリデュヌサヌは違反したすか



リデュヌサヌにはswitchステヌトメントが含たれおおり、堎合によっおは数十たたは数癟のケヌスにたで拡倧するこずがありたす。 レデュヌサヌの唯䞀の責任は、アプリケヌションの状態遷移を管理するこずです。 ぀たり、文字通り、䞀郚の応募者が答えたした。 たた、この意芋を裏付けるヒントはありたせん。



党䜓ずしお、ある皮のコヌドがSRPの原則を満たしおいるように芋えるが、同時に䞍快なにおいがする堎合-なぜこれが起こるのかを知っおください。 「コヌドには1぀の責任が必芁」ずいう定矩が機胜しないためです。



より適切な定矩



詊行錯誀から、より良い定矩ができたした

コヌドの責任は倧きすぎおはいけたせん



はい。クラスたたは関数の責任を「枬定」する必芁がありたす。 そしお、それが倧きすぎる堎合、この倧きな責任をいく぀かの小さな責任に分割する必芁がありたす。 蟲堎の䟋に戻るず、鶏を飌育する責任さえも倧きすぎる可胜性があり、たずえばブロむラヌを局から䜕らかの方法で分離するこずは理にかなっおいたす。



しかし、それを枬定する方法、このコヌドの責任が倧きすぎるず刀断する方法は



残念ながら、私は数孊的に正確な方法はなく、経隓的な方法しかありたせん。 そしお、これらのすべおは経隓に基づいおいたす。初心者の開発者はコヌドを分解するこずはできたせん。より高床な開発者はコヌドを所有するのに優れおいたす。



  1. メトリック埪環的耇雑床。 残念ながら、このメトリックをマスクする方法がありたすが、収集するず、アプリケヌション内で最も脆匱な堎所が衚瀺される可胜性がありたす。
  2. 関数ずクラスのサむズ。 800行の関数は、䜕か問題があるこずを理解するために読む必芁はありたせん。
  3. たくさんの茞入。 隣のチヌムのプロゞェクトでファむルを開くず、むンポヌトの画面党䜓が衚瀺され、ペヌゞを䞋に抌すず、画面にはむンポヌトのみが衚瀺されおいたした。 2回目のプレスの埌、コヌドの始たりを芋たした。 すべおの最新のIDEはプラス蚘号の䞋でむンポヌトを非衚瀺にできるず蚀うこずができたすが、良いコヌドは「匂い」を非衚瀺にする必芁はないず蚀いたす。 さらに、小さなコヌドを再利甚する必芁があったため、このファむルから別のコヌドに削陀し、むンポヌトの4分の1たたは3分の1がこの郚分の埌ろに移動したした。 このコヌドは明らかにそこに属しおいたせんでした。
  4. 単䜓テスト。 それでも責任の量を刀断するのが困難な堎合は、テストを曞くように匷制しおください。 境界線のケヌスなどをカりントせずに、関数の䞻な目的で20個のテストを蚘述する必芁がある堎合は、分解が必芁です。
  5. 同じこずは、テストの開始時の準備段階が倚すぎお、終了時のチェックにも圓おはたりたす。 ちなみに、むンタヌネット䞊では、いわゆる テストでアサヌトするのは䞀般に1぀だけです。 しかし、私は、arbitrarily意的に良いアむデアが絶察的なものに䞊げられるず、ばかげお非珟実的になるず信じおいたす。
  6. ビゞネスロゞックは、倖郚ツヌルに盎接䟝存しないでください。 OracleドラむバヌのExpressルヌトでは、これらすべおをビゞネスロゞックから分離したり、むンタヌフェヌスの背埌に隠したりするこずが望たしいです。


いく぀かのポむント



もちろん、すでに述べたように、コむンには裏返しがあり、1行の800メ゜ッドは800行の1メ゜ッドよりも良くないかもしれたせん。すべおのバランスが取れおいるはずです。



第二-私は、このコヌドをその責任に埓っおどこに眮くべきかずいう問題をカバヌしおいたせん。 たずえば、開発者がDALレむダヌに倚くのロゞックを取り蟌むのが難しい堎合もありたす。



第䞉に、「関数あたり50行以䞋」などの特定のハヌド制限を提案したせん。 このアプロヌチには、開発者、そしおおそらくチヌムの開発の方向性のみが含たれたす。 圌は私のために働いおいたす、圌は他の人のためにお金を皌がなければなりたせん。



そしお最埌に、TDDを䜿甚する堎合、これだけで、それぞれ20個のアサヌションを含む20個のテストを䜜成するはるか前に、コヌドを確実に分解できたす。



定型コヌドからビゞネスロゞックを分離する



良いコヌドのルヌルに぀いお蚀えば、䟋がなければできたせん。 最初の䟋は、定型コヌドの分離に関するものです。







この䟋は、バック゚ンドコヌドの通垞の蚘述方法を瀺しおいたす。 人々は通垞、URL、リク゚ストメ゜ッドなどのWebサヌバヌExpressぞのパラメヌタを瀺すコヌドを䜿甚しおロゞックを密接に蚘述したす。



ビゞネスロゞックを緑色のマヌカヌずしおマヌクし、赀色の散圚コヌドがク゚リパラメヌタヌず盞互䜜甚したす赀色。



私はこの2぀の責任を垞にこのように共有しおいたす。







この䟋では、Expressずのすべおの察話は別のファむルにありたす。



䞀芋したずころ、2番目の䟋では改善が芋られなかったように芋えるかもしれたせん。1぀ではなく2぀のファむルがあり、以前は存圚しおいなかった远加行が衚瀺されたした。 そしお、このコヌドの分離は䜕をもたらしたすか たず、「アプリケヌション゚ントリポむント」はExpressではなくなりたした。 これは通垞のTypescript関数です。 たたは、Cかどうかに関係なくWebAPIを䜜成するJavaScript関数。



これにより、最初の䟋では䜿甚できないさたざたなアクションを実行できたす。 たずえば、Expressを発生させるこずなく、テスト内でhttp芁求を䜿甚せずに、動䜜テストを䜜成できたす。 そしお、どんな皮類の濡れをする必芁もないずしおも、Routerオブゞェクトを「テスト」オブゞェクトに眮き換えれば、アプリケヌションコヌドをテストから盎接呌び出すこずができたす。



この分解によっお提䟛される別の興味深い機胜は、userApiServiceを解析し、このサヌビスをExpressに接続するベヌスでコヌドを生成するコヌドゞェネレヌタヌを䜜成できるこずです。 今埌の出版物では、次のこずを瀺す予定です。コヌド生成では、コヌドを蚘述するプロセスの時間を節玄できたせん。 コヌドゞェネレヌタヌのコストは、この定型文をコピヌする必芁がなくなったずいう事実によっお報われたせん。 コヌド生成は、それが生成するコヌドがサポヌトを必芁ずしないずいう事実によっお報われたす。これは時間を節玄し、最も重芁なこずずしお、長期的には開発者の神経を節玄したす。



分割しお埁服する



コヌドを蚘述するこの方法は長い間存圚しおおり、私は自分で発明したせんでした。 ビゞネスロゞックを蚘述するずきに非垞に䟿利であるずいう結論に達したした。 このために、別の架空の䟋を思い぀きたした。すぐに簡単にコヌドを蚘述し、すぐに適切に分解され、呜名方法によっお自己文曞化されるコヌドを䜜成する方法を瀺したした。



ビゞネスアナリストからタスクを取埗しお、埓業員レポヌトを保険䌚瀟に送信するメ゜ッドを䜜成するずしたす。 これを行うには



  1. デヌタベヌスからデヌタを取埗する必芁がありたす
  2. 目的の圢匏に倉換する
  3. 結果のレポヌトを送信


このような芁件は、垞に明瀺的に蚘述されおいるわけではなく、アナリストずの䌚話からこのようなシヌケンスが暗瀺されたり明確にされる堎合もありたす。 このメ゜ッドを実装するプロセスでは、デヌタベヌスたたはネットワヌクぞの接続を急いで開くのではなく、この単玔なアルゎリズムを「そのたた」コヌドに倉換しおみおください。 このようなもの



async function sendEmployeeReportToProvider(reportId){ const data = await dal.getEmployeeReportData(reportId);​ const formatted = reportDataService.prepareEmployeeReport(data);​ await networkService.sendReport(formatted);​ }
      
      





このアプロヌチを䜿甚するず、かなりシンプルで読みやすいコヌドを取埗できたすが、このコヌドは簡単で、テストの必芁はないず思いたす。 たた、レポヌトを送信しないのはこのメ゜ッドの責任であり、その責任はこの耇雑なタスクを3぀のサブタスクに分割するこずでした。



次に、芁件に戻り、レポヌトが絊䞎セクションず劎働時間のセクションで構成されおいる必芁があるこずを確認したす。



 function prepareEmployeeReport(reportData){ const salarySection = prepareSalarySection(reportData);​ const workHoursSection = prepareWorkHoursSection(reportData);​ return { salarySection, workHoursSection };​ }
      
      





その他、些现に近い小さなメ゜ッドの実装が残るたで、タスクを分割し続けたす。



オヌプンクロヌズ原則ずの盞互䜜甚



蚘事の冒頭で、SRPずOpen-Closeの原則の定矩は互いに矛盟しおいるず述べたした。 前者は倉曎の理由が1぀あるに違いないず述べ、埌者は倉曎のためにコヌドを閉じる必芁があるず述べおいたす。 そしお、原則自䜓は、互いに矛盟しないだけでなく、反察に、盞互に盞乗効果を発揮したす。 5぀のSOLID原則はすべお、1぀の良い目的を目指しおいたす-どのコヌドが「悪い」か、どのように倉曎しお「良い」コヌドになるかを開発者に䌝えるこずです。 皮肉なこずに、私は5぀の責任をもう1぀の責任に眮き換えたした。

したがっお、レポヌトを保険䌚瀟に送信する前の䟋に加えお、ビゞネスアナリストが来お、今床はプロゞェクトに2぀目の機胜を远加する必芁があるず蚀っおいるこずを想像しおください。 同じレポヌトを印刷する必芁がありたす。

SRPは「分解に関するものではない」ず考えおいる開発者がいるず想像しおください。

したがっお、この原則は圌に分解の必芁性を瀺しおおらず、圌は1぀の機胜で最初のタスク党䜓を実珟したした。 タスクが圌に来た埌、圌は2぀の責任を1぀にたずめたす。 それらの間には倚くの共通点があり、その名前は䞀般化しおいたす。 珟圚、この責任は「サヌビスレポヌト」ず呌ばれおいたす。 この実装は次のようになりたす。

 async function serveEmployeeReportToProvider(reportId, serveMethod){ /* lots of code to read and convert the report */ switch(serveMethod) { case sendToProvider: /* implementation of sending */ case print: /* implementation of printing */ default: throw; } }
      
      





プロゞェクトのコヌドを思い出したすか 私が蚀ったように、SRPの盎接的な定矩は䞡方ずも機胜したせん。 そのようなコヌドを蚘述できないずいう情報を開発者に送信するこずはありたせん。 そしお、どのコヌドを曞くこずができたす。 開発者がこのコヌドを倉曎する理由はただ1぀だけです。 圌は単に以前の理由を呌び盎し、スむッチを远加し、萜ち着いおいたす。 そしお、ここでは、Open-Close原則の原理が登堎したす。これは、既存のファむルを倉曎するこずは䞍可胜であるず盎接述べおいたす。 新しい機胜を远加するずきに、既存のファむルを線集するのではなく、新しいファむルを远加する必芁があるようにコヌドを蚘述する必芁がありたした。 ぀たり、このようなコヌドは䞀床に2぀の原則の芳点からは悪いです。 そしお、もし最初のものがそれを芋るのを助けなかったら、2番目のものが助けになるはずです。



そしお、分割統治法は同じ問題をどのように解決したすか

 async function printEmployeeReport(reportId){ const data = await dal.getEmployeeReportData(reportId);​ const formatted = reportDataService.prepareEmployeeReport(data);​ await printService.printReport(formatted);​ }
      
      





新しい関数を远加したす。 実装を持たないため、「スクリプト関数」ずも呌ばれるこずがあり、責任の分解郚分を呌び出す順序を決定したす。 明らかに、最初の2行、最初の2぀の分解された責任は、以前に実装された関数の最初の2行ず䞀臎しおいたす。 ビゞネスアナリストが説明した2぀のタスクの最初の2぀のステップが䞀臎するように。

したがっお、プロゞェクトに新しい機胜を远加するために、新しいスクリプトメ゜ッドず新しいprintServiceを远加したした。 叀いファむルは倉曎されおいたせん。 ぀たり、このコヌド蚘述方法は、2぀の原則の芳点から芋お適切です。 そしお、SRPずオヌプンクロヌズ



代替案



たた、次のような適切に分解されたコヌドを取埗するための代替の競合方法に぀いおも蚀及したいず思いたす。たず、「額に」コヌドを蚘述し、その埌、ファりラヌの本「リファクタリング」に埓っお、さたざたな手法を䜿甚しおリファクタリングしたす これらの方法は、チェスのゲヌムに察する数孊的アプロヌチを思い出させたした。戊略の芳点から自分が䜕をしおいるかを正確に理解せず、自分のポゞションの「重み」を蚈算し、動きを加えお最倧化しようずしたす。 メ゜ッドず倉数に名前を付けるこずは既に困難であり、ビゞネス䟡倀がない堎合は䞍可胜になるずいう小さな理由で、このアプロヌチが奜きではありたせんでした。 たずえば、これらの手法で、あちこちから6぀の同䞀の行を遞択する必芁があるこずが瀺唆されおいる堎合、それらを遞択しお、このメ゜ッドを䜕ず呌びたすか someSixIdenticalLines

予玄をしたい-この方法が悪いずは思わない。䜿い方を孊ぶこずができなかった。



合蚈



原則に埓うず、メリットが埗られたす。



「1぀の責任がなければならない」ずいう定矩は機胜したせん。



より良い定矩ずいく぀かの間接的な機胜、いわゆる コヌドは分解の必芁性を瀺す匂いがしたす。



分割統治のアプロヌチにより、すぐに適切に構造化された自己文曞化コヌドを曞くこずができたす。



All Articles