web-dev-qa-db-ja.com

関数が短すぎることはありますか?

同じロジックを複数回記述していることに気付いたときはいつでも、私は通常それを関数に貼り付けます。そのため、アプリケーションでそのロジックを維持する必要がある場所は1つだけです。副作用として、次のような1つまたは2つの行関数が発生することがあります。

function conditionMet(){
   return x == condition;
}

OR

function runCallback(callback){
   if($.isFunction(callback))
     callback();
}

これは怠惰ですか、それとも悪い習慣ですか?これにより、非常に小さなロジックの関数呼び出しの数が増えるためです。

128
Mark Brown

Hehe、ああブラウンさん、私が出会ったすべての開発者に機能をこれほど小さく保つように説得できれば、ソフトウェアの世界がより良い場所になると信じてください!

1)コードの可読性が10倍になります。

2)読みやすさのため、コードのプロセスを簡単に理解できます。

3)DRY-自分を繰り返さないでください-あなたはこれにとても順応しています!

4)テスト可能。小さな関数のテストは、私たちが頻繁に目にする200行のメソッドよりも100倍も簡単です。

ああ、パフォーマンスの面で「関数ホッピング」を心配する必要はありません。 "リリース"ビルドとコンパイラの最適化により、これが適切に処理され、パフォーマンスはシステム設計の他の場所の99%の時間です。

これは怠惰ですか? -正反対です!

これは悪い習慣ですか? - 絶対違う。あまりにも一般的すぎるtarボールや「God Objects」よりも、この方法でプルする方が良いです。

私の仲間の職人である良い仕事を続けてください;)

166
Martin Blore

次のいずれかの場合、リファクタリングされたメソッドは短すぎると言えます。

  • メソッドにする以外の目的ではなく、プリミティブな操作を複製します。

例:

boolean isNotValue() {
   return !isValue();
}

または...

  • コードは一度だけ使用され、その意図は一目で理解しやすいです。

例:

void showDialog() {
    Dialog singleUseDialog = new ModalDialog();
    configureDialog(singleUseDialog);
    singleUseDialog.show();
}

void configureDialog(Dialog singleUseDialog) {
    singleUseDialog.setDimensions(400, 300);
}

これは有効なパターンになる可能性がありますが、この例では、オーバーライドしたり、このコードを他の場所で再利用したりしない限り、configureDialog()メソッドをインライン化します。

64
RMorrisey

関数が短すぎることはありますか?一般的にはありません。

実際には、それを確実にする唯一の方法:

  1. デザインにすべてのクラスが見つかりました
  2. 関数が実行しているのは1つだけです。

関数をできるだけ小さく保つことです。または、言い換えると、これ以上抽出できなくなるまで、関数から関数を抽出します。これを「落とすまで抜粋」と呼んでいます。

これを説明するには:関数は、変数によって通信する機能のチャンクを持つスコープです。クラスは、変数によって通信する機能のチャンクを持つスコープでもあります。そのため、長い関数は常に小さなメソッドを持つ1つ以上のクラスで置き換えることができます。

また、関数から別の関数を抽出するのに十分な大きさの関数は、複数のことを行っています定義により。したがって、もしあなたがcan別の関数を抽出するなら、あなたはshouldその関数を抽出する。

一部の人々は、これが機能の拡散につながることを心配しています。彼らは正しい。そうなる。それは実際には良いことです。関数には名前があるので良いです。適切な名前の選択に注意する場合、これらの関数は、コードを通じて他のユーザーを誘導する標識投稿として機能します。実際、名前の付いた名前空間内の名前の付いたクラス内の名前の付いた関数は、読者が迷子にならないようにするための最良の方法の1つです。

これについては、cleancoders.comのClean CodeのエピソードIIIでさらに詳しく説明されています。

59
Uncle Bob.

うわー、これらの答えのほとんどはまったく役に立ちません。

IDがその定義である関数は記述しないでください。つまり、関数名が単に英語で書かれた関数のコードブロックである場合は、関数として記述しないでください。

あなたの関数conditionMetとこの他の関数addOneを検討してください(さびたJavaScriptを許してください):

function conditionMet() { return x == condition; }

function addOne(x) { return x + 1; }

conditionMetは適切な概念的な定義です。 addOneトートロジー です。 conditionMetconditionMetが何を伴うかわからないので良いですが、「条件が満たされた」と言っても、英語で読むとaddOneが馬鹿げている理由がわかります。 :

"For the condition to be met is for x to equal condition" <-- explanatory! meaningful! useful!

