Как использовать дженерики для заполнения моделей производных классов, чтобы избежать дублирования кода?
У меня есть 2 типа, и каждый тип имеет разную логику обработки. На основании этой обработки я готовлю результат и возвращаю его потребителю (приложение mvc, консольное приложение и т.д.)
Теперь проблема в том, что некоторый код является общим для обоих типов. Единственная часть, которая отличается, - это класс (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)
удален - не похоже, что ваш код может потерпеть там неудачу, и если это возможно, похоже, что вы можете кодировать ошибку, не прибегая к обработке исключений. В конце концов, обработка исключений должна быть для обработки исключительных ситуаций, таких как нехватка места на жестком диске.