Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to leverage generics to populate derive class models to avoid code duplication?

Tags:

c#

generics

I am having 2 types like and each type have different processing logic. Based on that processing I am preparing a result and returning it to the consumer (mvc application,console app etc..)

  • Type 1
  • Type 2

Now the problem is some code is common across both the types. The only part that differs is the class (Type1Manager,Type2Manager) for both the types that actually contains the logic for the processing type1 and type2 and preparing the result(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();
    }
}

Problem here that I'm facing is that Type1 needs Type1Model and Type2 needs Type2Model. Even if I try to move out common code in my BaseManager class, how would I populate Type1Model and Type2Model model? The part I'm struggling with is to refactor the code for both the types and move some common logic in base class.

Is is possible to refactor out duplicate code for both the types and move it in base class?

Update :

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();
        }
    }
  • Variants : Region
  • Subvariants : SubRegion
  • Type1 : AnalyticalCalculator
  • Type2 : AggregateCalculator
like image 556
ILoveStackoverflow Avatar asked Feb 13 '19 07:02

ILoveStackoverflow


2 Answers

You could pull most of the code to a base class if you make the base class generic and set the right constraints. In addition, you need to create a specific implementation for each type.
The following sample should give you an idea:

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;
  }
}

Please treat the code as a sample and not as finished work.

like image 79
Markus Avatar answered Nov 07 '22 15:11

Markus


Your object model really is way over the top. I've managed to remove most of the duplication, but with the class names it's hard to suggest any further refactoring. I suspect you're doing this the hard way.

Here's what I've got:

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) { }
}

I would dearly love to see you rename the classes with some consistency.

Also I'd like to see the catch (Exception ex) removed - it doesn't look like your code can fail there and if it can it looks like you could code around the failure without resorting to exception handling. After all, exception handling should be for handling exceptional situations like running out of hard drive space.

like image 37
Enigmativity Avatar answered Nov 07 '22 14:11

Enigmativity