Как использовать дженерики для заполнения моделей производных классов, чтобы избежать дублирования кода?

У меня есть 2 типа, и каждый тип имеет разную логику обработки. На основании этой обработки я готовлю результат и возвращаю его потребителю (приложение mvc, консольное приложение и т.д.)

  • Тип 1
  • Тип 2

Теперь проблема в том, что некоторый код является общим для обоих типов. Единственная часть, которая отличается, - это класс (Type1Manager,Type2Manager) для обоих типов, который фактически содержит логику для обработки type1 and type2 и подготовки результата (Type1Model,Type2Model).

public class Variant
{
    public int Id { get; set; }
    public string Name { get; set; }
    public List<Subvariants> Subvariants { get; set; }
}

public class Subvariants
{
    public int Id { get; set; }
    public string Name { get; set; }
}

public abstract class BaseManager
{
    //Other shared code
    public abstract ExecutionResult GetExecutionResult(Variant model);
}

public class ExecutionResult
{
    public string Name { get; set; }
    public string ErrorMessage { get; set; }
    public bool Success { get; set; }
    public List<Type1Model> Types1 { get; set; }
    public List<Type2Model> Types2 { get; set; }
}

public abstract class BaseModel<T>
{
    public string Name { get; set; }
    public T Value { get; set; }
    public T Coordinates { get; set; }
    public decimal OverAllPercentage { get; set; }
}

public class Type1Model : BaseModel<int>
{
    public decimal MiscPercentage { get; set; }
    public int PerformanceCounter { get; set; }
}

public class Type2Model : BaseModel<decimal> { }

public class Type1 : BaseManager
{
    public override ExecutionResult GetExecutionResult(Variant model)
    {
        var executionResult = new ExecutionResult();
        executionResult.Name = model.Name;
        var type1Result = new List<Type1Model>();
        try
        {
            for (int counter = 0; counter < model.Subvariants.Count - 1; counter++)
            {
                var left = model.Subvariants[counter];
                var right = model.Subvariants[counter + 1];
                using (var t = new Type1Manager(model))
                {
                    for (int i = 0; i < 2; i++)
                    {
                        if (i == 0)
                        {
                            t.Start(i);
                            if (counter == 0)
                            {
                                type1Result.Add(new Type1Model
                                {
                                    Name = left.Name,
                                    Value = t.Left
                                });
                            }
                        }
                        else
                        {
                            t.Start(i);
                            type1Result.Add(new Type1Model
                            {
                                Name = right.Name,
                                Value = t.Right,
                                Coordinates = t.Left + t.Right,
                                OverAllPercentage = t.OverAllPercentage,
                                PerformanceCounter = (t.NetPlus + t.AverageRatio),
                                MiscPercentage = t.MiscPercentage
                            });
                        }
                    }
                }
            }
            executionResult.Types1 = type1Result;
        }
        catch (Exception ex)
        {
            executionResult.Success = false;
            executionResult.ErrorMessage = ex.Message;
        }
        return executionResult;
    }
}

internal class Type1Manager : IDisposable
{
    private Variant model;
    public int Right { get; private set; }
    public int Left { get; private set; }
    public int NetPlus { get; private set; }
    public int AverageRatio { get; private set; }
    public decimal OverAllPercentage { get; private set; }
    public decimal MiscPercentage { get; private set; }
    public Type1Manager(Variant model)
    {
        this.model = model;
    }

    public void Start(int i)
    {

    }

    public void Dispose()
    {
        throw new NotImplementedException();
    }
}

public class Type2 : BaseManager
{
    public override ExecutionResult GetExecutionResult(Variant model)
    {
        var executionResult = new ExecutionResult();
        executionResult.Name = model.Name;
        var type2Result = new List<Type2Model>();
        try
        {
            for (int counter = 0; counter < model.Subvariants.Count - 1; counter++)
            {
                var left = model.Subvariants[counter];
                var right = model.Subvariants[counter + 1];
                using (var t = new Type2Manager(model))
                {
                    for (int i = 0; i < 2; i++)
                    {
                        if (i == 0)
                        {
                            t.Start(i);
                            if (counter == 0)
                            {
                                type2Result.Add(new Type2Model
                                {
                                    Name = left.Name,
                                    Value = t.Left
                                });
                            }
                        }
                        else
                        {
                            t.Start(i);
                            type2Result.Add(new Type2Model
                            {
                                Name = right.Name,
                                Value = t.Right,
                                Coordinates = t.Left + t.Right,
                                OverAllPercentage = t.OverAllPercentage,
                            });
                        }
                    }
                }
            }
            executionResult.Types2 = type2Result;
        }
        catch (Exception ex)
        {
            executionResult.Success = false;
            executionResult.ErrorMessage = ex.Message;
        }
        return executionResult;
    }
}

