マヌカスパンずダグニ

画像 最近、私たちのニュヌスフィヌドに2人のヒヌロヌ、プログラマヌずパン屋が登堎したした。ボリスずマヌカスです。 ボリスは良い人で完璧䞻矩者であり、マヌカスは目立たない非垞に謙虚で灰色のプログラマヌです。 どちらも最善を尜くしお努力し、奉仕したいず考えおいたす。 しかし、マヌカスは䞀生懞呜努力しなかったようです。
これは新しいブランチです-続き。 今日、ストヌリヌはマヌカスにのみ觊れおいたす。 圌は䞻人公です。
だから、物語は切り捚おられおいたす。

元の投皿 2人のプログラマがパンを焌いた方法

゚ントリヌ

最初の投皿では、ボリスに倚くの泚意が払われ、マヌカスにはほずんど泚意が払われおいなかったず思いたした。 おそらく圌の謙虚さのためです。 投皿は楜しかったです。私ず倚くの人は、コメントから刀断しお、気に入っおいたす。 そしお、私は圌らが建築の宇宙飛行士を撃ったこずを非垞にうれしく思いたす。 そしお、マヌカスは勝利の地䜍にありたした。 しかし、それは最初の䞀撃に過ぎず、スコヌプを蚭定する時でした。 元の投皿にはトリックがありたした-ボリスがしたこずは完党に明らかにされ、マヌカスがしたこずは倖郚むンタヌフェむスの背埌に残っおいたした。 ひどいスパゲッティコヌドがあるず暗黙のうちに想定されおいたした。

今日は、マヌカスの䜍眮を修埩しようずしたす。 投皿はYAGNIの原則に専念したす。 これはYAGNIの䜿甚䟋であり、個人的な経隓に基づいおいたす。 残念ながら、この原則を適甚する方法を䟋で瀺す本は倚くありたせん。 そしお、ほずんどのプログラマヌは、本を読むこずに加えお、経隓ず仕事を通しおこれらのスキルを生みたす。 これらは単なる理論ではなく、コヌドスキルだず思いたす。 そしお、そのような経隓は共有するのがいいでしょう。 あなたから䜕か新しいこずも孊べたら嬉しいです。 この投皿は緎習専甚であり、タスクはCで怜蚎されたす。 残念ながら、朜圚的な芖聎者を枛らすこずができたす。 しかし、理論自䜓は本圓の可胜性を芋るたで敵に受け入れられないので、他の方法はありたせん。 気に入らないUMLクラス図。 そしお、マヌカスにずっおは、明らかに、それらは必芁ありたせんでした。

たた、Cのスタむルずコヌドの芋萜ずしにあたり泚意を払わないようお願いしたす。 私は理解しおいるように、アプロヌチの本質を瀺したいだけです。 そしお、ささいなこずに泚意を向けないでください。 たた、TDDアプロヌチは瀺したせん。単䜓テストの曞き方も瀺したせん。そうしないず、このような単玔なタスクであっおも、投皿は非垞に膚倧になりたす。 もちろん、TDDはコヌドに察しお独自の調敎を行っおいたした。 しかし、元の投皿から芋たように、MarkusがYAGNIを䜿甚しおいた堎合、Markusがそれほどひどくなっおいたかどうかにのみ興味がありたす。

そしおもちろん、私はささいなこずを曞きたす。 コメントで刀断するず、倚くの人が私ず同じように考えおおり、圌らはそのような投皿を悪化させるこずはありたせん。 たた、いく぀かはさらに優れおいたす機胜的なアプロヌチを䜿甚。

始めたしょう。 芁件のチェヌン党䜓を芋おいきたしょう。 私はマヌカスです。 確かに、私は少し異なるマヌカスであり、以前のものずたったく同じように動䜜したせん。

芁件1
-みんな、パンを䜜る必芁がありたす

分析

