4

実際、コードの一部をリファクタリングしています。私がやりたいのは、オブジェクト「TaskArgument」でオブジェクト「Task」を初期化することです。「TaskArgument」が抽象的で、「Task」がメソッド「OnEnterTask(TaskArgument args)」を実装し、封印されているとしましょう (既存のシステムのいくつかの特別な動作のため、範囲外です)。

古いコード:

public sealed class Task : SomeSystemBaseTask {
  private int accessMe; 
  private int meToo;

  public void OnEnterTask(TaskArgument args) {
    if (args is SimpleTaskArgument) {
      accessMe = ((SimpleTaskArgument)args).uGotIt;
      meeToo = 0;
    } else if (args is ComplexTaskArgument) {
      accessMe = ((ComplexTaskArgument)args).uGotItValue * ((ComplexTaskArgument)args).multiplier;
      meToo = ((ComplexTaskArgument)args).multiplier - 1;
    }
  }
}

タイプチェックを避けるベストプラクティスは何ですか? 私の最初の馬鹿げた考えは:

public abstract class TaskArgument {
    internal public abstract Initialize(Task args);
}

public class SimpleTaskArgument : TaskArgument {
    public int uGotIt = 10;

    internal public Initialize(Task task){
        task.accessMe = uGotIt;
    }
}

public class ComplexTaskArgument : TaskArgument {
    public int uGotItValue = 10;
    public int multiplier = 10;

    internal public Initialize(Task task){
        task.accessMe = uGotItValue*multiplier;
        task.meToo = multiplier - 1;
    }
}

public sealed class Task : SomeSystemBaseTask {
    public int accessMe;
    public int meToo;

    public void OnEnterTask(TaskArgument args){
        args.Initialize(this);
    }
}

しかし、私の「accessMe」は公開されており、「初期化」メソッドは「タスク」でのみ機能します。そのため、タイプチェックを別の場所に移動しました(将来的に)。ベスト プラクティスや優れた設計アイデアはありますか。

...「内部公開」...うーん?

別のクレイジーなアイデアは内部クラスでしたが、私はそれらが好きではなく、そのような単純なケースをより複雑にするか、そうではありません:

public abstract class TaskArgument {
    internal public abstract Initialize(ITaskWrapper wrapper);
}

public class SimpleTaskArgument : TaskArgument {
    ...
}

public class ComplexTaskArgument : TaskArgument {
    ...
}

public interface ITaskWrapper {
    public int AccessIt { set; get; } 
    ...  
}

public sealed class Task : SomeSystemBaseTask {
    private int accessMe;
    ...

    class TaskWrapper : ITaskWrapper {
        ...
    }

    public void OnEnterTask(TaskArgument args){
        args.Initialize(new TaskWrapper(this));
    }
}

「TaskArgument」の指定されたタイプに基づいている場合、初期化に最適な場所はどこですか?

私の悪い英語の知識を親切に許してください

こんにちは

4

5 に答える 5

9

インターフェイスを使用します。

public void OnEnterTask(TaskArgument args) { 
   if (args is SimpleTaskArgument) { 
      accessMe = ((SimpleTaskArgument)args).uGotIt; 
   } else if (args is ComplexTaskArgument) { 
      accessMe = ((ComplexTaskArgument)args).uGotItValue * ((ComplexTaskArgument)args).multiplier; 
   } 
} 

になる

public void OnEnterTask(ITaskArgument args) { 
   accessMe = args.GetAccessMe();
} 

次に、クラスで ITaskArgument を実装し、各クラスのメソッドを実装します。一般的に、次のようなことをしている場合:

accessMe = ((ComplexTaskArgument)args).uGotItValue * ((ComplexTaskArgument)args).multiplier;

オブジェクトの複数のプロパティにアクセスして計算を実行する場合、通常はそのロジックをクラス自体にプッシュするのが理にかなっています。

于 2010-03-10T15:42:00.243 に答える
3

