web-dev-qa-db-ja.com

ANDを関数名で使用する必要がありますか?

私は窮地に陥っています。私は、その機能内で多くのデータベース変更を行うリポジトリーで作業しています。

私が扱っている関数は、responseidを返します(ただし、データベースアクションからではなく、変換から)。ただし、副作用として、それらのresponseIdを含むオブジェクトがデータベースに追加されます。

だから私はそれに名前を付ける必要があります:

  • getResponseIds:戻り値を強調表示します。これは非常に機能的な考え方ですが、関数postToDBがある場合、getStatusOfPostを使用しても意味がありません。
  • addResponseIdToDB:これは副作用を浮き彫りにしますが、私の関数の多くはデータベース上でのみ動作する(そして何も返さない傾向がある)と本当に思います。
  • getAndAddResponseIdsToDB:非常に有益ですが、非常に長いです。

上記の提案の長所と短所は何ですか?それとも、自分でより良い提案をすることができますか?

41
tintinthong

andを含む関数名は 間違った抽象化レベル です。

私はaddResponseIdToDB()に傾いています。それ以外の場合、「副作用」は完全な驚きだからです。しかしながら:

_responseIds = addResponseIdToDB();
_

誰も驚かないままにしません。

コマンドクエリの責任分離の原則 は、これがresponseIdオブジェクトを取得する唯一の方法ではないことを主張しています。このオブジェクトを取得できるDBを変更しないクエリもあります。

Bertrand Meyer とは対照的に、これはaddがvoidを返す必要があることを意味するとは思わない。同等の純粋なクエリが存在し、簡単に検索できるようにして、状態変更クエリの使用によってDBが不必要に悪用されないようにするだけです。

getResponseIds()が存在し、データベースと通信しないようにする必要があるため、両方を実行するメソッドの最適な名前は実際にはaddToDB(getResponseId())です。しかし、それはあなたがそれについてすべての機能的な構成を取得したい場合にだけです。

56
candied_orange

他の人が述べたように、関数名でandを使用すると、関数が少なくとも2つのことを実行していることを自動的に意味します。これは通常、関数が過度に実行している、または間違った場所で何かを実行していることを示します。

関数名でand句を使用することは、私の経験では、特定のフローで順番に実行する必要があるステップがある場合や、計算全体が意味を持つようになった場合に意味があります。

数値計算では、これは非常に理にかなっています。

normalizeAndInvert(Matrix m)

つまり、誰が知っているのでしょう?いくつか計算する必要がある場合は、たとえば、コンピュータグラフィックスのライティング軌道と特定の段階で、特定の行列を正規化および反転する必要があります。

単純な例として、正規化と反転の抽象化を導入するm = normalize(m)の後にinvert(m)を続けると、読みやすさの観点からは良いかもしれません。

意味論的に言えば、divideAndConquer()などのようなものは、明示的に書き出すことはおそらくお勧めできませんが、まあ、基本的にはAndが必要です。

14
Bruno Oliveira

一般的には問題ないと思いますが、すべての場合に許容できるわけではありません。

たとえば、関数名のandに対して、 単一責任の原則 別名S SOLID--andは複数の責任を意味する可能性があります。andが実際に関数が2つのことを実行していることを意味する場合は、おそらく何が起こっているのかを慎重に検討する必要があります。

関数名にandを使用するライブラリの例は、Javaの concurrency にあります。ここでandは、実際に起こっていることの非常に重要な部分であり、状態が変更され状態が返される投稿での説明と一致します。したがって、受け入れられると思われる人々(およびいくつかのユースケース)がいるのは明らかです。

9
Shaz

確かに、多くのコメントが証明できるように、それは答えるのが難しい質問です。人々は、良い名前とは何かについて、矛盾した意見やアドバイスを持っているようです。

この2か月前のスレッドに2セントを追加したいと思います。これは、すでに提案されているものにさらに色を追加できると思います。

命名はプロセスです

これらすべては、優れた6ステップガイド プロセスとしての命名 を思い出させます。

貧弱な関数名は驚くべきものであり、信頼できないことに気づきます。最初のショットで適切な名前を付けるのは難しい。経験すれば簡単になります。

適切な名前を作成するための6つの反復ステップ

  1. 意外な名前をappleSauce()のように明らかなナンセンスに置き換えます。ばかげているように聞こえますが、それは一時的なものであり、その名前が信頼できないことは明らかです。
  2. 関数の機能から理解したことに基づいて、正直な名前を取得します。 DB部分への挿入をまだ理解していないとしましょう。getResponseIdsAndProbablyUseDbがオプションになります。
  3. 完全に正直になりますので、関数名は、関数が実行するすべてのことを示します(getAndAddResponseIdsToDBまたはgetResponsesButAlsoAddCacheOfTheResponsesToTheDb @Fattieは素晴らしい例)
  4. 基本的に「AND」に沿って関数を分割する部分である「正しいことを行う」に進みます。つまり、実際にはgetResponseIdsaddResponseIdsToDbがあります。
  5. のポイントを解決する「意図を明らかにする」名前にたどり着く常に挿入したレスポンスIDを取得したい私がそうした後のDB」。実装の詳細について考えるのをやめ、2つの最小の関数を使用して別のものを構築する抽象化を構築します。これは、@ candied_orangeで言及されているより高い抽象化レベルです。たとえば、createResponseIdsはそれを行うことができ、getResponseIdsaddResponseIdsToDbの合成になります。
  6. ドメイン抽象化を取得します。これは難しいです、それはあなたのビジネスに依存します。ビジネス言語を正しく理解するには時間がかかります。最終的には、Responseの概念に到達する可能性があり、Response.createIds()は理にかなっています。あるいは、ResponseIdは貴重なものであり、多くを作成するためのファクトリーがあるでしょう。 DBへの挿入は、リポジトリなどの実装の詳細になります。はい、設計はより抽象的です。それはより豊かで、より多くのことを表現させるでしょう。あなたのチームの外の誰も、あなたの状況で正しいドメイン抽象化がどうあるべきかをあなたに教えることができません。 状況によります