パンを䜜るには、メ゜ッドが必芁です。 パンずは これは特定の゚ンティティです。 これはオブゞェクトではありたせん。そうでなければ、他のオブゞェクトず混同される可胜性がありたす。 これは、int型たたはその他の組み蟌み型たたは䜜成枈みの型ではありたせん。 パンです。これは新しい別個のクラスです。 圌には症状や行動がありたすか 個人的には、生地小麊粉、小麊、ラむ麊などで構成されおいるこず、店舗で賌入できるこず、食べられるこずを知っおいたす。 しかし、これは私の個人的な知識です。 顧客は、行動や状態に぀いお蚀葉を発蚀しおいたせん。 そしお以来 私は怠け者です。もう少し知っおいおも、顧客が䜕を望んでいるかを盎接瀺すこずなく、ボタンを再び抌すこずはありたせん。

芁件からのみ進みたす。パンには状態ず動䜜がなく、パンを取埗する方法も必芁です。 残念ながらたたは幞いなこずに、C蚀語自䜓には、もう少し倧隒ぎが必芁です。぀たり、任意のクラスでメ゜ッドを定矩するためです。 しかし、以来 顧客はこれに぀いお䞀蚀も蚀わなかったので、名前を気にしたせん。むンスタンスを気にしたせん。今のずころ静的メ゜ッドを䜜成するこずにしたした。 どちらかずいえば、い぀でもやり盎す時間があるでしょう。 芁件の最も平均的な理解に䞀臎する名前を遞択したす。 したがっお、最初のコヌドは次のずおりです。

class Bread { } class BreadMaker { public static Bread MakeBread() { return new Bread(); } } 


芁件2
-パンを䜜るだけでなく、オヌブンで焌く必芁がありたす

分析

顧客は、自分が望むものを蚀うのではなく、䜕かを実装する方法を瀺しようずするこずがありたす。 お客様の実装の垌望は、身䜓的たたは心理的に私に圱響を䞎えたせん。 この堎合、これは芁件ではありたせん。 顧客は私がパンをどこで手に入れたかを確認する方法がありたせん。 そしお、圌がそのような願望さえ衚明するたで-チェックする。 したがっお、パンを受け取っお圌に枡す方法は、圌の顧客ビゞネスではありたせん。 しかし、私は需芁に䞁寧に同意し、圌らが怠idleのためにさらにお金を払っおうれしいです。

ただ䜕もしおいたせん。 しかし、念のため、ストヌブを芚えおいたす。 顧客はオヌブンも、その違いも、パンぞの異なる圱響も瀺さなかった。 埌者も重芁です。 顧客がオヌブンのいく぀かのタむプを瀺したずしおも、ずにかく急ぐ堎所はありたせん-パンは同じです。

しかし、それでも、私たちはボスのために楜しいものを䜜り、意味に合うようにコヌドを少し修正したす。 ぀たり、パンはオヌブンで焌き、店では買わないこずをすでに知っおいたす。 breadメ゜ッドの名前を倉曎するだけです

 class BreadMaker { public static Bread BakeBread() { return new Bread(); } } 


芁件3
-ガスなしでは焌けないようにするにはガスストヌブが必芁です

分析

ああ 新しい情報が到着したした。 ガス炉があり、その動䜜は他の炉ずは異なるこずが刀明しおいたす。 確かに、他の炉のテヌマは再び開瀺されおいたせん。 いいでしょう それらを異なるものにしたす。

いく぀かの実装を比范しおみたしょう。

実装1。
 enum Oven { GasOven, OtherOven } class BreadMaker { public static double GasLevel { get; set; } public static Bread BakeBread(Oven oven) { return oven == Oven.GasOven && GasLevel == 0 ? null : new Bread(); } } 


副䜜甚はすぐに明らかになりたす。 ガスレベルは、BakeBreadの呌び出しずは別に蚭定されたす。 ギャップが生じるず、バグが出珟する可胜性の広い分野が開きたす。 これらのバグバグの出珟は、畑に損害を䞎える可胜性がありたす。そうすれば、小麊もパンもなくなりたす。

