共通のコードを集中化するためだけに関数を使用するのは良い習慣ですか?


20

私はこの問題に頻繁に出くわします。たとえば、私は現在、読み取り関数と書き込み関数を作成していますが、どちらもbufNULLポインターであるかどうかと、mode変数が特定の境界内にあるかどうかをチェックします。

これはコードの重複です。これは、独自の機能に移動することで解決できます。しかし、私はすべきですか?これはかなり貧弱な機能であり(あまり機能しません)、むしろローカライズされているため(一般的な目的ではありません)、単独ではうまく機能しません(場所がわからなければ必要なものがわかりません)中古)。別のオプションはマクロを使用することですが、この投稿では関数についてお話したいと思います。

それで、あなたはこのような何かのために関数を使うべきですか?長所と短所は何ですか?


2
単一行のものは、別の機能に移行することを保証するために、複数の場所で使用される以上のものが必要です。

1
いい質問ですね。私は同じことを不思議に思ったことが何度もありました。
rdasxy

回答:


31

これは関数の素晴らしい使用法です。

これはかなり貧弱な機能になります(あまり機能しません)...

それは良い。関数は1つのことだけを行う必要があります。

むしろローカライズ...

オブジェクト指向言語では、プライベートにします。

(だから汎用ではない)

複数のケースを処理する場合、それ一般的な目的です。さらに、関数の使用は一般化だけではありません。それらは確かに(1)同じコードを複数回書くのを節約するためにありますが、(2)コードを小さなチャンクに分割して読みやすくするためにもあります。この場合、(1)と(2)の両方を達成しています。ただし、関数が1か所からしか呼び出されていない場合でも、(2)が役立つ場合があります。

単独ではうまく機能しません(使用場所がわからない限り、何に必要なのかわかりません)。

それに良い名前を思い付くと、それはそれ自体で問題ありません。「ValidateFileParameters」または何か。今ではそれだけで問題ありません。


6
まあ品物ポイント。(3)1つの場所で修正できる場合、コードベース全体で重複するバグを探すのを防ぐことができます。コードの複製は、保守の地獄に非常に速くつながる可能性があります。
deadalnix

2
@deadalnix:良い点。私が書いていたとき、私は自分のポイントが非常に単純化しすぎていることに気づきました。書くこととデバッグを容易にすることは、確かに物事を機能に分割することの利点です(個別の機能を単体テストする機能も同様です)。
Kramii復活モニカ

11

それは完全に関数でなければなりません。

if (isBufferValid(buffer)) {
    // ...
}

はるかに読みやすく、保守しやすくなっています(チェックロジックが変更された場合、1か所で変更するだけです)。

さらに、これらのようなものは簡単にインライン化されるため、関数呼び出しのオーバーヘッドを心配する必要さえありません。

より良い質問をさせてください。これを行うのはどのように良い習慣ではありませんか?

正しいことをします。:)


4
isBufferValidが単純な場合、return buffer != null;読みやすさが損なわれていると思います。
pdr

5
@pdr:その単純なケースでは、コントロールマニアの考え方を持っている場合にのみ読みやすさが損なわれ、本当に、本当に、本当にコードがバッファが有効かどうかをチェックする方法を知りたいのです。そのため、これらの単純なケースでは主観的です。
スポイケ

4
@pdr標準によって読みやすさがどのように損なわれるかわかりません。あなたは気から他の開発者を免除しているどのようにあなたが何かをやっている、とに焦点を当てて何をやっています。isBufferValid(私の本では)の方が間違いなく読みやすくbuffer != null、目的をより明確に伝えているからです。繰り返しになりますが、言うまでもなく、ここでも重複からあなたを救います。さらに何が必要ですか?
ヤムマルコビッチ

5

IMO、コードスニペットは、コードが読みやすくなるたびに独自の関数に移動する価値があります、関数が非常に短いか、一度しか使用されないかに関係なく。

もちろん、常識によって決定される制限があります。たとえばWriteToConsole(text)、bodyが単にConsole.WriteLine(text)であるメソッドは必要ありません。しかし、読みやすさの面で間違いを犯すのは良い習慣です。


2

通常、関数を使用してコードの重複を削除することをお勧めします。

しかしながらそれは行き過ぎです。これは判断の呼びかけです。

ヌルバッファチェックの例を挙げると、同じパターンがいくつかの場所で使用されている場合でも、次のコードは十分に明確であり、別の関数に抽出しないでください。

if (buf==null) throw new BufferException("Null read buffer!");

エラーメッセージを汎用のnullチェック関数のパラメーターとして含め、関数の定義に必要なコードも考慮する場合、これを次のように置き換えることはネットLOCの節約ではありません。

checkForNullBuffer(buf, "Null read buffer!");

さらに、デバッグ時に関数の動作を確認するために関数に飛び込む必要があるということは、関数呼び出しがユーザーにとって「透過的」ではないことを意味します。


おそらく、あなたの例は、実際の複製の必要性よりも、言語の契約サポートの欠如について多くを語っています。とにかく良い点。
deadalnix

