web-dev-qa-db-ja.com

これは繰り返しコードと見なされますか?

C#/ ASP.net:

/// <summary>
/// Is a group in the basket already?
/// </summary>
public static bool isItemInBasket(List<BasketItem> BasketItems, int GroupID)
{
    return BasketItems.Where(c => c.GroupID == GroupID).SingleOrDefault() != null;
}
public static bool isItemInBasket(List<BasketItem> BasketItems, int GroupID, DateTime ArtworkDate)
{
    return BasketItems.Where(c => c.GroupID == GroupID && c.ArtworkDate == ArtworkDate).SingleOrDefault() != null;
}

私が話した一部の人々は、これを繰り返しコードと見なしますが、繰り返しコードは、別の手法を採用する場合にとにかくメソッドに渡さなければならないすべてのものであるため、本質的に繰り返しとは見なしません。これを単純化したり、さらに単純化したバージョンに分割したりすることは不可能だと思います。したがって、コードを繰り返す必要はありません。

これは言うのが合理的なことですか?

5
Tom

技術的には、それはis繰り返されるコードです。ただし、同僚が間違っているのは、繰り返されるコードは常に悪いと考えることです。この場合、繰り返しによって追加される明確さは、複数の障害点を作成するなど、繰り返しの有害な側面をはるかに上回ります。ヒューリスティックな設計を極端に行うと、最良の設計が得られる可能性は低くなります。ヒューリスティックの背後にある理由を理解する必要があります。

さて、これらの関数のページがあり、それぞれわずかに異なる基準の組み合わせがある場合、DRYルールが支配的な要因になります。トリックは、バランスポイントを認識し、コードのリファクタリングを行うことです。そのように成長します。

ちなみに、静的関数に最初の引数と同じオブジェクトを繰り返し渡す場合、それはインスタンスメソッド、つまりbasketItems.isInBasket(groupID)としてより適切に適合する可能性があることを指摘したいと思います。

13
Karl Bielefeldt

いいえ、繰り返されるコードではありません。ポリモーフィズムを使用して、呼び出し元が2つの異なるオーバーロードを使用できるようにします。

各メソッドのコードが長い場合は、共通部分を独自のメソッドにリファクタリングしますが、linqステートメントはすでに十分に簡潔です(他の人が提案しているようにAny()を使用すると仮定します)。

10
Robert Harvey

私はこれをコンパイラーに入れませんでしたが、理想的には、オーバーロードを行うときに、あるメソッドから別のメソッドにコードを複製しないでください。

完全なメソッドをコーディングすると、オーバーロードごとにメソッドが呼び出され、パラメーターがない場合はnullが渡され、次のようになります。

/// <summary>
/// Is a group in the basket already?
/// </summary>
public static bool isItemInBasket(List<BasketItem> BasketItems, int GroupID)
{
    //return BasketItems.Where(c => c.GroupID == GroupID).SingleOrDefault() != null;
    return isItemInBasket(BasketItems, GroupID, null)
}
public static bool isItemInBasket(List<BasketItem> BasketItems, int GroupID, DateTime? ArtworkDate)
{
    return BasketItems.Where(c => c.GroupID == GroupID && (ArtworkDate.HasValue ? c.ArtworkDate == ArtworkDate.Value : true)).SingleOrDefault() != null;
}

過負荷が増えると、1つの完全なメソッドを変更するだけで済みます(より複雑なメソッドですが)。そして、他の各メソッドは単なるラッパーであり、重複を排除しています。

3
CaffGeek

これらのメソッドにロジックを追加するつもりがない場合は、はい、コードを複製しています。 BasketItems.Any(c => /* insert condition here */)を使用することをお勧めします。

ただし、コードを使用する場所がたくさんある場合は、次のようにすることができます。

public bool IsInBasket(List<BasketItem> basketItems, Expression<Func<BasketItem, bool>> filter)
{
    return basketItems.Any(filter.Compile());
}
2
devnull

.net4.0を使用していると仮定します。オプションのパラメーターを持つメソッドが1つだけになる

public static bool IsItemInBasket(List<BasketItem> BasketItems, int GroupID, DateTime? ArtworkDate = null)
{
    var artWorkDate = ArtworkDate ?? DateTime.Now;
    return BasketItems.Any(c => c.GroupID == GroupID && c.ArtworkDate == artWorkDate);
}
1
rpgmaker

それは非常に境界線です。私はあなたが書かなければならないユニットテストが好きではありません。

私はむしろバスケットアイテムのメソッドを見てみたいです

public bool isInBasket(int GroupID, DateTime? ArtworkDate)
{
    return this.GroupID == GroupID &&
           this.ArtworkDate == ArtworkDate ?? this.ArtworkDate;
}

その後

/// <summary>
/// Is a group in the basket already?
/// </summary>
public static bool isItemInBasket(List<BasketItem> BasketItems, int GroupID)
{
    return isItemInBasket(BasketItems, GroupID, null);
}

public static bool isItemInBasket(List<BasketItem> BasketItems, int GroupID, DateTime? ArtworkDate)
{
    return BasketItems.Any(c => c.isInBasket(GroupID, ArtworkDate)) != null;
}

さらに進んで、これらのテストに偽の応答を追加できるように、BasketItemにインターフェイスを配置する場合があります。

そうは言っても、私はあなたのコードのコードレビューであなたを失敗させることはありません。それがもっと複​​雑になると問題になるだろうと警告します。

編集:実際、それを読み返してみると、それが要点を示していることに気づきました。今は意味がないからです。これは私の名前を再考することを余儀なくさせます。

public bool isInGroup(int GroupID, DateTime? ArtworkDate)
{
    return this.GroupID == GroupID &&
           this.ArtworkDate == ArtworkDate ?? this.ArtworkDate;
}

その後

/// <summary>
/// Is a group in the basket already? (do I even need this comment any more?)
/// </summary>
public static bool isGroupInBasket(List<BasketItem> BasketItems, int GroupID)
{
    return isItemInBasket(BasketItems, GroupID, null);
}

public static bool isGroupInBasket(List<BasketItem> BasketItems, int GroupID, DateTime? ArtworkDate)
{
    return BasketItems.Any(c => c.isInGroup(GroupID, ArtworkDate)) != null;
}

さらなる編集:実際、この道をさらに進むほど、問題が多くなると思います。私も含めて、重複していると感じる人は誰でも、メソッドのオーバーロードに反応しています。これは、本能的に1つのメソッドが別のメソッドを呼び出す必要があります。

しかし、実際には、2番目の方法は非常に非常に異なる何かを行っています。また、ArtworkDateフィールドの目的を誰も理解できないという事実は、2番目のメソッドの名前を変更する必要があることを示唆しています。 isGroupInBasketAnd ...何ですか?

コードの違いを知っているので、コードは重複していないように見えると思いますが、他のすべての人は同じ名前を見て、それは単なる標準的なオーバーロードであり、少し構文上の糖衣だと思います。

0
pdr