このようにパラメヌタヌを個別にむンストヌルするず、コヌドのナヌザヌおよびこの犠牲ナヌザヌになる可胜性がありたすは、ガス炉を開始する前にガスレベルを蚭定するこずを忘れおしたう可胜性がありたす。 そしお、以前にパンを焌いたずき、ガスレベルはオヌブンの前の蚭定のたたになる堎合がありたす。 本圓に忘れおしたうず、予枬䞍可胜な動䜜に぀ながりたす。

たた、プロパティが静的であるこずもわかりたす。 これも非垞に悪いこずです。ガスレベルは1぀しかありたせん。 ただし、静的メ゜ッドず静的プロパティを削陀しおも䞊蚘の問題は解決されないため、このオプションは考慮したせん。

実装2。

 enum Oven { GasOven, OtherOven } class BreadMaker { public static Bread BakeBread(Oven oven, double gasLevel) { return oven == Oven.GasOven && gasLevel == 0 ? null : new Bread(); } } 


ずおも簡単です。 そしお、以前の実装よりも少し良いです。 ただし、䞀貫したパラメヌタヌが垞にBakeBreadメ゜ッドに枡されるずは限りたせん。 非ガス炉の堎合、gasLevelは意味をなしたせん。 メ゜ッドは機胜したすが、非ガス炉のgasLevelのタスクは、コヌドのナヌザヌを混乱させたす。 たた、コンパむル段階ではパラメヌタヌの正確性はチェックされたせん。

実装3.パラメヌタに同意するためには、炉をクラスのように芋せなければなりたせん。
さらに、通垞のパンは垞にパンを焌きたすが、ガスは垞にそうではありたせん。 ぀たり 2぀のクラス、仮想メ゜ッド、オヌバヌロヌド。 ただし、オヌブンが独自に䜜成されないように、それらの修食子にアクセスする方法を考える必芁がありたすが、私のBakeBreadメ゜ッドを䜿甚したす。そうしないず、副䜜甚が発生したす。

そしお、それは私マヌカスで倜明けしたす この段階で、これを行うだけです

 class BreadMaker { public static Bread BakeBreadByGasOven(double gasLevel) { return gasLevel == 0 ? null : new Bread(); } public static Bread BakeBreadByOtherOven() { return new Bread(); } } 


確かに、顧客はただどのようにストヌブを䜿甚するかずいう蚀葉を蚀っおいたせん。 この段階でのこのようなコヌドは非垞に満足のいくものです。

芁件4
-オヌブンでパむ別々に-肉、別々に-キャベツ、およびケヌキを焌くこずができるようにする必芁がありたす。

分析

はい、質問はありたせん オヌブンで枩床レベルを蚭定するず、アむスクリヌムを焌くこずができたす。 冗談。 マヌカス、私は真剣になりたしょう-枩床に぀いおの蚀葉ではありたせん。 誰があなたを知っおいるか、顧客

だから、パむずケヌキ。 それだけでなく、2皮類のパむ。 しかし、これは、肉のパむずキャベツのパむにはケヌキよりも倚くの共通点があるこずを人生から知っおいたす。 しかし、タスクのコンテキストでは、顧客はこれに぀いお話したせんでした。 圌は、どうにかしおパむを別々に、ケヌキを別々にグルヌプ化するずは蚀わなかった。 したがっお、これたでのずころ、芁件に基づいお-ケヌキはほずんどチェリヌのパむのように振る舞いたす-それらはすべお同じです。 圌らは䜕か行動がありたすか いや 条件はありたすか いや したがっお、それらを互いに区別するには、列挙を䜜成するだけで十分です。 そしお、明日発生する顧客の垌望を掚枬しお先に進むために、私たちは基本的に望んでいたせん。 だから-リストが最も正しいです。 最も可胜性が高い。 わからない。 しかし、しないでください。 その堎合は、い぀でも曞き換えが可胜です。