"To add one to x is to take x and add one." <-- wtf!

まだ神聖であるかもしれない何かの愛のために、トートロジー関数を書かないでください!

(そして同じ理由で コードのすべての行にコメントを書かないでください !)

53
Rei Miyasaka

コメントを追加することでコードの意図を改善できると思う場合は、コメントを追加するのではなく、コードを独自のメソッドに抽出します。コードがどんなに小さくても。

したがって、たとえば、コードが次のようになる場合:

if x == 1 { ... } // is pixel on?

代わりに次のようにします。

if pixelOn() { ... }

function pixelOn() { return x == 1; }

つまり、メソッドの長さではなく、自己文書化コードについてです。

14
Julio

これはまさにあなたがやりたいことだと思います。現在、その機能は1行または2行にすぎない可能性がありますが、時間の経過とともに拡大する可能性があります。また、より多くの関数呼び出しがあると、関数呼び出しを読み取って、そこで何が起こっているのかを理解できます。これにより、コードが非常に DRY(Do n't Repeat Yourself) となり、はるかに保守しやすくなります。

7
mpenrow

私が見た他のすべての投稿に同意します。これは良いスタイルです。

オプティマイザーが呼び出しを最適化してコードをインライン化できるため、このような小さなメソッドのオーバーヘッドはゼロになる場合があります。このような単純なコードは、オプティマイザが最善の作業を行うことを可能にします。

明快さと単純さのためにコードを書くべきです。メソッドを次の2つの役割のいずれかに制限しようとします。または仕事を行う。これにより、1行のメソッドが生成される場合があります。私がこれを上手にできるほど、自分のコードを見つけやすくなります。

このようなコードは、凝集度が高く、カップリングが低い傾向があるため、優れたコーディング方法です。

編集:メソッド名に関するメモ。メソッドがそれをどのように実行するのかではないことを示すメソッド名を使用します。 verb_noun(_modifier)は良い命名体系です。これにより、Select_Customer_Using_NameIdxではなくFind_Customer_ByNameのような名前が付けられます。 2番目のケースは、メソッドが変更されると不正確になる傾向があります。最初のケースでは、顧客データベースの実装全体を交換できます。

5
BillThor

1行のコードを関数にリファクタリングすることは、過剰に思われます。 ver loooooong/comples行またはexpessionsなどの例外的なケースがあるかもしれませんが、私はknow関数が将来的に成長するまで、これを行いません。

最初の例では、グローバル(コード内の他の問題について話す場合とそうでない場合があります)の使用を示唆しています。さらにリファクタリングして、これらの2つの変数をパラメーターとして作成します。

function conditionMet(x, condition){
   return x == condition;
}
....
conditionMet(1,(3-2));
conditionMet("abc","abc");

conditionMetの例mightは、次のように条件が長く繰り返される場合に役立ちます。

function conditionMet(x, someObject){
   return x == ((someObject.valA + someObject.valB - 15.4) / /*...whole bunch of other stuff...*/);
}

このことを考慮:

簡単な衝突検出機能:

bool collide(OBJ a, OBJ b)
{
    return(pow(a.x - b.x, 2) + pow(a.y - b.y, 2) <= pow(a.radius + b.radius, 2));
}

コードにその「単純な」1つのライナーを常に書き込んだ場合、最終的には間違いを犯す可能性があります。それに、それを何度も書くのは本当に大変なことです。

3
Mateen Ulhaq

短すぎると思いますが、これは私の主観です。

なぜなら:

  • 関数を1回または2回だけ使用する場合は、関数を作成する理由はありません。 Defs Suckへのジャンプ。特に驚くほど高速なVSおよびC++コードで。
  • クラスの概要。あなたが何千もの小さな機能を持っているとき、それは私を怒らせます。クラスの定義を表示し、それが何をするのかをすぐに確認できるときは、SetXToOne、SetYToVector3、MultiplyNumbers、+ 100セッター/ゲッターではなく、楽しんでいます。
  • ほとんどのプロジェクトでは、これらのヘルパーは1つまたは2つのリファクタリングフェーズの後で重荷になり、「すべて検索」->「削除」を実行して、古いコード(通常はその約25%以上)を取り除きます。

長い関数は悪いですが、3行より短く、1つの処理しか実行しない関数は、同じように悪いです。

だから私はそれがそうである場合にのみ小さな関数を書くと思います:

  • 3行以上のコード
  • ジュニア開発者が見逃す可能性があることを知っている(わからない)
  • 追加の検証を行います
  • 使用されている、または少なくとも3倍使用される
  • 頻繁に使用するインターフェースを簡素化
  • 次のリファクタリングの際に重荷にならない
  • テンプレートの特殊化など、特別な意味がある
  • いくつかの分離ジョブを実行します-const参照、変更可能なパラメーターに影響、プライベートメンバーの取得を実行します

次の開発者(上級者)は、すべてのSetXToOne関数を覚えておくことよりも、もっと良いことをするでしょう。そのため、どちらの方法でも、すぐに自重に変わります。

2
Coder

いいえ、それが問題になることはほとんどありません。誰かが関数を1行よりも長くすべきではないと感じた場合(それがそのように単純である場合のみ)、それは問題であり、何が適切かについて考えていないので、ある意味で面倒です。

2
JeffO

私は例が好きではありません。 1、i番目の総称名のため。

conditionMetは一般的ではないように思われるため、特定の条件を表していますか?お気に入り

isAdult () = { 
  age >= 18 
}

これで結構です。それは意味の違いですが、

isAtLeast18 () { age >= 18; } 

私には大丈夫ではないでしょう。

多分それは頻繁に使用され、後で変更される可能性があります:

getPreferredIcecream () { return List ("banana", "choclate", "Vanilla", "walnut") }

結構です複数回使用すると、必要に応じて1か所を変更するだけで済みます。明日はホイップクリームが可能になるかもしれません。

isXYZ (Foo foo) { foo.x > 15 && foo.y < foo.x * 2 }

アトミックではないため、素晴らしいテストの機会が与えられるはずです。

もちろん、関数を渡す必要がある場合は、好きなものを渡し、それ以外の場合はばかげて見える関数を記述します。

しかし、一般的に、短すぎる関数よりも長すぎる関数の方がはるかに多く見られます。

最後の言葉:一部の関数は冗長すぎるため、適切に見えるだけです。

function lessThan (a, b) {
  if (a < b) return true else return false; 
}

あなたが見れば、それは同じです

return (a < b); 

あなたは問題ありません

localLessThan = (a < b); 

の代わりに

localLessThan = lessThan (a, b); 
1
user unknown

コードなしのようなコードはありません!

シンプルに保ち、物事を複雑にしないでください。

それは怠惰ではなく、あなたの仕事をしています!

0
RDL

短すぎても問題ありません。関数が短い理由は次のとおりです。

再利用性

例えば。 setメソッドのような関数がある場合、パラメーターが有効であることを確認するためにそれをアサートする場合があります。このチェックは、変数が設定されているすべての場所で行う必要があります。

メンテナンス性

将来変更される可能性があると思うステートメントを使用する場合があります。例えば。列に記号を表示しますが、後で別の記号(またはビープ音)に変わる可能性があります。

均一性

あなたは使用していますファサードパターンと関数が行う唯一のことは、引数を変更せずに関数を別の関数に正確に渡すことです。

0
Michel Keijzers

コードセクションに名前を付ける場合、本質的にはnameを付けることです。これにはいくつかの理由がありますが、重要な点は、プログラムに追加する意味のあるの名前を指定できるかどうかです。 「addOneToCounter」のような名前は何も追加しませんが、conditionMet()は追加します。

スニペットが短すぎるか長すぎるかを見つけるためのルールが必要な場合は、スニペットの意味のある名前を見つけるのにかかる時間を考慮してください。適切な時間内にできない場合、スニペットは適切なサイズではありません。

0
user1249

はい、ショートコード機能があっても大丈夫です。以前の回答で述べたように、「ゲッター」、「セッター」、「アクセサ」などのメソッドの場合は非常に一般的です。

場合によっては、これらの短い「アクセサ」関数は仮想です。これは、サブクラスでオーバーライドされると、関数により多くのコードが含まれるためです。

グローバルでもメソッドでも、多くの関数でそれほど短くない機能が必要な場合、私は通常、直接の戻り値の代わりに「結果」変数(Pascalスタイル)を使用します。デバッガーを使用するときに非常に役立ちます。

function int CalculateSomething() {
  int Result = -1;

   // more code, maybe, maybe not

  return Result;
}
0
umlcat

いいえ、しかし、それは簡潔すぎる場合があります。

覚えておいてください:コードは一度だけ書かれますが、何度も読み込まれます。

コンパイラのコードを記述しないでください。あなたのコードを維持する必要がある将来の開発者のためにそれを書いてください。

0
Jim G.