単䞀責任の原則。 芋た目ほど単玔ではありたせん

単䞀責任の原則。 芋た目ほど単玔ではありたせん 単䞀責任の原則、単䞀責任の原則ずも呌ばれたす。
別名均䞀倉動性の原理 - 理解するのが非垞に難しく、プログラマヌの面接では非垞に緊匵する質問です。

私がこの原理を初めお真剣に知ったのは、XNUMX幎生の初め、幌虫から生埒、぀たり本物の生埒を䜜るために若くお緑色の子が森に連れお行かれたずきでした。

森の䞭で、私たちは89人のグルヌプに分かれお、グルヌプの最初の人がグラスにりォッカを泚ぎ、XNUMX番目の人がそれを飲むずいう条件で、どのグルヌプがりォッカのボトルを䞀番早く飲むか競争したした。そしおXNUMX人目はおや぀を食べたす。 操䜜を完了したナニットはグルヌプのキュヌの最埌に移動したす。

キュヌ サむズが XNUMX の倍数であるケヌスは、SRP の適切な実装でした。

定矩 1. 単䞀の責任。

単䞀責任原則 (SRP) の公匏の定矩では、各゚ンティティは独自の責任ず存圚理由を持ち、責任は XNUMX ぀だけであるず述べられおいたす。

オブゞェクト「酒飲み」(チッププラヌ).
SRP 原則を実装するために、私たちは責任を XNUMX ぀に分割したす。

  • XNUMX ぀を泚ぎたす (泚ぐ操䜜)
  • 䞀杯飲みたすドリンクアップオペレヌション)
  • 䞀人は軜食を持っおいたすテむクバむトオペレヌション)

プロセスの各参加者は、プロセスの XNUMX ぀のコンポヌネントに察しお責任を負いたす。぀たり、飲む、泚ぐ、軜食ずいう XNUMX ぀の原子的な責任を負いたす。

氎飲み堎は、次のような操䜜のファサヌドです。

сlass Tippler {
    //...
    void Act(){
        _pourOperation.Do() // МалОть
        _drinkUpOperation.Do() // выпОть
        _takeBiteOperation.Do() // закусОть
    }
}

単䞀責任の原則。 芋た目ほど単玔ではありたせん

なぜですか

人間のプログラマヌは猿人のためにコヌドを曞きたすが、猿人は䞍泚意で愚かで、い぀も急いでいたす。 圌は䞀床に玄 3  7 ぀の甚語を保持しお理解するこずができたす。
酔っぱらいの堎合には、これらの甚語が XNUMX ぀ありたす。 しかし、コヌドを XNUMX 枚のシヌトで䜜成するず、手、メガネ、喧嘩、政治に関する終わりのない議論が含たれるこずになりたす。 これらすべおが XNUMX ぀のメ゜ッドの本䜓に含たれたす。 あなたも実務でそのようなコヌドを芋たこずがあるず思いたす。 粟神にずっお最も人道的なテストずは蚀えたせん。

䞀方、猿人は頭の䞭で珟実䞖界の物䜓をシミュレヌトするように蚭蚈されおいたす。 圌の想像力の䞭で、それらを抌し合わせたり、そこから新しいオブゞェクトを組み立おたり、同じ方法で分解したりするこずができたす。 叀いモデルの車を想像しおください。 想像力の䞭で、ドアを開け、ドアトリムを緩めるず、窓の昇降機構があり、その䞭に歯車が入っおいるのが芋えたす。 ただし、マシンのすべおのコンポヌネントを XNUMX ぀の「リスト」で同時に確認するこずはできたせん。 少なくずも「猿人」には無理だ。

したがっお、人間のプログラマは、耇雑なメカニズムをそれほど耇雑ではない機胜する芁玠のセットに分解したす。 ただし、分解方法はさたざたです。倚くの叀い車では、゚アダクトがドアに入り蟌み、珟代の車では、ロック電子機噚の故障により゚ンゞンが始動できなくなり、修理䞭に問題が発生する可胜性がありたす。

今、 SRPはどのように分解するか、぀たりどこに境界線を匕くかを説明する原則です.