䞊行しお、名前を倉曎したした。今はパンではなくベヌカリヌ補品を焌いおいたす。

 public enum BakeryProductType { Bread, MeatPasty, CabbagePasty, Cake } public class BakeryProduct { public BakeryProduct(BakeryProductType bakeryProductType) { this.BakeryProductType = bakeryProductType; } public BakeryProductType BakeryProductType { get; private set; } } class BakeryProductMaker { public static BakeryProduct BakeByGasOven(BakeryProductType bakeryProductType, double gasLevel) { return gasLevel == 0 ? null : new BakeryProduct(bakeryProductType); } public static BakeryProduct BakeByOtherOven(BakeryProductType breadType) { return new BakeryProduct(breadType); } } 


芁件5
-さたざたなレシピに埓っおパン、パむ、ケヌキを焌く必芁がありたす

分析

コヌドをざっず芋おみるず、BakeryProductTypeのすばらしいリストがあるこずがわかりたす。 サブゞェクト゚リアの近くではなく、なんずなく䞍噚甚に、䜕らかの方法でプログラム的に呌び出されたす。 しかし、それはレシピのように振る舞いたす。 䜕が起こるか パンずロヌルは、タむプではなく、レシピに埓っお焌きたす。 そしお、おそらく、レシピはロヌルデザむナヌに䌝わりたす。 名前を倉曎するのに十分。 唯䞀の障害はお団子財産です。 しかし、私は自分自身を蟞任したでしょう。 コヌドを機械的に芋お、サブゞェクト領域をいく぀かのセットずしお想像するず、レシピずタむプの間に倧きな違いは芋られたせん。 ぀たり レシピは、埌で起こるこずの盎接の理由です。 もちろん、生掻の䞭で、私たちはレシピに぀いおもう少し知っおいたす-圌らは䜕が起こるかを蚘述するだけではありたせん。 たた、取埗アルゎリズムも含たれおいたす。 しかし、誰が気にしたすか 顧客はこれに぀いお話したしたか いや これは、タスクのコンテキストではこれが発生しなかったこずを意味したす。 アルゎリズムが必芁です-埌でバむンドし、䜕かを考え出したす。
したがっお、プロパティは型のたたであり、列挙はレシピのたたであるずいう事実に我慢したす。 話し蚀葉の特性のため、盞続人や別のリストを䜜成しないでください。 タスクのコンテキストでは、すべおが正確です。 あたり矎しくありたせんが。 劥協

 public enum Recipe { Bread, MeatPasty, CabbagePasty, Cake } public class BakeryProduct { public BakeryProduct(Recipe recipe) { this.BakeryProductType = recipe; } public Recipe BakeryProductType { get; private set; } } class BakeryProductMaker { public static BakeryProduct BakeByGasOven(Recipe recipe, double gasLevel) { return gasLevel == 0 ? null : new BakeryProduct(recipe); } public static BakeryProduct BakeByOtherOven(Recipe recipe) { return new BakeryProduct(recipe); } } 


芁件6
「レンガをオヌブンで焌く必芁がありたす。」

分析

この芁件ずすべおの曞面による芁件を文字通り守れば、レンガはケヌキやパンず倉わりたせん。 おもしろいこずに、私たちのレンガはゞャムパむずははるかに異なりたす。芁件にそれが含たれおいないためです。 パンのように。 したがっお、YAGNIによるこの芁件は倧幅に誇匵されおおり、すべおのクラスの名前を倉曎しおレシピの列挙を拡匵するこずによっおのみ実珟されたす。これは、レンガなどの「オヌブン補品」のロヌルです。 クラスアヌキテクチャを䜜成する方法の党䜓的なポむントは、その䜿甚方法です。 これは、䞀般的ず芋なされるもの぀たり、基本クラスの状態ず動䜜およびプラむベヌト継承者の状態ず動䜜からのものです。 どちらも存圚しない堎合、列挙も可胜です。 掚枬しないこずは怖くない。 クラスず盞続人ぞの列挙は簡単に倉換されたす。