あなたのケースでは、すでに1と2を理解しているので、少なくとも3に進む必要があります(「AND」を使用します)。しかし、さらに先に進むことは、名前を付けるだけの問題ではなく、実際には責任を分割することになります。

したがって、ここでのさまざまな提案は有効です

基本的に:

  • はい、意外な関数名はひどいもので、誰もそれを扱いたくない
  • はい、誤解を招くような名前よりも優れているため、関数名には「AND」を使用できます。
  • はい、抽象化のレベルは関連する概念であり、それは重要です

すぐにステージ6に行く必要はありません。よくわかるまで、ステージ2、3、または4にとどまっても問題ありません。


それが矛盾するアドバイスのように見えるものに別の視点を与えるのに役立つことを願っています。私は誰もが同じ目標(驚くべき名前ではない)を目指していますが、自分の経験に基づいて、さまざまな段階で立ち止まると思います。

質問があればお答えします:)

3
nicoespeon

関数は2つのことを行います。

  1. トランスフォームを介してIDのセットを取得し、呼び出し元に返します。
  2. そのIDのセットをデータベースに書き込みます。

ここでは単一の責任の原則を思い出してください。関数は2つの責任ではなく1つの責任を持つことを目指す必要があります。あなたには2つの責任があるので、2つの機能があります。

getResponseIds-変換を介してIDのセットを取得し、呼び出し元に返します
addResponseIdToDB-IDのセットを取得して、データベースに書き込みます。

そして、複数の責任を持つ単一の関数を何と呼ぶか​​という問題全体がなくなり、関数名にandを入れる誘惑もなくなります。

追加のボーナスとして、同じ関数が2つの無関係なアクションの原因ではなくなったため、getResponseIdsをリポジトリコードから移動できます(DB関連のアクティビティを実行していないため、属していない)。別のビジネスレベルのクラス/モジュールなどに.

2
David Arno

複合関数名を使用することは許容されるため、使用する命名方式はコンテキストによって異なります。それを分解するように言う純粋主義者がいますが、私はそうでないと主張します。

そのようなことを回避しようとする2つの理由があります。

  • 純度-2つの処理を実行する関数は、それぞれ1つの処理を実行する2つの関数に変換する必要があります。
  • 字句サイズ-bigUglyCompositeFunctionNameは読みにくくなります。

純粋性の議論は、通常、焦点を当てているものです。漠然とした一般的なヒューリスティックとして、関数を分割することは良いことです。何が起こっているのかを区分して、理解しやすくします。変数を共有して「賢い」トリックを行う可能性は低く、2つの動作の結合につながります。

だから自問するのは「とにかくやるべきか」ということです。物事を分割することはあいまいなヒューリスティックなので、本当にそれに反対するために必要なのは、適切な議論だけです。

主な理由の1つは、2つの操作を1つとして考えることが有益であることです。これのすべての究極の例は、アトミック操作:_compare_and_set_にあります。 _compare_and_set_はアトミック(ロックなしでマルチスレッドを実行する方法)の基本的な基礎であり、多くの人がそれをthe基礎。その名前には「and」があります。これには非常に正当な理由があります。この操作の要点は、比較と設定を1つの分割できない操作として行うことです。 compare()set()に分割すると、2つのcompares()が次のように発生する可能性があるため、最初に関数が存在した理由全体を無効にしますset() sの前に戻ります。

これはパフォーマンスでも見られます。私のお気に入りの例は fastInvSqrt です。これは、1/sqrt(x)を計算するQuakeの有名なアルゴリズムです。 「逆」操作と「平方根」操作を組み合わせると、パフォーマンスが大幅に向上します。それらは、単一のステップで両方の演算を実行するニュートンの近似を実行します(当時は、浮動小数点ではなく整数演算で実行されていました)。 inverse(sqrt(x))を実行すると、かなり遅くなります。

結果がより明確になる場合があります。デッドロックのようなものについてひどく注意しなければならないスレッド化を含むいくつかのAPIを書きました。ユーザーに内部実装の詳細を知らせたくなかったので(特に変更する可能性があるため)、いくつかの「and」関数を使用してAPIを作成しました。 1つの関数呼び出し。これは、彼らが私が内部でマルチスレッド同期をどのように処理していたかを知る必要がなかったことを意味します。実際、ほとんどのユーザーは、マルチスレッド環境にいることにさえ気づいていませんでした。

したがって、一般的なルールは物事を分割することですが、それらを結合する理由は常にあります。たとえば、ユーザーは「gets」を間に挟まずにデータベースに複数回追加することで、アーキテクチャを壊すことができますか? DBに記録された各応答に一意の識別子が必要な場合、ユーザーが無理にそれらを実行しなければならない場合、問題が発生する可能性があります。同様に、ユーザーが結果をDBに記録せずに「取得」できるようにしたいですか?答えが「はい」の場合は、それらを分割して、ユーザーが「取得」機能にアクセスできるようにします。ただし、ユーザーが結果をDBに記録せずに「取得」できる場合に、アプリケーションのセキュリティが壊れている場合は、機能をまとめておく必要があります。

語彙の問題については、関数名はユーザーがそれについて知る必要があることを説明する必要があります。 「取得」の部分から始めましょう。すべての例で割り当てが表示されるため、関数名から「get」を削除するのは非常に簡単です:int id = addResponseIdToDB() "。関数が使用されるすべての場所で、最終的に関数が値を返しました。

同様に、「追加」はオプションにすることができます。 「副作用」という用語はすべてを包括する用語として使用しますが、色合いはさまざまです。 DBエントリが単なるログである場合、それを強調表示する理由はありません。 playMusicAndSendPersonalInformationToOurCorporateServersのような関数はありません。それは単にplayMusicであり、運が良ければ、ドキュメントにはインターネット経由のソケット接続についての記述があるかもしれません。一方、ユーザーがDBに追加する目的でこの関数を呼び出すことが予想される場合は、「追加」が不可欠です。取り出さないでください。

今、私はあなたが求めていることのあらゆる可能な組み合わせをする多くの理由を書きました。選択の余地があるため、質問には意図的に回答しませんでした。従うべき規則ではありません。 APIを作成しています。

そうは言っても、私の本能はaddResponseIdToDBがおそらく最良の答えであるということです。ほとんどの場合、「取得」部分は明らかな十分な副作用であり、どこにでも入力することによって引き起こされる余分なノイズを獲得しません。ただし、私が重要だと考える場所はいくつかあります。

  • 「取得」にコストがかかる場合-インターネット経由で何かを取得し、それをローカルキャッシュDBに追加することを伴う「取得」を行う必要がある場合は、「取得」が重要です。それはユーザーがしようとしていることです。
  • ユーザーが値に注意を払う必要がある必要があることを明確にする必要がある場合。ユーザーが変数を使用したい場合、APIが変数を返すことに気づき、それを使用します。ただし、ユーザーがそれを必要としていることを知らないユーザーに注意する必要があります。たとえば、メモリを解放するために後でこの操作をIDで「閉じる」必要がある場合は、何かを実行しているという事実に注意を向けることができます。このような場合、「get」以外の動詞を確認することをお勧めします。 「get」は、しばしばべき等関数を意味します(再度呼び出しても何も起こりません)。リソースを返す場合、「作成」や「取得」などの他の動詞はいいです。この特定の障害メカニズムは、例外処理を行う際のCの主要な問題です。 CにはC++のキャッチ/スローメカニズムがないため、戻りコードに依存しています。開発者は有名で、これらの戻りコードのチェックに失敗し、それが原因でバッファオーバーフローなどの本当に悪い状況に陥ります。
  • 対称性-APIを字句対称性を持つように設計することがあります。単語がペアになるように、または他のパターンになるように単語を設定し、パターンに従っているかどうかを視覚的に簡単に識別できるようにコマンドを作成します。これはまれですが、前代未聞ではありません。 XMLタグを閉じるときにタグ名(<foo>や</ foo>など)が繰り返されるのには理由があります。
1
Cort Ammon

究極とはintentこのメソッドの?なぜこれらの変換されたIDをDBに追加するのですか、それはキャッシュの目的ですか?その仮定の下で働きます。

メソッドの目的は、実際には変換された応答IDを取得すること(変換を実行するか、キャッシュから取得すること)であると思われるため、メソッド名があります。

getTransformedResponseIds以下の詳細getResponseIds()

この名前のメソッドはキャッシュすることもしないこともあり、ドキュメント化することもできますが、メソッド名で特定の実装に結び付けることはできません。 DBの使用をやめ、代わりに一時的にメモリなどの他の場所にキャッシュする場合はどうなりますか?

副作用はそれだけでなければなりません。副作用はおそらく文書化されるべきですが、関数のコアインテント(または成功)や名前に実際に影響を与えるべきではありません。キャッシュからの値の取得が失敗した場合(またはキャッシュをスキップするように構成した場合)、それが重大な問題ではない場合、メソッドは透過的に再計算し、キャッシュ(またはそうでない)にして、新しい値を返します。

JavaメソッドgetAndIncrementおよびincrementAndGetなど)を使用して意図を伝えるのに「AND」を使用すると役立つことがあるので、これは間違いなくテーブルから外れていません。

0
xtratic