1行のコードを実行し、プログラムで1回だけ呼び出されるパラメーターのない(edit:必ずしも必要ではない)関数を考えます(将来再び必要になることは不可能ではありません)。 。
それはクエリを実行し、いくつかの値をチェックし、正規表現を含む何かをすることができます...あいまいなまたは「ハッキー」なもの。
これの背後にある理論的根拠は、ほとんど読めない評価を避けることです:
_if (getCondition()) {
// do stuff
}
_
ここで、getCondition()
は1行の関数です。
私の質問は単純です:これは良い習慣ですか?私には問題ないようですが、長期的にはわかりません...
その1行に依存します。行が読みやすく簡潔である場合、関数は必要ない場合があります。単純な例:
void printNewLine() {
System.out.println();
}
OTOH、たとえば、関数が次を含むコード行に適切な名前を付ける場合:複雑で読みにくい表現で、完全に正当化されています(私にとって)。不自然な例(ここでは読みやすくするために複数行に分割しています):
boolean isTaxPayerEligibleForTaxRefund() {
return taxPayer.isFemale()
&& (taxPayer.getNumberOfChildren() > 2
|| (taxPayer.getAge() > 50 && taxPayer.getEmployer().isNonProfit()));
}
はい、これはベストプラクティスを満たすために使用できます。たとえば、明確な名前の関数を使用すると、たとえそれが1行しかない場合でも、より大きな関数内にそのコード行を含めて、その機能を説明する1行のコメントが必要になるよりも、いくつかの作業を行う方が適切です。また、コードの隣接する行は、同じ抽象化レベルでタスクを実行する必要があります。反例は次のようなものです
startIgnition();
petrolFlag |= 0x006A;
engageChoke();
この場合は、真ん中の行をわかりやすい名前の関数に移動する方が間違いなく優れています。
多くの場合、このような関数は良いスタイルだと思いますが、この条件を他の場所のどこかで使用する必要がない場合は、ローカルのブール変数を代替と見なすことができます。
bool someConditionSatisfied = [complex expression];
これは、コードリーダーにヒントを与え、新しい関数の導入を回避します。
Peter's answer に加えて、その条件を将来のある時点で更新する必要がある場合は、単一の編集ポイントのみを使用することを提案した方法でカプセル化することにより、.
ピーターの例に従って、これが
boolean isTaxPayerEligibleForTaxRefund() {
return taxPayer.isFemale()
&& (taxPayer.getNumberOfChildren() > 2
|| (taxPayer.getAge() > 50 && taxPayer.getEmployer().isNonProfit()));
}
これになる
boolean isTaxPayerEligibleForTaxRefund() {
return taxPayer.isMutant()
&& (taxPayer.getNumberOfThumbs() > 2
|| (taxPayer.getAge() > 123 && taxPayer.getEmployer().isXMan()));
}
あなたは単一の編集を行い、それは普遍的に更新されます。保守性に関しては、これはプラスです。
パフォーマンスに関しては、ほとんどの最適化コンパイラは関数呼び出しを削除し、とにかく小さなコードブロックをインライン化します。このような最適化を行うと、実際にブロックサイズを縮小できるため(関数呼び出しや戻りなどに必要な命令を削除することにより)、インライン化を妨げる可能性がある状況でも通常は安全です。
読みやすさに加えて(またはそれを補完して)、これにより、適切な抽象化レベルで関数を記述できます。
場合によります。たった1行であっても、式を関数/メソッドにカプセル化する方が良い場合があります。読むのが複雑な場合や、複数の場所で必要な場合は、良い方法だと思います。 単一の変更点と読みやすさを導入したので、長期的には保守が容易になります。
ただし、必要のない場合もあります。とにかく式が読みやすい場合や、1か所にしか表示されない場合は、折り返さないでください。
それらの数が少ない場合は問題ないと思いますが、コードにそれらがたくさんあると問題が発生します。そして、コンパイラーが実行されるとき、または(使用する言語に応じて)インターピターがメモリ内でその関数に渡されます。コンピューターに気付かないと思いますが、そのうちの3つがあるとしましょう。ただし、100個の小さなものを使い始めた場合、システムは、一度だけ呼び出されて破棄されない関数をメモリに登録する必要があります。
コメントの代わりにコードの実際の意味を明確にするために、リファクタリングを行っているアプリケーションで最近、この正確なことを行いました。
protected void PaymentButton_Click(object sender, EventArgs e)
Func<bool> HaveError = () => lblCreditCardError.Text == string.Empty && lblDisclaimer.Text == string.Empty;
CheckInputs();
if(HaveError())
return;
...
}
すでに良い答えはたくさんありますが、特筆すべき特別なケースがあります。
1行のステートメントにコメントが必要で、その目的を明確に(つまり、名前を付ける)できる場合は、拡張しながら関数を抽出することを検討してくださいAPIドキュメントへのコメント。これにより、関数呼び出しをより速く簡単に理解できるようになります。
興味深いことに、現在何もする必要がない場合でも同じことができますが、拡張が必要であることを思い出させるコメント(非常に近い将来に)1))、 したがって、この、
def sophisticatedHello():
# todo set up
say("hello")
# todo tear down
これに変更することもできます
def sophisticatedHello():
setUp()
say("hello")
tearDown()
1) あなたはこれについて本当に確信しているはずです( [〜#〜] yagni [〜#〜] 原則を参照)
言語でサポートされている場合は、通常、ラベル付きの匿名関数を使用してこれを実現します。
someCondition = lambda p: True if [complicated expression involving p] else False
#I explicitly write the function with a ternary to make it clear this is a a predicate
if (someCondition(p)):
#do stuff...
IMHOこれは、複雑な式がif
条件を乱雑にせず、小さな使い捨てラベルでグローバル/パッケージ名前空間を乱雑にしないという読みやすさの利点を提供するため、良い妥協案です。これには、関数「定義」が適切に使用され、定義の変更と読み取りが容易になるという追加の利点があります。
述語関数である必要はありません。私はこのような小さな関数で繰り返しボイラープレートを囲むのも好きです(これは、ブラケット構文を乱雑にすることなく、Pythonicリストの生成で特にうまく機能します)。たとえば、PythonでPILを使用する場合の次の単純化された例
#goal - I have a list of PIL Image objects and I want them all as grayscale (uint8) numpy arrays
im_2_arr = lambda im: array(im.convert('L'))
arr_list = [im_2_arr(image) for image in image_list]
その1行を適切な名前のメソッドに移動すると、コードが読みやすくなります。他の多くの人はすでにそれについて言及しています(「自己文書化コード」)。メソッドに移動するもう1つの利点は、単体テストが簡単になることです。独自のメソッドに分離され、単体テストが行われると、バグが見つかった場合でも、このメソッドには含まれないことが確実になります。