コヌドに恐怖がありたしたか このコヌドをテストするのは難しいかもしれたせんか はい、ボリスのコヌドはテストするのがはるかに難しいようです。 ボリュヌムは、より倚くのテストです。 必芁以䞊の機胜がありたすか より倚くのテスト。
もちろん、明らかに、元の投皿は、芁件がより詳现であり、各フレヌズが詳现な説明で明確にされたこずを暗瀺しおいたした。 しかし、YAGNIゞャンルは考えないこずを芁求したす。

芁件をさらに詊したしょう。

芁件7
「どうしお芋なかったの」 すべおのストヌブがレンガを燃やすこずができるわけではありたせん。 これを行うには、特別なオヌブンが必芁です。

分析

いいでしょう 列挙からブリックを削陀しレシピ、名前を返したす。 別の空のBrickクラスを䜜成したす。 そしお新しい方法

 public static Brick MakeBrickByFurnace() { return new Brick(); } 


ずころで、柔軟性が今必芁ない堎合は、誰もが䜕らかの皮類のオブゞェクトを正確に生成する豊富なメ゜ッドが、オブゞェクトを䜜成する柔軟な方法よりも優れおいたす。 柔軟性が必芁でない堎合、プログラムは蚱可を少なくし、制限を増やす必芁がありたす。 特定のタむプのオブゞェクトをむンタヌフェむスに眮き換えるこずがしばしば䟿利なナニットテストは考慮したせん。 このコヌドはすべお、堎合によっおは簡単にむンタヌフェむスに倉換されたす。 はい、たた、Cを反映したものは、ある皮のデカップリングのテストではそれほど芁求されたせん。

さらに、顧客はマヌカスず察戊するこずにしたした。

芁件8
-各レシピには、補品ずその量重量を含める必芁がありたす。 需芁のレシピが添付されおいたす。

分析

ボリスが埅っおいた最初の血。

ひどいスパゲッティコヌドに察凊しおみたしょう。これは、ここで長い間圢成されおいたはずであり、リファクタリングする機䌚を䞎えおくれたせん。 そうですか

レシピの補品は明らかにリストされおいたす。 レシピ自䜓には、䜜成するものの名​​前たたは同じもの、それ自䜓の名前だけでなく、数量ず補品のセットも既に含たれおいたす。 しかし、同時に、補品の厳密なセットが特定のレシピに関連付けられおおり、倉曎されおいないこずに気付きたす。 もう䞀床、YAGNIに぀いおの投皿を思い出したす-「予備金を倉曎したい堎合はどうでしょう」いいえ、突然-今日-これは今日、そしお明日-これは明日です。

぀たり 顧客は、レシピの補品ず重量が異なる可胜性があるずは蚀わなかった。 もちろん、圌はそれらを修正すべきだずは蚀わなかった。 しかし、固定されたケヌスはより限定的で厳密です。 そしお、より限られたケヌスを垞に遞択したす。 私たちにずっおは、柔軟性を高めるのではなく、よりシンプルで厳栌な方が良いのです。

そしお、厳密な補品セットを備えたレシピは、個人的な䜓隓により良く察応しおいたす。 この堎合、継承を䜿甚しお各レシピのクラスを蚘述するこずは実甚的ではありたせん。 その埌、各クラスは定数のみを保存したす。

そしおさらにいく぀かの考え。 なぜなら 珟時点では、コヌド内のレシピは指定された列挙にすぎず、コンパむル前に蚭定されおいたす。他に芁件がない堎合は、明らかにこの動䜜が残っおいるはずです。 これから、すべおのレシピが利甚可胜になり、コヌドで盎接蚭定されるこずになりたす。 列挙型拡匵機胜なしで新しいものを䜜成するこずはできたせん。 ここから、RecipeNameで同じ名前の列挙の名前を倉曎した埌、Recipeクラスを䜜成する必芁があるようです。 䞖界は非垞に䞍安定です。 珟圚、リストにはレシピのみが瀺されおおり、レシピを遞択するこずはできたすが、完党には特城付けられおいたせん。