internal class Type2Manager : IDisposable
{
    private Variant model;
    public decimal Right { get; private set; }
    public decimal Left { get; private set; }
    public decimal OverAllPercentage { get; private set; }
    public Type2Manager(Variant model)
    {
        this.model = model;
    }

    public void Start(int i)
    {

    }
    public void Dispose()
    {
        throw new NotImplementedException();
    }
}

Проблема здесь, с которой я сталкиваюсь, заключается в том, что для Type1 needs Type1Model а для Type2 needs Type2Model. Даже если я попытаюсь BaseManager общий код в своем классе BaseManager, как бы я Type1Model and Type2Model модели Type1Model and Type2Model? Часть, с которой я борюсь, - это рефакторинг кода для обоих типов и перемещение некоторой общей логики в базовый класс.

Можно ли реорганизовать дубликаты кода для обоих типов и переместить их в базовый класс?

Обновить :

public abstract class BaseManager
 {
        public abstract ExecutionResult GetExecutionResult(Variant model);
        public abstract void ExecuteAndSave(Variant model, string connectionString);
 }




public class Type1 : BaseManager
    {
        public override void ExecuteAndSave(Variant model, string connectionString)
        {
            //other logic
            var t = new Type1Manager(new SaveData());
        }

        public override ExecutionResult GetExecutionResult(Variant model)
        {
            var executionResult = new ExecutionResult();
            executionResult.Name = model.Name;
            var type1Result = new List<Type1Model>();
            try
            {
                for (int counter = 0; counter < model.Subvariants.Count - 1; counter++)
                {
                    var left = model.Subvariants[counter];
                    var right = model.Subvariants[counter + 1];
                    using (var t = new Type1Manager(new Repository()))
                    {
                        for (int i = 0; i < 2; i++)
                        {
                            if (i == 0)
                            {
                                t.Start(null,null);
                                if (counter == 0)
                                {
                                    type1Result.Add(new Type1Model
                                    {
                                        Name = left.Name,
                                        Value = t.Left
                                    });
                                }
                            }
                            else
                            {
                                t.Start(null,null);
                                type1Result.Add(new Type1Model
                                {
                                    Name = right.Name,
                                    Value = t.Right,
                                    Coordinates = t.Left + t.Right,
                                    OverAllPercentage = t.OverAllPercentage,
                                    PerformanceCounter = (t.NetPlus + t.AverageRatio),
                                    MiscPercentage = t.MiscPercentage
                                });
                            }
                        }
                    }
                }
                executionResult.Types1 = type1Result;
            }
            catch (Exception ex)
            {
                executionResult.Success = false;
                executionResult.ErrorMessage = ex.Message;
            }
            return executionResult;
        }
    }

    internal class Type1Manager : IDisposable
    {
        public int Right { get; private set; }
        public int Left { get; private set; }
        public int NetPlus { get; private set; }
        public int AverageRatio { get; private set; }
        public decimal OverAllPercentage { get; private set; }
        public decimal MiscPercentage { get; private set; }
        private Repository _repository;
        public Type1Manager(Repository repository)
        {
            this._repository = repository;
        }

        public void Start(string connectionString,string sqlQuery)
        {

        }

        public void Dispose()
        {
            throw new NotImplementedException();
        }
    }

    public class Repository
    {
         //Dont save result here.Used only for Type1
    }

    public class SaveData : Repository
    {
        //Save result here.Used only for Type1
    }

    public class Type2 : BaseManager
    {
        public override void ExecuteAndSave(Variant model, string connectionString)
        {
            //using (var t = new Type2Manager(2)//not hardcoded.taken from model
        //Save data here returns from Type2Manager instance
        }

        public override ExecutionResult GetExecutionResult(Variant model)
        {
            var executionResult = new ExecutionResult();
            executionResult.Name = model.Name;
            var type2Result = new List<Type2Model>();
            try
            {
                for (int counter = 0; counter < model.Subvariants.Count - 1; counter++)
                {
                    var left = model.Subvariants[counter];
                    var right = model.Subvariants[counter + 1];
                    using (var t = new Type2Manager(2)) //not hardcoded.taken from model
                    {
                        for (int i = 0; i < 2; i++)
                        {
                            if (i == 0)
                            {
                                t.Start(null,null);
                                if (counter == 0)
                                {
                                    type2Result.Add(new Type2Model
                                    {
                                        Name = left.Name,
                                        Value = t.Left
                                    });
                                }
                            }
                            else
                            {
                                t.Start(null,null);
                                type2Result.Add(new Type2Model
                                {
                                    Name = right.Name,
                                    Value = t.Right,
                                    Coordinates = t.Left + t.Right,
                                    OverAllPercentage = t.OverAllPercentage,
                                });
                            }
                        }
                    }
                }
                executionResult.Types2 = type2Result;
            }
            catch (Exception ex)
            {
                executionResult.Success = false;
                executionResult.ErrorMessage = ex.Message;
            }
            return executionResult;
        }
    }

    internal class Type2Manager : IDisposable
    {
        public decimal Right { get; private set; }
        public decimal Left { get; private set; }
        public decimal OverAllPercentage { get; private set; }
        int precision;
        public Type2Manager(int precision)
        {
            this.precision = precision;
        }

        public void Start(string connectionString, string sqlQuery)
        {

        }
        public void Dispose()
        {
            throw new NotImplementedException();
        }
    }
  • Варианты: Регион
  • Подварианты: Субрегион
  • Тип1: Аналитический калькулятор
  • Тип 2: AggregateCalculator

