Clean Code、35ページに、それは言う
これは、ifステートメント、elseステートメント、whileステートメントなどの中のブロックが1行の長さであることを意味します。おそらくその行は関数呼び出しでなければなりません。これにより、囲んでいる関数が小さく保たれるだけでなく、ブロック内で呼び出された関数にわかりやすい名前を付けることができるため、ドキュメント値も追加されます。
私は完全に同意します。それは非常に理にかなっています。
後で、40ページで、関数の引数について述べています
関数の引数の理想的な数はゼロ(ニラディック)です。次に1つ(モナディック)、2つ(ダイアディック)がそれに続きます。 3つの引数(3項)は可能な限り避けてください。 3つ以上(ポリアディック)は、非常に特別な正当化を必要とします。したがって、とにかく使用しないでください。議論は難しいです。彼らは多くの概念的な力を持っています。
私は完全に同意します。それは非常に理にかなっています。
ただし、かなり頻繁に、別のリストからリストを作成していることに気づき、2つの悪のうちの1つと共存する必要があります。
I ブロックの2行を使用、モノを作成するためのもの、結果に追加するためのもの:
public List<Flurp> CreateFlurps(List<BadaBoom> badaBooms)
{
List<Flurp> flurps = new List<Flurp>();
foreach (BadaBoom badaBoom in badaBooms)
{
Flurp flurp = CreateFlurp(badaBoom);
flurps.Add(flurp);
}
return flurps;
}
またはI 関数に引数を追加する事が追加されるリストの場合、「1つの引数が悪い」ことになります。
public List<Flurp> CreateFlurps(List<BadaBoom> badaBooms)
{
List<Flurp> flurps = new List<Flurp>();
foreach (BadaBoom badaBoom in badaBooms)
{
CreateFlurpInList(badaBoom, flurps);
}
return flurps;
}
私には見られない(不都合な)利点はありますか?または、特定の状況でそのような利点があります。その場合、決定を下すときに何を探す必要がありますか?
これらのガイドラインは地図ではなくコンパスです。彼らは賢明な方向であなたを指します。しかし、どのソリューションが「最適」であるかを絶対的に示すことはできません。目的地に到着したので、ある時点で、コンパスが指している方向に歩くのをやめる必要があります。
Clean Codeは、コードを非常に小さくて明白なブロックに分割することをお勧めします。それは一般的に良い方向です。しかし、(引用されたアドバイスの文字通りの解釈が示唆するように)極端に解釈すると、コードを無駄に小さい断片に細分することになります。何も実際には何もせず、すべて委任するだけです。これは本質的に別の種類のコード難読化です。
「小さすぎると役に立たない」と「小さければ良い」のバランスを取るのはあなたの仕事です。どちらのソリューションがより簡単かを自問してください。私にとって、それは明らかに最初の解決策です明らかにはリストを組み立てます。これはよく理解されているイディオムです。さらに別の関数を見なくても、そのコードを理解することができます。
もっとうまくできる場合は、「リストから別のリストにすべての要素を変換する」ことは、機能的なmap()
操作を使用して抽象化できることが多い一般的なパターンであることに注意してください。 C#では、Select
と呼ばれていると思います。このようなもの:
public List<Flurp> CreateFlurps(List<BadaBoom> badaBooms)
{
return badaBooms.Select(BadaBoom => CreateFlurp(badaBoom)).ToList();
}
関数の引数の理想的な数はゼロです(ニラディック)
番号!関数の引数の理想的な数は1です。ゼロの場合は、アクションを実行するために、関数が外部情報にアクセスする必要があることを保証しています。 「おじ」ボブはこれを非常に間違っていました。
コードに関しては、最初の行にローカル変数を作成しているため、最初の例ではブロックに2行しかありません。その割り当てを削除すると、次のクリーンコードガイドラインに準拠します。
public List<Flurp> CreateFlurps(List<BadaBoom> badaBooms)
{
List<Flurp> flurps = new List<Flurp>();
foreach (BadaBoom badaBoom in badaBooms)
{
flurps.Add(CreateFlurp(badaBoom));
}
return flurps;
}
しかし、それは非常に長いコード(C#)のコードです。次のようにしてください:
IEnumerable<Flurp> CreateFlurps(IEnumerable<BadaBoom> badaBooms) =>
from badaBoom in babaBooms select CreateFlurp(badaBoom);
「クリーンコード」アドバイスは完全に間違っています。
ループ内で2行以上を使用します。関数内で同じ2行を非表示にすることは、それらが説明を必要とするランダムな数学である場合は意味がありますが、行がすでに説明的である場合は何も行いません。 「作成」と「追加」
2行目を回避するために2番目の引数を追加する必要がないため、2番目の方法で言及しても意味がありません。
public List<Flurp> CreateFlurps(List<BadaBoom> badaBooms)
{
List<Flurp> flurps = new List<Flurp>();
foreach (BadaBoom badaBoom in badaBooms)
{
flurps.Add(badaBoom .CreateFlurp());
//or
badaBoom.AddToListAsFlurp(flurps);
//or
flurps.Add(new Flurp(badaBoom));
//or
//make flurps a member of the class
//use linq.Select()
//etc
}
return flurps;
}
または
foreach(var flurp in ConvertToFlurps(badaBooms))...
他の人が指摘しているように、最良の関数は引数のない関数であるというアドバイスは、OOPせいぜい、最悪の場合は明らかに悪いアドバイスに歪んでいます
CreateFlurpInList
はリストを受け入れてそのリストを変更するため、2番目の方法は明らかに悪いものです。メソッド名に、メソッドがリストに追加することのみを示唆するものはありません。
そして、私は3番目の最高のオプションを提供します:
public List<Flurp> CreateFlurps(List<BadaBoom> badaBooms)
{
return badaBooms.Select(CreateFlurp).ToList();
}
そして、地獄、それが使用される場所が1つしかない場合は、そのメソッドをすぐにインライン化できます。これは、ワンライナー自体が明確であるため、意味を持たせるためにメソッドによってカプセル化する必要がないためです。
1つの引数のバージョンの方が優れていますが、主に引数の数が原因ではありません。
それがより良い最も重要な理由は、それが低カップリングを持っていることです。
CreateFlurp(BadaBoom)
を提供していただければ、シンプルな_Flurp[]
_、_List<Flurp>
_、_LinkedList<Flurp>
_、_Dictionary<Key, Flurp>
_、等々。しかし、CreateFlurpInList(BadaBoom, List<Flurp>)
を使用して、明日、CreateFlurpInBindingList(BadaBoom, BindingList<Flurp>)
を要求するために返信します。これにより、ビューモデルがリストが変更されたという通知を取得できます。おい!
追加の利点として、単純な署名は既存のAPIに適合する可能性が高くなります。あなたは繰り返し問題があると言います
かなり頻繁に、別のリストからリストを作成している
利用可能なツールを使用するだけです。最も短く、最も効率的で、最良のバージョンは次のとおりです。
_var Flurps = badaBooms.ConvertAll(CreateFlurp);
_
List<T>.ConvertAll()
は、結果が入力と同じ数の項目を持つことを認識し、結果リストを事前に割り当てて正しいサイズ。コード(両方のバージョン)では、リストを増やす必要があります。
全体的な目標を覚えておいてください。コードを読みやすく、保守しやすくすることです。
多くの場合、複数の行を1つの意味のある関数にグループ化することが可能です。このような場合は、そうしてください。場合によっては、一般的なアプローチを再考する必要があります。
たとえば、あなたの場合、実装全体をvarに置き換えます
flups = badaBooms.Select(bb => new Flurp(bb));
可能性があります。またはあなたは次のようなことをするかもしれません
flups.Add(new Flurp(badaBoom))
時には、最もクリーンで読みやすいソリューションが1行に収まらないことがあります。したがって、2行になります。任意のルールを満たすためだけに、コードを理解しにくくしないでください。
2番目の例は、(私の意見では)最初のものよりも理解するのがかなり難しいです。 2番目のパラメーターがあるというだけでなく、パラメーターが関数によって変更されるということです。それについてClean Codeが何を言わなければならないか調べてください。 (現時点では本を手元に置いていないが、基本的に「回避できる場合はそのようなことをしない」と確信している)。