䞊蚘の条件を満たすには、次のようにしたす。

 public enum RecipeName { Bread, MeatPasty, CabbagePasty, Cake } public enum RecipeProduct { Salt, Sugar, Egg, Flour } public class Recipe { private Recipe() { } public RecipeName Name { get; private set; } public IEnumerable<KeyValuePair<RecipeProduct, double>> Products { get; private set; } private static Dictionary<RecipeName, Dictionary<RecipeProduct, double>> predefinedRecipes; static Recipe() { predefinedRecipes = new Dictionary<RecipeName, Dictionary<RecipeProduct, double>> { { RecipeName.Bread, new Dictionary<RecipeProduct, double> { {RecipeProduct.Salt, 0.2}, {RecipeProduct.Sugar, 0.4}, {RecipeProduct.Egg, 2.0}, {RecipeProduct.Flour, 50.0} } } .................. }; } public static Recipe GetRecipe(RecipeName recipeName) { Recipe recipe = new Recipe(); recipe.Name = recipeName; recipe.Products = predefinedRecipes[recipeName]; return recipe; } } 


䜕も壊す必芁はありたせん。 補品を䜜成するには、今のずころレシピの名前で十分です。 そこでは䜕も倉曎したせん。 それが必芁になりたす、我々はレシピ自䜓を枡したす。

このコヌドでは、レシピ䞀芧からクラスを䜜成し、レシピの名前ずそれを構成する補品を関連付けたした。 「ねじ蟌み」できるように、レシピでアクションのシヌケンスを衚珟する必芁がありたす。 これが明確で、スパゲッティコヌドがないこずを願っおいたす。 クラスごずに別の動䜜が衚瀺されたす。Recipeクラスが簡単に基本クラスになり、盞続人が衚瀺されたす。 しかし、私たちはそれに぀いお考えたせん。 YAGNIがありたすが、これを行うように蚀われおいたせん。 しかし、私たちはこれを恐れおいたせん。

芁件9
蚈画倖のアプロヌチを知った䞋品な顧客は、それをキャッチするこずにしたした。
「レシピを倉えおほしい。」 ストヌブは、料理人が䜜成したレシピに埓っお調理されたした。

論争に入る
-倉曎方法
-料理人はレシピを知らず、実隓できるず信じおいたす。 レシピを修正したしたか そしお、圌は異なる量の卵、砂糖などを远加したいず考えおいたす。
-そしお、この堎合、どのようなパンを手に入れたすか 䜕か手に入れるべき パン、ケヌキ、たたはケヌキ 明らかに、レシピが異なる堎合、料理人は䜕か他のものを焌きたす。
-ケヌキは味が異なり、甘く、甘くないず思いたす。 パンも。 そのため、レシピはある皋床異なる堎合がありたすが、リストから補品を入手したす。
-぀たり 䜕が埗られるのかを知るには、料理人のレシピの補品リストに最も近いレシピを探す必芁がありたすか
-はい。

分析

レシピを修正したした。 珟圚、レシピは修正されおいない可胜性がありたす。 しかし、私たちが持っおいるものはベンチマヌクです。 コヌドのナヌザヌが独自のレシピを䜜成できるようにするには、コンストラクタヌを開いおください。 しかし、補品を尋ねる機䌚を䞎えるこずも必芁です。 プロパティをナヌザヌに割り圓おたり、特定のタむプを指定したりする機䌚を䞎えたくありたせん。 そうでなければ、圌は私たちの基準を損なうこずができたす。 したがっお、最も簡単な方法は、デザむナヌに補品を転送する機䌚を䞎えるこずです。 たた、䜜成ず初期化の間のギャップをなくし、バグの可胜性を枛らしたす。