Ответы

Ответ 1

Вы можете перенести большую часть кода в базовый класс, если сделаете базовый класс универсальным и установите правильные ограничения. Кроме того, вам нужно создать конкретную реализацию для каждого типа.
Следующий пример должен дать вам представление:

public abstract class SharedLogicClass<TValue, TType>
  where TType : BaseModel<TValue>, new
{
    public override ExecutionResult GetExecutionResult(Variant model)
    {
      var executionResult = new ExecutionResult();
      executionResult.Name = model.Name;
      var type1Result = new List<TTYpe>();
      try
      {
        for (int counter = 0; counter < model.Subvariants.Count - 1; counter++) 
        {
          var left = model.Subvariants.Count[counter];
          var right = model.Subvariants.Count[counter + 1];
          using (var t = CreateTypeManager(model))
          {
            for (int i = 0; i < 2; i++)
            {
              if (i == 0)
              {
                t.Start(i);
                if (counter == 0)
                {
                  type1Result.Add(new TType()
                  {
                    Name = left.Name,
                    Value = t.LeftValue
                  });
                }
              }
              else
              {
                t.Start(i);
                var type = new TType()
                {
                  Name = right.Name,
                  Value = t.RightValue,
                  Coordinates = t.Left + t.Right,
                  OverAllPercentage = t.OverAllPercentage,
                });
                AssignAdditionalValues(type, t);
                type1Result.Add(type);
              }
            }
          }
        }
        AssignExecutionResult(executionResult, type1Result);
      }
      catch (Exception ex)
      {
        executionResult.Success = false;
        executionResult.ErrorMessage = ex.Message;
      }
      return executionResult;
    }
    protected abstract BaseManager CreateTypeManager(Variant model);
    protected virtual void AssignAdditionalData(Type1Model type, TypeManager t) {}
    protected abstract void AssignExecutionResultList(ExecutionResult res, IList<TType> lst);
}

public class SharedLogicImplementationType1 : SharedLogicClass<int, Type1Model>
{
  protected override BaseManager CreateTypeManager(Variant model)
  {
    return new Type1Manager(model);
  }

  protected override void AssignAdditionalData(Type1Model type, TypeManager t)
  {
    type.PerformanceCounter = (t.NetPlus + t.AverageRatio);
    type.MiscPercentage = t.MiscPercentage;
  }

  protected override void AssignExecutionResultList(ExecutionResult res, IList<Type1Model> lst)
  {
    res.Types1 = lst;
  }
}


public class SharedLogicImplementationType2 : SharedLogicClass<decimal, Type2Model>
{
  protected override BaseManager CreateTypeManager(Variant model)
  {
    return new Type2Manager(model);
  }

  protected override void AssignExecutionResultList(ExecutionResult res, IList<Type2Model> lst)
  {
    res.Types2 = lst;
  }
}

Пожалуйста, рассматривайте код как образец, а не как законченную работу.

Ответ 2

Ваша объектная модель действительно слишком. Мне удалось удалить большую часть дублирования, но с именами классов трудно предложить дальнейший рефакторинг. Я подозреваю, что вы делаете это трудным путем.

Вот что у меня есть:

public class Variant
{
    public int Id { get; set; }
    public string Name { get; set; }
    public List<Subvariants> Subvariants { get; set; }
}

public class Subvariants
{
    public int Id { get; set; }
    public string Name { get; set; }
}

public class ExecutionResult<T, TModel>
    where TModel : BaseModel<T>
{
    public string Name { get; set; }
    public string ErrorMessage { get; set; }
    public bool Success { get; set; }
    public List<TModel> Types { get; set; }
}

public abstract class BaseModel<T>
{
    public string Name { get; set; }
    public T Value { get; set; }
    public T Coordinates { get; set; }
    public decimal OverAllPercentage { get; set; }
}