TaskArgument の各サブクラスに関連付けられたロジックをそのクラスに配置したいようです。サブクラス固有の計算を持つ抽象メソッドをTaskArgumentcalledに追加できます。Calculateこれにより、if ステートメントが完全に不要になります。

public class Task {
  private int accessMe; 

public void OnEnterTask(TaskArgument args) { accessMe = args.Calculate(); } }

次に、乗算または適切なものを各サブクラスに入れます。

于 2010-03-10T15:41:35.513 に答える
1

OK、コメントに表示される要件の変化に照らして、私の答えを少し変更しました! (おい、スコープクリープか何か?!)

public class Task
{
    public int Variable1 { get; internal set; }
    public int Variable2 { get; internal set; }

    public void OnEnterTask(ITaskInitializer initializer)
    {
        initializer.Initialize(this);
    }
}

public interface ITaskInitializer
{
    void Initialize(Task task);
}

public class SimpleTaskInitializer : ITaskInitializer
{
    private int uGotIt = 10;

    public void Initialize(Task task)
    {
        task.Variable1 = uGotIt;
    }
}

public class ComplexTaskInitializer : ITaskInitializer
{
    private int uGotIt = 10;
    private int multiplier = 10;

    public void Initialize(Task task)
    {
        task.Variable1 = uGotIt;
        task.Variable2 = uGotIt * multiplier;
        // etc - initialize task however required.
    }
}
于 2010-03-10T15:41:38.853 に答える
1

メソッドのみを公開するパブリック インターフェイスを作成しますIntialize。派生クラスで計算を行います。

public interface ITaskArgument
{
    void Initialize(Task task);
}

public abstract class TaskArgument : ITaskArgument
{
    protected int _value;
    public class TaskArgument(int value)
    {
        _value = value;
    }

    public abstract void Initialize(Task task);
}

public class SimpleTaskArgument : TaskArgument, ITaskArgument
{
    public SimpleTaskArgument(int value)
       : base (value)
    {
    }

    public override void Initialize(Task task)
    {
        task.AccessMe = _value;
    }
}

public class ComplexTaskArgument : TaskArgument, ITaskArgument
{
    private int _multiplier;

    public ComplexTaskArgument(int value, int multiplier)
       : base (value)
    {
         _multiplier = multiplier;
    }

    public override void Initialize(Task task)
    {
        task.AccessMe = _value * _multiplier;
    }
}

public class Task
{
    public Task()
    {
    }

    public int AccessMe { get; set; }

    public void OnEnterTask(ITaskArgument args)
    {                         
        args.Initialize(this);                         
    }  
}

SimpleTaskArgument simpleArgs = new SimpleTaskArgument(10);
ComplexTaskArgument complexArgs = new ComplexTaskArgument(10, 3);
Task task = new Task();
task.OnEnterTask(simpleArgs);
Console.WriteLine(task.AccessMe); // would display 10
task.OnEnterTask(complexArgs);
Console.WriteLine(task.AccessMe); // would display 30
于 2010-03-10T15:51:07.780 に答える
0

1 つのオプションとして Task のオーバーロードを作成できます。

public class SimpleTask : Task
{
   public override void EnterTask(TaskArgument arg)
   {
      var s = (SimpleTaskArgument)arg;
   }
}

したがって、各タスク タイプは同等の引数タイプを扱います。または、int を返す静的メソッドを使用してロジックを TaskFactory に移動し、そこに型チェック引数を含めることができます。

public static class TaskFactory
{
   public static int GetVal(TaskArgument arg)
   {
      if (args is SimpleTaskArgument) { 
        return ((SimpleTaskArgument)args).uGotIt; 
      } else if (args is ComplexTaskArgument) { 
        return ((ComplexTaskArgument)args).uGotItValue * ((ComplexTaskArgument)args).multiplier; 
      }
   }
}

インターフェイスの実装も機能します。私はそれを軽視しません...またはTaskargument内で抽象メソッドを定義し、それぞれがオーバーライドして値を返します。

HTH。

于 2010-03-10T15:42:26.080 に答える