これで2぀のコンストラクタヌができたした。

 private Recipe() { } public Recipe(IEnumerable<KeyValuePair<RecipeProduct, double>> products) { Dictionary<RecipeProduct, double> copiedProducts = new Dictionary<RecipeProduct, double>(); foreach (KeyValuePair<RecipeProduct, double> pair in products) { copiedProducts.Add(pair.Key, pair.Value); } this.Products = copiedProducts; } 


2番目のコンストラクタヌはコピヌを䜜成したす。 これは、Cプロパティ-デフォルトのリンクを枡すためです。 クラスのナヌザヌにはリンクがあり、コピヌを䜜成しない堎合は、埌でレシピの材料を倉曎できたす。 蚈画に含たれおいないもの。

たた、この投皿では、暙準OOPのフレヌムワヌク内に留たり、ラムダずzheneriksを䜿甚しないようにしおいたす。 より倚くの聎衆が私がしおいるこずを理解できるように。 コヌドは異なる方法で簡単に蚘述できたす。 しかし、私の目暙は、YAGNIの原理自䜓ずコヌドを評䟡するいく぀かの方法を説明するこずであり、シャヌプのさたざたな可胜性を瀺すこずではありたせん。 もちろん、評䟡方法は蚀語ずその機胜に䟝存したす。

ナヌザヌ甚の2番目のコンストラクタヌは、プロパティの倀レシピの名前を蚭定したせん。 なぜなら 私たちの補品はデザむナヌに枡され、倉曎するこずはできたせん。そこで、そこから䜕らかの方法で近接床を蚈算したす。 より正確には、特に「スマヌト」は倉化する可胜性がありたすが、パラノむアに悩むこずはありたせん。 開発者は適切であり、砎壊ではなく創造の立堎にあるず信じおいたす。

䜕らかの近接メ゜ッドを曞く必芁がありたす。 顧客は指定しなかったので、最小二乗法で最も単玔なものを蚘述したす。 各成分に異なる「重量」があるこずを考えるず。 今のずころ、埌で調敎できる重みを曞き留めおおきたす。

コヌドはほが次のようになりたす。

 private double GetDistance(Recipe recipe) { Dictionary<RecipeProduct, double> weights = new Dictionary<RecipeProduct, double>(); weights[RecipeProduct.Salt] = 50; weights[RecipeProduct.Sugar] = 20; weights[RecipeProduct.Egg] = 5; weights[RecipeProduct.Flour] = 0.1; double sum = 0.0; foreach(KeyValuePair<RecipeProduct, double> otherProductAmount in recipe.Products) { var productAmounts = this.Products.Where(p => p.Key == otherProductAmount.Key); if (productAmounts.Count() == 1) { sum += Math.Pow(productAmounts.First().Value - otherProductAmount.Value, 2) * weights[otherProductAmount.Key]; } else { return double.MaxValue; } } return sum; } private RecipeName GetRecipeName() { IEnumerable<Recipe> etalons = ((RecipeName[])Enum.GetValues(typeof(RecipeName))) .Select(recipeName => Recipe.GetReceipt(recipeName)); IEnumerable<KeyValuePair<RecipeName, double>> recipeNamesWithDistances = etalons .Select(e => new KeyValuePair<RecipeName, double>(e.Name, GetDistance(e))); double minDistance = recipeNamesWithDistances.Min(rd => rd.Value); if (minDistance == double.MaxValue) { throw new Exception("   "); } return recipeNamesWithDistances.First(rd => rd.Value == minDistance).Key; } 


そしお、それぞれコンストラクタヌ呌び出しで、呜名が远加されたす

 public Recipe(IEnumerable<KeyValuePair<RecipeProduct, double>> products) { Dictionary<RecipeProduct, double> copiedProducts = new Dictionary<RecipeProduct, double>(); foreach (KeyValuePair<RecipeProduct, double> pair in products) { copiedProducts.Add(pair.Key, pair.Value); } this.Products = copiedProducts; this.Name = GetRecipeName(); } 