圌は、「責任」の分割の原則に埓っお、぀たり特定のオブゞェクトのタスクに埓っお分解する必芁があるず述べおいたす。

単䞀責任の原則。 芋た目ほど単玔ではありたせん

飲酒ず分解䞭に猿人が受ける利点に戻りたしょう。

  • コヌドはあらゆるレベルで非垞に明確になりたした
  • コヌドは耇数のプログラマヌが同時に䜜成できたす (各プログラマヌが個別の芁玠を曞き蟌みたす)。
  • 自動テストは簡玠化されたす - 芁玠が単玔であればあるほど、テストが簡単になりたす。
  • コヌドの構成性が衚瀺されたす - 眮き換えるこずができたす ドリンクアップオペレヌション 酔っぱらいがテヌブルの䞋に液䜓を泚ぐずいう行為。 たたは、泚ぐ操䜜を、ワむンず氎、たたはりォッカずビヌルを混ぜる操䜜に眮き換えたす。 ビゞネス芁件に応じお、メ゜ッド コヌドに觊れるこずなくすべおを実行できたす ティップラヌ・アクト.
  • これらの操䜜により、グルトンを折りたたむこずができたすのみを䜿甚したす TakeBitオペレヌション、アルコヌルのみ䜿甚 ドリンクアップオペレヌション ボトルから盎接、その他倚くのビゞネス芁件を満たしたす。

(ああ、これはすでに OCP の原則のようで、この投皿の責任に違反したした)

そしおもちろん、短所もありたす:

  • さらに倚くのタむプを䜜成する必芁がありたす。
  • 酔っ払いは、通垞よりも数時間遅れお初めおお酒を飲みたす。

定矩 2. 統䞀された倉動性。

蚱しおください、玳士諞君 飲酒クラスにも責任は XNUMX ぀ありたす。それは飲むこずです。 そしお䞀般に、「責任」ずいう蚀葉は非垞に曖昧な抂念です。 人類の運呜には誰かが責任を負い、極地でひっくり返ったペンギンを育おる責任も誰かにある。

ドリンカヌの XNUMX ぀の実装を考えおみたしょう。 䞊で説明した最初のクラスには、泚ぐ、ドリンク、スナックの XNUMX ぀のクラスが含たれおいたす。

XNUMX ぀目は、「Forward and Only Forward」方法論で蚘述されおおり、メ゜ッド内のすべおのロゞックが含たれおいたす。 行為:

//Не тратьте вреЌя  Ма ОзучеМОе этПгП класса. Лучше съешьте печеМьку
сlass BrutTippler {
   //...
   void Act(){
        // МалОваеЌ
    if(!_hand.TryDischarge(from:_bottle, to:_glass, size:_glass.Capacity))
        throw new OverdrunkException();

    // выпОваеЌ
    if(!_hand.TryDrink(from: _glass,  size: _glass.Capacity))
        throw new OverdrunkException();

    //ЗакусываеЌ
    for(int i = 0; i< 3; i++){
        var food = _foodStore.TakeOrDefault();
        if(food==null)
            throw new FoodIsOverException();

        _hand.TryEat(food);
    }
   }
}

これらのクラスは䞡方ずも、倖郚の芳察者の芳点からはたったく同じに芋え、「飲酒」ずいう同じ責任を共有したす。

混乱

次に、オンラむンにアクセスしお、SRP の別の定矩である単䞀倉曎可胜性の原則を芋぀けたす。

SCPは次のよ​​うに述べおいたす。モゞュヌルを倉曎する理由はただ XNUMX ぀だけです」。 それは、「責任は倉化の理由である」ずいうこずです。

元々の定矩を考えた人たちは猿人のテレパシヌ胜力に自信があったようです

これで、すべおがうたくいきたした。 泚ぐ、飲む、おや぀の手順を個別に倉曎できたすが、ドリンカヌ自䜓では、たずえば飲む前におや぀を移動したり、也杯の読みを远加したりするなど、操䜜の順序ず構成を倉曎するこずしかできたせん。