public class Type1Model : BaseModel<int>
{
    public decimal MiscPercentage { get; set; }
    public int PerformanceCounter { get; set; }
}

public class Type2Model : BaseModel<decimal> { }

public abstract class BaseManager<T, TManager, TModel, R>
    where TManager : TypeBaseManager<T>
    where TModel : BaseModel<T>
    where R : ExecutionResult<T, TModel>, new()
{
    protected abstract TManager CreateTypeManager(Variant model);
    protected abstract TModel CreateTypeModelLeft(TManager typeManager, Subvariants subvariant);
    protected abstract TModel CreateTypeModelRight(TManager typeManager, Subvariants subvariant);

    public R GetExecutionResult(Variant model)
    {
        var executionResult = new R();
        executionResult.Name = model.Name;
        var type1Result = new List<TModel>();
        try
        {
            for (int counter = 0; counter < model.Subvariants.Count - 1; counter++)
            {
                var left = model.Subvariants[counter];
                var right = model.Subvariants[counter + 1];
                using (var t = CreateTypeManager(model))
                {
                    for (int i = 0; i < 2; i++)
                    {
                        if (i == 0)
                        {
                            t.Start(i);
                            if (counter == 0)
                            {
                                type1Result.Add(CreateTypeModelLeft(t, left));
                            }
                        }
                        else
                        {
                            t.Start(i);
                            type1Result.Add(CreateTypeModelRight(t, right));
                        }
                    }
                }
            }
            executionResult.Types = type1Result;
        }
        catch (Exception ex)
        {
            executionResult.Success = false;
            executionResult.ErrorMessage = ex.Message;
        }
        return executionResult;
    }
}

public class Type1 : BaseManager<int, Type1Manager, Type1Model, ExecutionResult<int, Type1Model>>
{
    protected override Type1Manager CreateTypeManager(Variant model)
    {
        return new Type1Manager(model);
    }

    protected override Type1Model CreateTypeModelLeft(Type1Manager typeManager, Subvariants subvariant)
    {
        return new Type1Model
        {
            Name = subvariant.Name,
            Value = typeManager.Left
        };
    }

    protected override Type1Model CreateTypeModelRight(Type1Manager typeManager, Subvariants subvariant)
    {
        return new Type1Model
        {
            Name = subvariant.Name,
            Value = typeManager.Right,
            Coordinates = typeManager.Left + typeManager.Right,
            OverAllPercentage = typeManager.OverAllPercentage,
            PerformanceCounter = (typeManager.NetPlus + typeManager.AverageRatio),
            MiscPercentage = typeManager.MiscPercentage
        };
    }
}

public class Type2 : BaseManager<decimal, Type2Manager, Type2Model, ExecutionResult<decimal, Type2Model>>
{
    protected override Type2Manager CreateTypeManager(Variant model)
    {
        return new Type2Manager(model);
    }

    protected override Type2Model CreateTypeModelLeft(Type2Manager typeManager, Subvariants subvariant)
    {
        return new Type2Model { Name = subvariant.Name, Value = typeManager.Left };
    }

    protected override Type2Model CreateTypeModelRight(Type2Manager typeManager, Subvariants subvariant)
    {
        return new Type2Model
        {
            Name = subvariant.Name,
            Value = typeManager.Right,
            Coordinates = typeManager.Left + typeManager.Right,
            OverAllPercentage = typeManager.OverAllPercentage,
        };
    }
}

public class TypeBaseManager<T> : IDisposable
{
    protected Variant model;
    public T Right { get; private set; }
    public T Left { get; private set; }
    public decimal OverAllPercentage { get; private set; }
    public TypeBaseManager(Variant model)
    {
        this.model = model;
    }

    public void Start(int i)
    {
    }

    public void Dispose()
    {
        throw new NotImplementedException();
    }
}

public class Type1Manager : TypeBaseManager<int>
{
    public int NetPlus { get; private set; }
    public int AverageRatio { get; private set; }
    public decimal MiscPercentage { get; private set; }

    public Type1Manager(Variant model) : base(model) { }
}

public class Type2Manager : TypeBaseManager<decimal>
{
    public Type2Manager(Variant model) : base(model) { }
}

Мне бы очень хотелось, чтобы вы переименовали классы с некоторой последовательностью.

Кроме того, я хотел бы, чтобы catch (Exception ex) удален - не похоже, что ваш код может потерпеть там неудачу, и если это возможно, похоже, что вы можете кодировать ошибку, не прибегая к обработке исключений. В конце концов, обработка исключений должна быть для обработки исключительных ситуаций, таких как нехватка места на жестком диске.