垞にオンザフラむで蚈算する方が信頌性が高くなりたす。 しかし、その堎合は、参照甚ず非固定レシピ甚の呜名の分離を考え出す必芁がありたす。 今のずころこれで十分です。

ご芧のずおり、このような珟圚の芁件を満たすためにコヌドをやり盎すこずはたったく怖いこずではありたせん。 䜕も壊したせんでした そしおちょうど拡倧したした。 事前に予枬しおいた堎合よりも、完了する時間はありたせん。 しかし、掚枬ではなく予枬するこずは本圓に怖いです。 この芁件に埓っお、䞀芋シンプルに芋えるこのコヌドを想像しおみおください。 これは、远加テストが必芁な単なるモンスタヌです。 そしお今、それは合理的に曞かれおいたす。

コヌドは完璧ではありたせん、私は助けるこずができたせんでしたが、zhenerikiずlambdaに突入したす。 そのため、コヌドはより小さく、よりきれいになりたす。 これにより、Cやラムダに慣れおいない読者の理解が損なわれないこずを願っおいたす。 もちろん、さらに枛らすこずができたす。 しかし、私はより倚くの人々に理解されようずしおいたす。

ここでは、特定のアルゎリズムに既に関連付けられおいたすが、この顧客はこれを必芁ずしたせんでした。 ダグニかどうか ここで状況を理解したす。 おそらく、顧客はすぐに目に芋える結果を必芁ずしたす。 これはよく起こりたす。 したがっお、少なくずもいく぀かのアルゎリズムが必芁です。 しかし、埌で別のアルゎリズムが必芁になった堎合、このアルゎリズムを別のアルゎリズムに眮き換えるのに費甚はかかりたせん。 たたは、いく぀かを曞いお遞択するこずもできたす。 たたは、コヌドのナヌザヌからでも、近接性を比范するデリゲヌトを取埗したす。

明確なビゞネス、今では補造方法にレシピの名前ではなく、レシピ自䜓を転送する必芁がありたす。 ぀たり このように

 public class BakeryProduct { public BakeryProduct(Recipe recipe) { this.BakeryProductType = recipe.Name; } public RecipeName BakeryProductType { get; private set; } } 


そしお

 public static BakeryProduct BakeByGasOven(Recipe recipe, double gasLevel) { return gasLevel == 0 ? null : new BakeryProduct(recipe); } public static BakeryProduct BakeByOtherOven(Recipe recipe) { return new BakeryProduct(recipe); } 


ここで、リファクタリングは過剰なストレスからはほど遠い。

芁件10
油断ならない顧客はどういうわけか私たちのコヌドを研究し、私たちのコヌドが完党に準備されおいない最も苊しい堎所を探しおいたす。 9 . . .

— , , , . , . , , , , . ( ))), , .. , .

分析

, . , . . , , ? , , . , . , , . , . ? . すべきではない。

, – . . , , – , . , , . . Product. Product? . . , , . , , . . ぀たり , , . , . , – . ? .

. , . ぀たり . , , , , ..

぀たり – , .

-.
, , : « , . , ( ). . , .»

. – .

 public abstract class Article { public double Price { get; private set; } public Article(double price) { this.Price = price; } } 


:

 public class BakeryProduct : Article { public BakeryProduct(Recipe recipe, double price): base(price) { this.BakeryProductType = recipe.Name; } public RecipeName BakeryProductType { get; private set; } } public class Brick: Article { public Brick(double price) : base(price) { } } 


, , .

このようなもの

 public static BakeryProduct BakeByOtherOven(Recipe recipe, double price) { return new BakeryProduct(recipe, price); } 


. . , . , . , – . , , . , .. ( , ..) , . , – .

switch , , , , . . . . , . , .

, , . . . , , . . . . . , . YAGNI , . , , . - YAGNI . .

, . . , , , YAGNI . . . , — .

Source: https://habr.com/ru/post/J153845/


All Articles