関数の引数の数を最小化する手法


13

クリーンコードでは、「関数の引数の理想的な数はゼロ」と書かれています。理由は説明されており、理にかなっています。私が望んでいるのは、この問題を解決するために4つ以上の引数を持つメソッドをリファクタリングする手法です。

1つの方法は、引数を新しいクラスに抽出することですが、それは確かにクラスの爆発につながりますか?そして、それらのクラスは、いくつかの命名規則に違反する名前で終わる可能性が高い(「データ」または「情報」などで終わる)?

別の手法は、複数の関数で使用される変数をプライベートメンバー変数にして、それらを渡さないようにすることですが、変数のスコープを拡張し、おそらく実際にそれを必要としない関数に対して開かれるようにします。

関数の引数を最小化する方法を探し、それを行うのが良い考えである理由を受け入れました。


21
Tbh私はきれいなコードにはまったく同意しません。関数の引数の数がゼロの場合、それは関数に副作用があり、おそらくどこかで状態が変化することを意味します。私は4つ未満の引数が良い経験則であることに同意しますが、私はむしろ8つの引数が静的で副作用のない関数を持っています。
wasatz

4
クリーンコードでは、「関数の理想的な引数の数はゼロです」と書かれています。」それはとても間違っています!理想的なパラメーターの数は1つであり、戻り値はその1つのパラメーターから決定論的に導き出されます。実際には、パラメーターの数はそれほど重要ではありません。重要なのは、可能な場合は常に関数が純粋であることです(つまり、副作用のないパラメーターからのみ戻り値を導き出す)。
デビッドアルノ

2
さて本は...副作用が望ましいものではないことを指摘して、外出先で、後に行います
ニール・バーンウェル


回答:


16

覚えておくべき最も重要なことは、それらがルールではなくガイドラインであることです。

メソッドが単に引数を取る必要がある場合があります。+たとえば、数字の方法を考えてください。または、addコレクションのメソッド。

実際には、1でも、それは2つの数値を追加するために何を意味するのかℤで例えば、文脈に依存していると主張するかもしれません3 + 3 == 6が、ℤ中| 5 3 + 3 == 2ので、本当に加算演算子はかかるコンテキストオブジェクトのメソッドでなければなりません2 Aの引数を代わりに1つの引数を取る数値のメソッド。

同様に、2つのオブジェクトを比較するメソッドは、一方をもう一方を引数として取るメソッド、または2つのオブジェクトを引数として取るコンテキストのメソッドのいずれかでなければならないため、単に比較メソッドを使用するのは意味がありません引数が1つ未満。

そうは言っても、メソッドの引数の数を減らすためにできることがいくつかあります。

  • メソッド自体を小さくします。たぶん、メソッドがそのような多くの引数を必要とする場合、それはやりすぎです?
  • 欠落している抽象化:引数が密接に相関している場合、おそらくそれらは一緒に属していて、欠落している抽象化がありますか?(正規のテキストブックの例:2つの座標の代わりにPointオブジェクトを渡すか、ユーザー名とメールを渡す代わりにオブジェクトを渡しIdCardます。)
  • オブジェクト状態:複数のメソッドで引数が必要な場合、オブジェクト状態の一部である可能性があります。一部のメソッドでのみ必要であり、他のメソッドでは必要でない場合、オブジェクトが多すぎて、実際には2つのオブジェクトである必要があります。

1つの方法は、引数を新しいクラスに抽出することですが、それは確かにクラスの爆発につながりますか?

ドメインモデルにさまざまな種類のものがある場合、コードはさまざまな種類のオブジェクトになります。それには何の問題もありません。

そして、それらのクラスは、いくつかの命名規則に違反する名前で終わる可能性が高い(「データ」または「情報」などで終わる)?

適切な名前が見つからない場合は、多すぎる引数をグループ化したか、少なすぎます。そのため、クラスのフラグメントだけがあるか、複数のクラスがあります。

別の手法は、複数の関数で使用される変数をプライベートメンバー変数にして、それらを渡さないようにすることですが、変数のスコープを拡張し、おそらく実際にそれを必要としない関数に対して開かれるようにします。

すべてが同じ引数で動作するメソッドのグループとそうでないメソッドのグループがある場合、それらは異なるクラスに属している可能性があります。

「たぶん」という言葉の使用頻度に注意してください。それがルールではなくガイドラインである理由です。たぶん、4つのパラメーターを使用したメソッドはまったく問題ありません!


7
@BrunoSchäpper:確かに:(1)「メソッド自体を小さくします。たぶん、メソッドがそれほど多くの引数を必要とするなら、それはやりすぎです?」paramsの数は、これの不十分なテストです。オプション/ブール型のパラメータと多くのコード行は、メソッドが多すぎることを示す強力な指標です。多くのパラメータはせいぜい弱いものです。(2)「オブジェクトの状態:引数が複数のメソッドで必要な場合、オブジェクトの状態の一部である可能性があります」いいえ、いいえ、3回、いいえ。オブジェクトの状態を最小化します。関数パラメーターではありません。可能であれば、パラメータを介してすべてのメソッドに値を渡し、オブジェクトの状態を回避します。
デビッドアルノ

あなたが追加のために与えた例は、完全に間違っています。add自然数とする機能add整数環のための機能は、mod nを二つの異なるタイプの2つの異なる機能のアクションです。「コンテキスト」の意味もわかりません。
ガーデンヘッド