1
あなたは私がそれを見る方法でポイントを逃しました。ないステップオーバーまたはロジックたびにデバッグを検討する必要があるとしたことは何である私は探しています。それがどのように行われたかを知りたい場合は、一度確認します。毎回それをしなければならないことは、単にばかげています。
ヤムマルコビッチ

エラーメッセージ(「Null read buffer」)が繰り返される場合、その重複は確実に排除されます。
ケビンクライン

それは単なる例にすぎませんが、おそらく、各関数呼び出しサイトに異なるメッセージが表示されます。たとえば、「Null read buffer」や「Null write buffer」などです。状況ごとに同じログ/​​エラーメッセージが必要な場合を
除き、

-1 Preconditions.checkNotNullは良い方法であり、悪い方法ではありません。ただし、文字列メッセージを含める必要はありません。google-collections.googlecode.com/svn/trunk/javadoc/com/google/...
ripper234

2

通常、コードを集中化することは常に良い考えです。可能な限りコードを再利用する必要があります。

ただし、その方法に注意することが重要ですです。たとえば、compute_prime_number()またはcheck_if_packet_is_bad()を実行するコードがある場合、それは適切です。機能のアルゴリズム自体が進化し、利益が得られる可能性があります。

ただし、散文として繰り返されるコードは、すぐに集中化する資格がありません。これは悪いです。コードを非表示にするためだけに、関数内の任意のコード行を非表示にすることができます。アプリケーションの複数の部分が使用を開始すると、それらはすべて、関数のすべての呼び出し先のニーズと互換性を維持する必要があります。

ここに尋ねる前に尋ねるべき質問がいくつかあります

  1. 独自の意味を作成している機能はありますか、それとも単なる行ですか?

  2. 同じ機能を使用する必要がある他のコンテキストはどれですか?これを使用する前に、APIを少し一般化する必要があるかもしれませんか?

  3. 例外をスローする場合、アプリケーション(の異なる部分)にはどのような期待がありますか?

  4. 機能が進化することを確認するシナリオは何ですか?

また、このようなものが既に存在するかどうかも確認する必要があります。すでに存在するものを検索するのではなく、マクロMIN、MAXを常に再定義する傾向にある人が非常に多いのを見てきました。

基本的に、質問は「この新しい機能は本当に再利用に値するのか、それとも単なるコピーアンドペーストなのか?」です。最初であれば、行ってもいいです。


1
まあ、複製されたコードがそれ自身の意味を持っていない場合、あなたのコードはリファクタリングが必要だと言っています。重複が発生する場所にもおそらく独自の意味がないためです。
deadalnix

1

コードの重複は避けてください。予想するたびに、コードの重複を避ける必要があります。予想していなかった場合は、同じコードが3回複製される前に3のルールを適用してリファクタリングし、2回複製されたときの癖に注釈を付けます。

コードの複製とは何ですか?

  • コードベースの複数の場所で繰り返されるルーチン。このツールは、単純な関数呼び出しよりも複雑でなければなりません(そうでなければ、因数分解しても何も得られません)。
  • 非常に一般的なタスクであり、些細なタスク(多くの場合、テスト)です。これにより、コードのカプセル化とセマンティックが改善されます。

以下の例を検討してください。

if(user.getPrileges().contains("admin")) {
    // Do something
}

になる

if(user.isAdmin()) {
    // Do something
}

カプセル化(条件を透過的に管理者に変更できるようになった)とコードのセマンティクスを改善しました。ユーザーが管理者であることを確認する方法でバグが発見された場合、コードベース全体を投げてどこでも修正する必要はありません(1つを忘れてアプリケーションのセキュリティ欠陥を取得するリスクがあるため)。


1
Btwの例は、デメテルの法則を示しています。
マル

1

DRYはコード操作を簡素化することです。あなたはこの原則についての細かい点に触れてきました:それはだではない、あなたのコード内のトークンの数を最小限に抑えるのではなく、作成について、意味的に同等のコードのための修正の単一のポイントを。チェックは常に同じセマンティックを持っているように聞こえるので、それらを変更する必要がある場合に備えて、それらを関数に入れる必要があります。


0

重複している場合は、一元化する方法を見つける必要があります。

関数は良い方法です(多分最高ではないかもしれませんが、それは言語に依存します)。

他に何かを確認する必要がある場合はどうしますか?

余分なチェックを追加したり、機能を変更したりする必要があるすべての場所を見つけますか?


...一方、何かを追加しない可能性が非常に高い場合はどうでしょう。その場合でも、あなたの提案は依然として最善の行動方針ですか?
EpsilonVector

1
@EpsilonVectorの経験則では、一度変更する必要がある場合は、リファクタリングすることもできます。だから、あなたの場合、私はそれを残し、私がそれを変更しなければならなかったら、それは機能になるでしょう。
サノスパパタナシオ

0

次の条件が満たされている場合、ほとんど常に良いです。

  • 読みやすさを改善します
  • 限られた範囲内で使用

より大きな範囲では、複製と依存関係のトレードオフを慎重に検討する必要があります。スコープ制限技術の例:プライベートセクションまたはモジュールを公開せずに非表示にします。

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