「Forward and Only Forward」アプロヌチでは、倉曎できるものはすべおメ゜ッド内でのみ倉曎されたす。 行為。 これは、ロゞックがほずんどなく、めったに倉曎されない堎合には読みやすく効果的ですが、倚くの堎合、ロシアが NATO に加盟するために必芁な数よりも倚くの if ステヌトメントを含む、それぞれ 500 行ずいうひどい方法で終わりたす。

定矩 3. 倉曎のロヌカラむれヌション。

飲酒者は、なぜ他人のアパヌトで目が芚めたのか、携垯電話がどこにあるのか理解できないこずがよくありたす。 詳现なログを远加したす。

泚湯プロセスのログを開始したしょう。

class PourOperation: IOperation{
    PourOperation(ILogger log /*....*/){/*...*/}
    //...
    void Do(){
        _log.Log($"Before pour with {_hand} and {_bottle}");
        //Pour business logic ...
        _log.Log($"After pour with {_hand} and {_bottle}");
    }
}

それをカプセル化するこずで、 泚ぐ操䜜、私たちは責任ずカプセル化の芳点から賢明に行動したしたが、今では倉動性の原則ず混同されおいたす。 操䜜自䜓が倉曎可胜であるこずに加えお、ロギング自䜓も倉曎可胜になりたす。 泚湯操䜜甚に特別なロガヌを分離しお䜜成する必芁がありたす。

interface IPourLogger{
    void LogBefore(IHand, IBottle){}
    void LogAfter(IHand, IBottle){}
    void OnError(IHand, IBottle, Exception){}
}

class PourOperation: IOperation{
    PourOperation(IPourLogger log /*....*/){/*...*/}
    //...
    void Do(){
        _log.LogBefore(_hand, _bottle);
        try{
             //... business logic
             _log.LogAfter(_hand, _bottle");
        }
        catch(exception e){
            _log.OnError(_hand, _bottle, e)
        }
    }
}

泚意深く読んでいる方ならお気づきでしょうが、 ログアフタヌ, 前のログ О ゚ラヌ時 個別に倉曎するこずもでき、前の手順ず同様に、次の XNUMX ぀のクラスが䜜成されたす。 泚ぐロガヌ前, 泚ぐロガヌ埌 О PourErrorLogger.

そしお、飲酒者には 14 ぀の操䜜があるこずを思い出しお、XNUMX ぀のロギング クラスを取埗したす。 結果、飲み䌚サヌクル党䜓はXNUMXクラス!!!ずなりたした。

双曲線 しそうにない 分解手抎匟を持った猿人は、「泚ぐ人」をデカンタ、グラス、泚ぎオペレヌタ、絊氎サヌビス、分子衝突の物理モデルに分割し、次の四半期に枡っお䟝存関係を解きほぐそうずしたす。グロヌバル倉数。 そしお信じおください、圌は止たらないでしょう。

この時点で、倚くの人は SRP はピンクの王囜のおずぎ話であるずいう結論に達し、ヌヌドルをするために去っおいきたす...

...Srp の XNUMX 番目の定矩の存圚に぀いお孊習するこずなく、次のようになりたす。

「単䞀責任原則では次のように述べられおいたす。 倉曎に類䌌したものは XNUMX か所に保管する必芁がありたす」。 たたは "䞀緒に倉曎されるものは XNUMX か所に保管する必芁がありたす 

぀たり、操䜜のログを倉曎する堎合は、XNUMX か所で倉曎する必芁がありたす。

これは非垞に重芁な点です。䞊蚘の SRP のすべおの説明では、型を朰すずきに型を朰す必芁がある、぀たり、オブゞェクトのサむズに「䞊限」を課す必芁があるず述べられおいたため、すでに「䞋限」に぀いお話しおいたす。 蚀い換えるず、 SRPでは「朰しながら朰す」だけでなく、無理をしない「連動したものを朰さない」こずも求められたす。。 オッカムのカミ゜リず猿人の倧決戊だ

単䞀責任の原則。 芋た目ほど単玔ではありたせん

これで、飲酒者は気分が良くなるはずです。 IPourLogger ロガヌを XNUMX ぀のクラスに分割する必芁がないこずに加えお、すべおのロガヌを XNUMX ぀のタむプに結合するこずもできたす。

class OperationLogger{
    public OperationLogger(string operationName){/*..*/}
    public void LogBefore(object[] args){/*...*/}       
    public void LogAfter(object[] args){/*..*/}
    public void LogError(object[] args, exception e){/*..*/}
}

XNUMX 番目のタむプの操䜜を远加するず、そのログの準備はすでに完了しおいたす。 たた、操䜜のコヌド自䜓はクリヌンで、むンフラストラクチャのノむズがありたせん。

その結果、飲酒問題を解決するための 5 ぀のクラスが甚意されたした。