Thx @DavidArno。1)同意したが、それ自体は強力な指標ではない。しかし、それでも良いものです。多くの場合、いくつかのメソッドが表示され、いくつかのオブジェクトが渡されます。オブジェクトの状態はありません。それは良いことですが、2)より良いオプションIMHOはこれらのメソッドをリファクタリングし、暗黙的な状態を新しいクラスに移動します。新しいクラスはこれらすべてのパラメータを明示的な引数として受け取ります。最終的に、1つの公開ゼロ引数メソッドと、多数のゼロから1引数の内部メソッドになります。状態は公開されておらず、グローバルでもなく、長期にわたって維持されていますが、コードはずっときれいです。
ブルーノシェーパー

6

オブジェクトは暗黙的な引数であるため、引数がゼロであることは副作用を意味しないことに注意してください。いくつのゼロアリティメソッドを見るたとえば、Scalaの不変リストに含まれる

私が「レンズフォーカシング」テクニックと呼ぶ便利なテクニックの1つです。カメラレンズの焦点を合わせるとき、遠くに移動して正しいポイントに戻すと、真のフォーカスポイントが見やすくなります。ソフトウェアリファクタリングについても同じことが言えます。

特に、分散バージョン管理を使用している場合、ソフトウェアの変更は簡単に試してみて、見た目が好きかどうかを確認し、気に入らない場合は元に戻してください。

あなたの現在の質問の文脈では、それは最初にいくつかの分割された関数でゼロまたは1つの引数バージョンを書くことを意味し、次に読みやすくするためにどの関数を組み合わせる必要があるかを比較的簡単に見ることができます。

作者はテスト駆動開発の大擁護者でもあることに注意してください。これは、ささいなテストケースから始めるため、最初は低アリティ関数を生成する傾向があります。


1
「レンズフォーカシング」のアナロジーのように-特にリファクタリング時には、クローズアップレンズの代わりに広角レンズを使用することが重要です。パラメーターの数を調べることは、近すぎます
-tofro

0

関数への引数の数を減らすことを単純に(そして単純に-または盲目的に言うべきです)アプローチは、おそらく間違いではありません。多数の引数を持つ関数には何も問題はありません。ロジックで必要な場合、それらは必要です...長いパラメーターリストは、読みやすくするために適切にフォーマットされ、コメント化されている限り、まったく心配しません。

全てまたは引数のサブセットが1つの独特の論理エンティティに属し、一般的に、あなたのプログラム全体のグループに回された場合には、それは多分、一般的に構造体や他のオブジェクト-いくつかのコンテナにグループ彼らに意味があります。典型的な例は、ある種のメッセージまたはイベントデータタイプです。

そのアプローチを簡単にやり直すことができます-そのようなトランスポートコンテナーとの間で荷物の梱包と開梱が読みやすさを向上させるよりも多くのオーバーヘッドを生成することがわかったら、おそらく行き過ぎです。

OTOH、大きなパラメーターリストは、プログラムが適切に構造化されていないことを示している可能性があります-そのような多数のパラメーターを必要とする関数は、多すぎること試みているため、いくつかの小さな関数に分割する必要があります。パラメータの数を心配するよりも、ここから始めたいと思います。


5
もちろん、引数の数を盲目的に減らすことは間違っています。しかし、「引数の数が多い関数にはまったく問題はありません」には同意し ません。。私の意見では、多数の引数を持つ関数をヒットすると、すべての場合の99.9%で、関数の引数の数を減らす(また)意図的な方法でコードの構造を改善する方法があります。
ドックブラウン

@DocBrownですから、この最後の段落とそこから始めることをお勧めします。そしてもう1つ:MS Windows APIに対してプログラムしようとしたことはおそらくないでしょう;)
tofro

「おそらく、このような多数のパラメーターを必要とする関数は、あまりにも多くのことをしようとしているだけであり、いくつかの小さな関数に分割する必要があります。」私は完全に同意しますが、実際には、これらのいくつかの小さな関数を呼び出すスタックの上位に別の関数が存在するだけではありませんか?その後、それらをオブジェクトにリファクタリングできますが、そのオブジェクトにはアクターがあります。あなたはビルダーの何とか何とか何とかを使用できます。ポイントは、それが無限回帰だということです-どこかに、ソフトウェアが仕事をするために必要な多くの値があり、それらは何らかの形でそれらの機能に到達しなければなりません。
ニールバーンウェル

1
@NeilBarnwell理想的な場合(リファクタリングに値する)では、10個の引数が必要な関数を、それぞれ3〜4個の引数が必要な3つに分割できる場合があります。それほど理想的ではない場合、それぞれ10個の引数を必要とする3つの関数になります(その後、そのままにしておく方が良いでしょう)。上位のスタック引数に関して:同意-場合もありますが、必ずしもそうではありません-引数はどこかから来ており、その検索はスタック内のどこかにある可能性があり、適切な場所に置く必要があります-マイレージ変化する傾向があります。
トフロ

ソフトウェアロジックには、4つ以上のパラメーターは必要ありません。コンパイラーのみが可能です。
theDoctor

0

魔法の方法はありません。正しいアーキテクチャを発見するには、問題の領域をマスターする必要があります。それがリファクタリングの唯一の方法です。問題のあるドメインをマスターすることです。4つ以上のパラメーターは、現在のアーキテクチャに障害があり、間違っていることを確実に示しています。

唯一の例外はコンパイラ(メタプログラム)とシミュレーションです。制限は理論的には8ですが、おそらく5だけです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.