2017-04-03 27 views
3

サブクラスB、C、Dを持つ基底クラスAがあるとします。クラスごとに、それぞれにオーバーロードを含むMyClassクラスがあります。これらのメソッドの1つがMyClass以外のどこからでも呼び出されると、いつでもカウンタをインクリメントすることができます。これは、いくつかの簡単なヘルパーメソッドで実現できます。C#重複したロジックの回避

public class MyClass 
{ 
    int counter; 

    public doSomethingExternal(B b) {counter++; doSomething(b);} 
    public doSomethingExternal(C c) {counter++; doSomething(c);} 
    public doSomethingExternal(D d) {counter++; doSomething(d);} 

    private doSomething(B b) {...} 
    private doSomething(C c) {...} 
    private doSomething(D d) {...} 
} 

これは本当に私の開発者を悩ましています。それらのヘルパーメソッドを書くことができるので、サブクラスごとにロジックが重複しないようにすることはできません。私は一般化のこの種は、リフレクションを達成することができるが、私は反射が一般的に遅く、理解するために複雑になることを聞いたことがあると思います

// Magically, we know which type we are and call the correct doSomething 
public doSomethingExternal(A a) {counter++; doSomething(a);} 

:私は想像する解決策のようなものです。私はこの論理重複の問題を解決するために見過ごされている従来のパターンがあるのではないかと不思議です。

+1

[テンプレートメソッドパターン](https://www.tutorialspoint.com/design_pattern/template_pattern.htm)が役立つかもしれません。 – ckruczek

+0

'A'、' B'と 'C'がインタフェースのように共通のものを共有していれば役に立ちます。 – ja72

+0

私は 'C#' 7.0に正しいパターンを呼び出すのに役立つパターンマッチングがあると思います。 – ja72

答えて

2

これを解決できる方法の1つは、クラスA、B、Cにインターフェイスを実装させることです。 doSomething関数が何をしているかによって、このようなことができます。

public interface IFoo 
{ 
    void DoSomething(); 
} 

MyClassには1つの公開機能があります。ベース・クラスであることがdoSomethingExternalの引数を残しdynamic

public class MyClass 
{ 
    int counter; 

    public doSomethingExternal(A value) // A is base class 
    { 
     counter++; 
     dynamic dynamicValue = value; 
     doSomething(dynamicValue); // Correct overload will be used based on actual type 
    } 

    private doSomething(B b) {...} 
    private doSomething(C c) {...} 
    private doSomething(D d) {...} 
} 

を用い

public void doSomethingExternal(IFoo foo) 
{ 
    counter++; 
    foo.DoSomething(); 
} 
+1

これは、 'MyClass'の主な責任(A、B、Cクラスを持つもの)をクラスA、B、Cに移します。 'MyClass'が何もしなければ、' MyClass'はまったく必要ありません。 – Fabio

+0

MyClassには状態があるので正確ではありません。カウンタです。また、doSomethingメソッドが何をしているかによって、foo.DoSomething()を使用してMyClassをメソッドに送ることができます。 –

2

別のアプローチは、この方法に他の種類を通過できなくなります。

-1

これは厄介で、C#7.0とパターンマッチングでクリーンアップできますが、まだありません。

public interface A { } 
public struct B :A { } 
public struct C :A { } 
public struct D: A { } 

public class MyClass 
{ 
    int counter; 
    private void DoSomething(B b) { ... } 
    private void DoSomething(C c) { ... } 
    private void DoSomething(D d) { ... } 

    public void DoSomethingExternal(A arg) 
    { 
     if (arg is B) 
     { 
      DoSomething((B)arg); 
     } 
     else if (arg is C) 
     { 
      DoSomething((C)arg); 
     } 
     else if (arg is D) 
     { 
      DoSomething((D)arg); 
     } 
     else 
     { 
      // If `A` is not of `B`, `C` or `D` types return without incrementing counter 
      return; 
     } 
     counter++; 
    } 
} 

編集1

共通のインタフェースやクラスAが存在しない場合、その後、あなたはobjectを使用する必要があります。

public void DoSomethingExternal(object arg) 
{ 
} 
+0

サンプルにパターンマッチングを使用していません。パターンマッチングは "pattern"をチェックし、結果のインスタンスを提供するので、 'if'ステートメントは以下のようになります:' if(arg is B argB){DoSomething(argB); } ' – Fabio

+0

@Fabio私はまだ2017を持っていないので、私はサンプルを提供することができませんでした。他の誰かがC#7.0を使って答えを出すことを望んでいます – ja72

-1

これは本当に私の中でDEVを悩まします。それらのヘルパーメソッドを書くことができるので、サブクラスごとにロジックが重複しないようにすることはできません。

なぜですか?私はあなたのコードを読むのはかなり簡単です。私は実際には "重複コード"とは考えず、単純なカウンタをインクリメントしてプライベートメソッドに委任します。投稿されたすべての代替案を検討しますか?あなたの人生を楽にしてくれますか?

もちろん、C#7のパターンマッチングを利用することもできますが、それは本当に価値がありますか?

public void doSomethingExternal(A a) 
{ 
    counter++; 

    switch (a) 
    { 
     case B b: 
      doSomething(b); 
      break; 
     case C c: 
      doSomething(c); 
      break; 
     case D d: 
      doSomething(d); 
      break; 
     case null: 
      throw new ArgumentNullException(nameof(a)); 
     default: 
      throw new InvalidOperationException(); 
    } 
} 

これは単に重複しないようにするためのものです。counter++

あなたの呼び出しは、おそらくコードが実際にどのように見えるか、そして "複製された"ロジックの程度の非常に単純化されたシナリオを見るだけです。

関連する問題