  • 泚湯䜜業
  • 飲酒運転
  • ゞャミング操䜜
  • ロガヌ
  • 酒飲みのファサヌド

それぞれが XNUMX ぀の機胜を厳密に担圓しおおり、倉曎の理由は XNUMX ぀ありたす。 倉曎に䌌たルヌルはすべお近くにありたす。

実際の䟋

私たちはか぀お、B2B クラむアントを自動的に登録するサヌビスを䜜成したした。 そしお、同様の内容の 200 行に察しお GOD メ゜ッドが登堎したした。

  • 1Cに移動しおアカりントを䜜成したす
  • このアカりントを䜿甚しお、支払いモゞュヌルに移動し、そこで䜜成したす
  • メむンサヌバヌにそのようなアカりントが䜜成されおいないこずを確認しおください
  • 新しいアカりントを䜜成する
  • 支払いモゞュヌルの登録結果ず 1c 番号を登録結果サヌビスに远加したす
  • このテヌブルにアカりント情報を远加したす
  • ポむント サヌビスでこのクラむアントのポむント番号を䜜成したす。 1c アカりント番号をこのサヌビスに枡したす。

そしお、このリストには、接続がひどい状態にある事業所がさらに 10 件ほどありたした。 ほが党員がアカりント オブゞェクトを必芁ずしおいたした。 コヌルの半分でポむント ID ずクラむアント名が必芁でした。

100 時間のリファクタリングの埌、むンフラストラクチャ コヌドずアカりント操䜜の埮劙な郚分を個別のメ゜ッド/クラスに分離するこずができたした。 God メ゜ッドのおかげで䜜業は簡単になりたしたが、解きほぐしたくないコヌドが XNUMX 行残っおいたした。

この「軜量」手法の本質がビゞネス アルゎリズムであるこずが明らかになったのは、数日埌でした。 そしお、技術仕様の元の説明は非垞に耇雑でした。 そしお、このメ゜ッドを现かく分割しようずする詊みが SRP に違反するこずになり、その逆はありたせん。

圢匏䞻矩。

酔っぱらいを攟っおおく時が来た。 涙を拭いおください - い぀か必ず戻っおきたす。 それでは、この蚘事の知識を圢匏的に敎理しおみたしょう。

圢匏䞻矩 1. SRP の定矩

  1. それぞれの芁玠が XNUMX ぀のこずを担圓するように芁玠を分離したす。
  2. 責任ずは「倉化する理由」を意味したす。 ぀たり、ビゞネス ロゞックの芳点から、各芁玠の倉曎理由は XNUMX ぀だけです。
  3. ビゞネス ロゞックが倉曎される可胜性がありたす。 ロヌカラむズする必芁がありたす。 同期的に倉化する芁玠は近くにある必芁がありたす。

圢匏䞻矩 2. 必芁な自己テスト基準。

SRPを満たすための十分な基準がわかりたせん。 ただし、必芁な条件がありたす。

1) このクラス/メ゜ッド/モゞュヌル/サヌビスが䜕をするのかを自問しおください。 簡単な定矩で答えなければなりたせん。  ありがずう ブラむトリ )

説明

ただし、単玔な定矩を芋぀けるのが非垞に難しい堎合がありたす。

2) バグの修正たたは新しい機胜の远加は、ファむル/クラスの最小数に圱響したす。 理想的には - XNUMX ぀。

説明

(機胜たたはバグに察する) 責任は XNUMX ぀のファむル/クラスにカプセル化されおいるため、どこを調べお䜕を線集すればよいかが正確にわかりたす。 たずえば、ロギング操䜜の出力を倉曎する機胜では、ロガヌのみを倉曎する必芁がありたす。 残りのコヌドを実行する必芁はありたせん。

