дружеские занятия в c # - PullRequest
       42

дружеские занятия в c #

4 голосов
/ 10 марта 2010

На самом деле я рефакторинг какой-то части кода. что я хочу сделать, это инициализировать объект «Задача» с объектом «TaskArgument». скажем, «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" общедоступен, а метод "Initialize" работает только с "Task". поэтому я перенес проверку типов в другое место (в будущем). Есть ли лучшая практика или хорошая идея дизайна.

... "внутренняя публика" ... ммхмммм?

Еще одна сумасшедшая идея - внутренний класс, но мне они не нравятся, и это делает такой простой случай более сложным или нет:

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»?

прошу прощения за мои плохие знания английского

привет мо

Ответы [ 5 ]

9 голосов
/ 10 марта 2010

Использовать интерфейс.

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;

когда вы обращаетесь к нескольким свойствам объекта для выполнения вычисления, обычно имеет смысл вставить эту логику в сам класс.

3 голосов
/ 10 марта 2010

Звучит так, будто вы хотите поместить логику, связанную с каждым подклассом TaskArgument, в этот класс. Вы можете добавить абстрактный метод к TaskArgument с именем Calculate, который имеет специфичные для подкласса вычисления. Это полностью исключило бы необходимость в ваших утверждениях if: public class Task { private int accessMe; </p> <p>public void OnEnterTask(TaskArgument args) { accessMe = args.Calculate(); } }

Затем вы бы поместили умножение или что-то подходящее в каждый подкласс.

1 голос
/ 10 марта 2010

Я бы создал публичный интерфейс, который предоставляет только метод 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
1 голос
/ 10 марта 2010

ОК, немного изменил мой ответ в свете меняющихся требований, появляющихся в комментариях! (Sheesh, сфера ползать или что?!)

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.
    }
}
0 голосов
/ 10 марта 2010

Вы можете создать перегрузки Task как один из вариантов:

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

Таким образом, каждый тип задачи имеет дело с эквивалентным типом аргумента. Или вы можете переместить логику в TaskFactory с помощью статического метода, который возвращает int и имеет аргумент проверки типа.

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, который каждый переопределяет, чтобы вернуть значение.

НТН.

...