Я думаю, что вы ищете более ортогональный дизайн, в котором вы можете добавлять новые элементы с другими свойствами, расчетами и правилами стабильности без необходимости параллельного расширения ItemBase
и CalculationResultBase
. Это вполне возможно здесь и, вероятно, более ремонтопригодно.
ItemBase
кажется входом для различных расчетов, и вы хотите, чтобы тип элемента контролировал расчет. Это нормально, и я бы подумал об использовании наследования только для ваших элементов, больше ничего:
// does not really matter if it is an abstract class or an interface
public interface ItemBase
{
double CalcResultingForce(double mass);
bool IsSystemStable(double maxForce);
}
public class Item1 : ItemBase
{
public int A { get; set; }
public int B { get; set; }
public override double CalcResultingForce(double mass)
{
return (A + B) / mass;
}
public override bool IsSystemStable(double force)
{
return force >= A;
}
}
public class Item2 : ItemBase
{
public int C { get; set; }
public double MaxForce { get; set; }
public override double CalcResultingForce(double mass)
{
return mass * C;
}
public override virtual bool IsSystemStable(double force)
{
return !(force == 0 || MaxForce < value);
}
}
Это позволит реализовать CalculationResult и CalculationService более общим способом, который не требует каких-либо изменений или создания подклассов при добавлении нового типа элемента:
public class CalculationResult
{
public bool SystemIsStable { get; set; }
private double resultingForce;
public virtual double ResultingForce
{
get => resultingForce;
set
{
resultingForce = value;
SystemIsStable = SomeItem.IsSystemStable(resultingForce);
}
}
public ItemBase SomeItem { get; set; }
public SystemInfo SystemInfo { get; set; }
public CalculationResult(ItemBase someItem, SystemInfo systemInfo)
{
SomeItem = someItem;
SystemInfo = systemInfo;
}
}
public static CalculationResult Calculate(SystemInfo systemInfo, ItemBase item)
{
CalculationResult output = new(item, systemInfo);
output.ResultingForce = item.CalcResultingForce(systemInfo.Mass);
return output;
}
Теперь вся основная логика находится в одном месте, а расширения можно выполнять, добавляя дополнительные подклассы ItemBase. Это также устраняет проблему дублирования SomeItem
, как упоминал JonasH. В качестве бонуса это также соответствует OCP (в отношении возможных новых требований для расчета сил и критериев устойчивости).
Обратите также внимание, что я без колебаний изменю дизайн вашего исходного класса CalculationResultBase
напрямую, если вы сами контролируете код. OCP не является самоцелью, это имеет смысл при создании компонентов или библиотек для повторного использования другими командами или организациями, которые хотят иметь возможность расширять библиотеку сейчас, не прося поставщика утвердить свои новые требования в следующем. версия, которая будет выпущена через шесть месяцев в будущем.
Прикрепляю к посту несколько видео по теме: