40

異なるフィルターを使用して同じ形状のデータを返す複数のメソッドを定義する場合のより良い方法は何ですか?明示的なメソッド名またはオーバーロードされたメソッド?

例えば。いくつかの製品があり、データベースからプルしている場合

明示的な方法:

public List<Product> GetProduct(int productId) {    // return a List    }
public List<Product> GetProductByCategory(Category category) {    // return a List    }
public List<Product> GetProductByName(string Name ) {    // return a List    }

過負荷の方法:

public List<Product> GetProducts() {    // return a List of all products    }
public List<Product> GetProducts(Category category) { // return a List by Category }
public List<Product> GetProducts(string searchString ) { // return a List by search string }

同様の署名で問題が発生する可能性があることは承知していますが、基本型(string、int、char、DateTimeなど)の代わりにオブジェクトを渡す場合、これはそれほど問題にはなりません。だから...あなたが持っているメソッドの数を減らして明確にするためにメソッドをオーバーロードするのは良い考えですか、それともデータを異なる方法でフィルタリングする各メソッドは異なるメソッド名を持つべきですか?

4

16 に答える 16

47

はい、オーバーロードは簡単に使いすぎてしまう可能性があります。

過負荷が正当化されるかどうかを判断するための鍵は、聴衆を考慮することです。コンパイラーではなく、数週間、数か月、数年で登場し、コードが何であるかを理解しなければならない保守プログラマーです。達成しようとしています。

GetProducts() のような単純なメソッド名は明確で理解しやすいものですが、多くのことが語られていません。

多くの場合、GetProducts() に渡されるパラメーターに適切な名前が付けられていれば、保守担当者はオーバーロードが何をするかを理解できますが、それは使用時に適切な命名規則に依存しているため、強制することはできません。強制できるのは、呼び出しているメソッドの名前です。

私が従うガイドラインは、交換可能な場合、つまり同じことを行う場合にのみ、メソッドをオーバーロードすることです。そうすれば、クラスのコンシューマーが呼び出すバージョンは同等であるため、気にしません。

説明のために、DeleteFile() メソッドのオーバーロードを喜んで使用します。

void DeleteFile(string filePath);
void DeleteFile(FileInfo file);
void DeleteFile(DirectoryInfo directory, string fileName);

ただし、あなたの例では、別の名前を使用します。

public IList<Product> GetProductById(int productId) {...}
public IList<Product> GetProductByCategory(Category category) {...}
public IList<Product> GetProductByName(string Name ) {...}

フルネームを使用すると、保守担当者 (おそらく私) にとってコードがより明確になります。署名の衝突の問題を回避します。

// No collisions, even though both methods take int parameters
public IList<Employee> GetEmployeesBySupervisor(int supervisorId);
public IList<Employee> GetEmployeesByDepartment(int departmentId);

目的ごとにオーバーロードを導入する機会もあります。

// Examples for GetEmployees

public IList<Employee> GetEmployeesBySupervisor(int supervisorId);
public IList<Employee> GetEmployeesBySupervisor(Supervisor supervisor);
public IList<Employee> GetEmployeesBySupervisor(Person supervisor);

public IList<Employee> GetEmployeesByDepartment(int departmentId);
public IList<Employee> GetEmployeesByDepartment(Department department);

// Examples for GetProduct

public IList<Product> GetProductById(int productId) {...}
public IList<Product> GetProductById(params int[] productId) {...}

public IList<Product> GetProductByCategory(Category category) {...}
public IList<Product> GetProductByCategory(IEnumerable<Category> category) {...}
public IList<Product> GetProductByCategory(params Category[] category) {...}

ソース管理への最初のチェックイン後にコードに戻らなくても、コードを書いている間にそのコード行を数十回読むことになります。続くコード。

最後に、使い捨てのコードを書いている場合を除き、他の人が他の言語からあなたのコードを呼び出すことを許可する必要があります。ほとんどのビジネス システムは、使用期限を過ぎても本番環境にとどまるようです。2016 年にクラスを使用するコードは、最終的に VB.NET、C# 6.0、F#、またはまだ発明されていないまったく新しいもので記述される可能性があります。言語がオーバーロードをサポートしていない可能性があります。

于 2008-10-30T09:05:57.240 に答える
18

私の知る限り、メソッドの数は少なくなり、名前の数は少なくなります。私は通常、オーバーロードされた名前付けのメソッドシステムを好みますが、コードにコメントを付けて文書化する限り、それほど大きな違いはないと思います(どちらの場合もそうする必要があります)。

于 2008-10-29T20:09:29.400 に答える
16

使いすぎてもらえますか?ええ、そうです、それは本当です。

ただし、ここで示した例は、メソッドのオーバーロードを使用する場合の完璧な例です。それらはすべて同じ機能を実行します。異なるタイプを渡すという理由だけで、なぜそれらに異なる名前を付けるのですか。

主なルールは、最も明確で理解しやすいことを行うことです。巧妙または巧妙にするためだけにオーバーロードを使用するのではなく、意味のあるときに実行してください。他の開発者もこのコードに取り組んでいる可能性があります。できるだけ簡単にコードを理解して理解し、バグを実装せずに変更を実装できるようにする必要があります。

于 2008-10-29T20:13:27.533 に答える
13

メソッドをオーバーロードして、後でインテリセンスで同じメソッドを何百万も使用しないようにするのが好きです。また、名前を12回変えるのではなく、オーバーロードする方が理にかなっているようです。

于 2008-10-29T20:10:51.830 に答える
5

おそらく、プロジェクト全体の標準が必要です。個人的には、オーバーロードされたメソッドの方がはるかに読みやすいと思います。IDEをサポートしている場合は、それを利用してください。

于 2008-10-29T20:10:12.097 に答える
5

考慮すべきことの1つは、オーバーロードされたメソッドをWCFWebサービスの操作コントラクトとして公開できないことです。したがって、これを行う必要があると思われる場合は、別のメソッド名を使用するための引数になります。

異なるメソッド名の別の議論は、それらがインテリセンスを使用してより簡単に発見できるかもしれないということです。

しかし、どちらの選択にも長所と短所があります。すべての設計はトレードオフです。

于 2008-10-29T20:23:15.127 に答える
5

コードを使用する誰かの学習曲線を緩和するためのオーバーロードのポイント...そして、メソッドが何をするかについてユーザーに知らせる命名スキームを使用できるようにします。

すべてが従業員のコレクションを返す 10 個の異なるメソッドがある場合、10 個の異なる名前を生成すると (特に名前が異なる文字で始まる場合)、ユーザーのインテリセンス ドロップダウンに複数のエントリとして表示され、ドロップダウンし、すべて従業員コレクションを返す 10 個のメソッドのセットと、クラス内の他のメソッドとの違いを隠します...

コンストラクターやインデクサーなど、.Net フレームワークによって既に強制されているものについて考えてみてください...それらはすべて同じ名前にする必要があり、それらをオーバーロードすることによってのみ複数を作成できます...

それらをオーバーロードすると、それらはすべて 1 つとして表示され、それぞれの異なる署名とコメントが横に表示されます。

2 つのメソッドが異なる機能または無関係な機能を実行する場合、2 つのメソッドをオーバーロードしないでください。

のように型ごとに同じシグネチャを持つ 2 つのメソッドをオーバーロードする場合に発生する可能性のある混乱については、

public List<Employee> GetEmployees(int supervisorId);
public List<Employee> GetEmployees(int departmentId); // Not Allowed !!

署名を区別するために、問題のあるコアタイプのラッパーとして個別のタイプを作成できます..

  public struct EmployeeId 
  { 
      private int empId;
      public int EmployeeId { get { return empId; } set { empId = value; } }
      public EmployeeId(int employeId) { empId = employeeId; }
  }

  public struct DepartmentId 
  {
   // analogous content
  }

 // Now it's fine, as the parameters are defined as distinct types...
 public List<Employee> GetEmployees(EmployeeId supervisorId);
 public List<Employee> GetEmployees(DepartmentId  departmentId);
于 2008-10-29T21:24:59.123 に答える
3

フレームワークを一目見れば、多数の過負荷が受け入れられている状況であることがわかります。無数のオーバーロードに直面して、使いやすさのためのオーバーロードの設計は、Microsoft Framework設計ガイドラインのセクション5.1.1(Kwalina and Abrams、2006)によって直接対処されています。そのセクションの簡単な説明は次のとおりです。

  • 短いオーバーロードで使用されるデフォルトを示すために、説明的なパラメーター名を使用するようにしてください

  • オーバーロードで任意に変化するパラメーター名は避けてください。

  • オーバーロードされたメンバーのパラメーターの順序に一貫性がないことを避けてください。

  • 最長の過負荷のみを仮想化してください(拡張性が必要な場合)。短いオーバーロードは、単に長いオーバーロードを呼び出す必要があります。

  • メンバーをオーバーロードするためにrefまたはパラメーターを使用しないでください。out

  • nullオプションの引数に渡すことを許可してください。

  • デフォルトの引数でメンバーを定義するのではなく、メンバーのオーバーロードを使用してください

于 2008-10-29T22:00:50.000 に答える
2

もう1つのオプションは、Queryオブジェクトを使用して「WHERE句」を作成することです。したがって、次のような方法は1つだけです。

public List<Product> GetProducts(Query query)

Queryオブジェクトには、オブジェクト指向の方法で表現された条件が含まれています。GetProductsは、Queryオブジェクトを「解析」することによってクエリを取得します。

http://martinfowler.com/eaaCatalog/queryObject.html

于 2008-10-29T20:09:47.933 に答える
2

メソッドの引数にわずかな違いしかない場合に、オーバーロードが多用されるのを見てきました。例えば:

public List<Product> GetProduct(int productId) { // return a List  }
public List<Product> GetProduct(int productId, int ownerId ) { // return a List  }
public List<Product> GetProduct(int productId, int vendorId, boolean printInvoice) { // return a List  }

私の小さな例では、2番目のint引数を所有者IDにするか顧客IDにするかがすぐに不明確になります。

于 2008-10-29T20:23:04.643 に答える
1

はい、あなたはそれを使いすぎることができます、しかしここにそれの使用を制御下に保つのを助けることができるもう一つの概念があります...

.Net 3.5以降を使用していて、複数のフィルターを適用する必要がある場合は、IQueryableとチェーンを使用する方がおそらく良いでしょう。

GetQuery<Type>().ApplyCategoryFilter(category).ApplyProductNameFilter(productName);

そうすれば、必要な場所でフィルタリングロジックを何度も再利用できます。

public static IQueryable<T> ApplyXYZFilter(this IQueryable<T> query, string filter)
{
     return query.Where(XYZ => XYZ == filter);
} 
于 2008-10-29T20:18:57.883 に答える
1

オーバーロードは必要なだけ使用できます。ベストプラクティスの観点からも、データに対して同じ「操作」を(全体的に)実行しようとしている場合は、オーバーロードを使用することをお勧めします。例:getProduct()

また、Java APIが表示されている場合、オーバーロードはどこにでもあります。それ以上の支持はありません。

于 2008-10-29T21:49:57.480 に答える
1

オーバーロードは望ましいポリモーフィック動作です。人間のプログラマーがメソッド名を覚えるのに役立ちます。明示的が型パラメーターと重複している場合、それは悪いことです。型パラメーターがメソッドの実行内容を暗示していない場合、明示的な方法が有効になります。

あなたの例では、 getProductByName は、他の文字列で製品を取得したい場合があるため、明示的に意味がある唯一のケースです。この問題は、プリミティブ型のあいまいさが原因でした。getProduct(Name n) は、場合によってはより優れたオーバーロード ソリューションになる可能性があります。

于 2008-10-29T22:33:40.653 に答える
0

はい、使いすぎてもかまいません。あなたの例では、最初と3番目はおそらく単一のアイテムを返し、2番目は複数のアイテムを返すように見えます。それが正しければ、1番目と3番目のGetProductと2番目のGetProductsまたはGetProductListを呼び出します

これが当てはまらず、3つすべてがいくつかを返す場合(productID 5を渡す場合のように、productidに5が含まれるアイテムを返すか、名前に文字列パラメーターが含まれるアイテムを返します)、3つすべてのGetProductsを呼び出します。またはGetProductListを実行し、それらすべてをオーバーライドします。

いずれにせよ、名前は関数の機能を反映している必要があるため、製品のリストを返すときにGetProduct(単数)と呼んでも、適切な関数名にはなりません。IMNSHO

于 2008-10-30T13:33:03.130 に答える
0

どうですか

public IList<Product> GetProducts() { /* Return all. */}

public IList<Product> GetProductBy(int productId) {...}
public IList<Product> GetProductBy(Category category) {...}
public IList<Product> GetProductBy(string Name ) {...}

等々?

于 2012-05-17T11:47:00.967 に答える
0

私は「明示的な」方法の完全なファンです: 各関数に異なる名前を付けます。Add(...)過去に多くの機能を持っていたコードを 、 などにリファクタリングAddRecord(const Record&)AddCell(const Cell&)ました。

これにより、混乱、不注意なキャスト (少なくとも C++ では)、およびコンパイラの警告を回避するのに役立ち、明確さが向上すると思います。

場合によっては、他の戦略が必要になることもあります。まだ遭遇したことがありません。

于 2009-01-14T07:42:27.220 に答える