別の䟋は、以前のものず同様の新しい UI コントロヌルを远加するこずです。 これにより、10 の異なる゚ンティティず 15 の異なるコンバヌタを远加する必芁がある堎合は、やりすぎのように芋えたす。

3) 耇数の開発者がプロ​​ゞェクトの異なる機胜に取り組んでいる堎合、マヌゞ競合の可胜性、぀たり同じファむル/クラスが耇数の開発者によっお同時に倉曎される可胜性は最小限になりたす。

説明

「テヌブルの䞋にりォッカを泚ぐ」ずいう新しい操䜜を远加するずきに、飲む操䜜ず泚ぐ操䜜であるロガヌに圱響を䞎える必芁がある堎合、責任が歪んで分割されおいるように芋えたす。 もちろん、これが垞に可胜であるずは限りたせんが、この数字を枛らすように努める必芁がありたす。

4) ビゞネス ロゞックに぀いお (開発者たたはマネヌゞャヌから) 明確な質問をされた堎合、厳密に XNUMX ぀のクラス/ファむルにアクセスし、そこからのみ情報を受け取りたす。

説明

機胜、ルヌル、アルゎリズムはそれぞれ XNUMX か所にコンパクトに蚘述され、コヌド空間党䜓にフラグが散らばるこずはありたせん。

5) ネヌミングがわかりやすい。

説明

私たちのクラスたたはメ゜ッドは XNUMX ぀のこずに察しお責任を負い、その責任はその名前に反映されおいたす。

AllManagersManagerService - おそらく神クラス
LocalPayment - おそらくそうではありたせん

圢匏䞻矩 3. オッカムファヌストの開発方法論。

蚭蚈の開始時には、サルマンは解決されおいる問題のすべおの埮劙な点を理解しおおらず、感じおいないため、間違いを犯す可胜性がありたす。 さたざたな方法で間違いを犯す可胜性がありたす。

  • さたざたな責任を統合しおオブゞェクトが倧きくなりすぎる
  • XNUMX ぀の責任をさたざたな皮類に分割しお再構成する
  • 責任の範囲を誀っお定矩する

「倧きな間違いをしたほうが良い」「確信が持おない堎合は分割しない」ずいうルヌルを芚えおおくこずが重芁です。 たずえば、クラスに XNUMX ぀の責任が含たれおいる堎合でも、それは理解可胜であり、クラむアント コヌドぞの最小限の倉曎で XNUMX ぀に分割できたす。 ガラスの砎片からガラスを組み立おるのは、コンテキストが耇数のファむルに分散しおいたり​​、クラむアント コヌドに必芁な䟝存関係がなかったりするため、通垞はより困難になりたす。

もう終わりにする時が来た

SRP の範囲は OOP ず SOLID に限定されたせん。 これは、メ゜ッド、関数、クラス、モゞュヌル、マむクロサヌビス、およびサヌビスに適甚されたす。 これは「figax-figax-and-prod」開発ず「ロケットサむ゚ンス」開発の䞡方に適甚され、あらゆる堎所で䞖界を少しでも良くしたす。 考えおみれば、これはすべおの゚ンゞニアリングのほが基本原則です。 機械工孊、制埡システム、そしお実際すべおの耇雑なシステムはコンポヌネントから構築されおおり、「断片化䞍足」は蚭蚈者の柔軟性を奪い、「断片化過剰」は蚭蚈者の効率を奪い、境界線が間違っおいるず理性ず安心感を奪いたす。

単䞀責任の原則。 芋た目ほど単玔ではありたせん

SRP は自然に発明されたものではなく、粟密科孊の䞀郚でもありたせん。 それは私たちの生物孊的および心理的限界を打ち砎り、猿人の脳を䜿っお耇雑なシステムを制埡し開発する方法にすぎたせん。 圌はシステムを分解する方法を教えおくれたす。 元の定匏化にはかなりの量のテレパシヌが必芁でしたが、この蚘事で煙幕の䞀郚が解消されるこずを願っおいたす。

出所 habr.com

コメントを远加したす