Такое происходит в случаях, когда иерархия становится многоуровневой и в разных ветвях возможно дублирование функциональности:
public abstract class Salary
{
public decimal Value { get; }
public abstract decimal GetResult();
public abstract void PrintSalary();
}
public abstract class FixSalary : Salary
{
public override decimal GetResult() => Value;
}
public abstract class HourlySalary : Salary
{
private int hours;
public override decimal GetResult() => Value * hours;
}
public class MenFixSalary : FixSalary
{
public override void PrintSalary()
{
Console.WriteLine(GetResult() * 2);
}
}
public class WomanFixSalary : FixSalary
{
public override void PrintSalary()
{
Console.WriteLine(GetResult());
}
}
public class ManHourlySalary : HourlySalary
{
public override void PrintSalary()
{
Console.WriteLine(GetResult() * 2);
}
}
public class WomanHourlySalary : HourlySalary
{
public override decimal GetResult() => base.GetResult() * (decimal) 1.5;
public override void PrintSalary()
{
Console.WriteLine(GetResult());
}
}
Например для мужчин и для женщин PrintSalary всегда одинаковый для обеих ветвях иерархии уровнях иерархии, во-первых нужно посмотреть что именно дублируется, если только вывод, то его нужно вынести в отдельный класс, а после в классе Salary объявить абстрактное свойство PrintSalary имеющее тип этого нового класса, и в классах WomanHourlySalary,ManHourlySalary,WomanFixSalary,MenFixSalary определить это свойство, далее просто реализовать метод Print(Print Salary раньше, я его переименовал), теперь это шаблонный метод:
public abstract class PrintSalary
{
public abstract void Print(decimal value);
}
public class MenPrintSalary : PrintSalary
{
public override void Print(decimal value)
{
Console.WriteLine(value * 2);
}
}
public class WomenPrintSalary : PrintSalary
{
public override void Print(decimal value)
{
Console.WriteLine(value);
}
}
public abstract class Salary
{
public decimal Value { get; }
public abstract decimal GetResult();
protected abstract PrintSalary PrintSalary { get; }
public void Print() => PrintSalary.Print(GetResult());
}
public abstract class FixSalary : Salary
{
public override decimal GetResult() => Value;
}
public abstract class HourlySalary : Salary
{
private int hours;
public override decimal GetResult() => Value * hours;
}
public class MenFixSalary : FixSalary
{
protected override PrintSalary PrintSalary { get; } = new MenPrintSalary();
}
public class WomanFixSalary : FixSalary
{
protected override PrintSalary PrintSalary { get; } = new WomenPrintSalary();
}
public class ManHourlySalary : HourlySalary
{
protected override PrintSalary PrintSalary { get; } = new MenPrintSalary();
}
public class WomanHourlySalary : HourlySalary
{
protected override PrintSalary PrintSalary { get; } = new WomenPrintSalary();
}
Но у нас все-равно осталось дублирование свойств, в этом проблема наследования, релизовывать иерархии схожие по нескольким признакам сложно, поэтому также можно использовать паттерн стратегия, кстати он не являеется таким ООП паттерном, ведь в нем нет состояния, стратегия это функция, которая что-то принимает и что-то отдает, этот класс не должен хранить данные, они должен хранить только функции, ФП - функции и данные отдельно ООП - функции + данные вместе Паттерн стратегия - функции и данные отдельно
public abstract class Salary
{
private readonly ISalaryPrinter _salaryPrinter;
public decimal Value { get; }
public abstract decimal GetResult();
public Salary(ISalaryPrinter salaryPrinter)
{
_salaryPrinter = salaryPrinter;
}
public void Print() => _salaryPrinter.Print(GetResult());
}
public abstract class FixSalary : Salary
{
public override decimal GetResult() => Value;
protected FixSalary(ISalaryPrinter salaryPrinter) : base(salaryPrinter)
{
}
}
public abstract class HourlySalary : Salary
{
private int hours;
public override decimal GetResult() => Value * hours;
protected HourlySalary(ISalaryPrinter salaryPrinter) : base(salaryPrinter)
{
}
}
public class MenFixSalary : FixSalary
{
public MenFixSalary() : base(new MenPrintSalary())
{
}
}
public class WomanFixSalary : FixSalary
{
public WomanFixSalary() : base(new WomenPrintSalary())
{
}
}
public class ManHourlySalary : HourlySalary
{
public ManHourlySalary() : base(new MenPrintSalary())
{
}
}
public class WomanHourlySalary : HourlySalary
{
public WomanHourlySalary() : base(new WomenPrintSalary())
{
}
}
Этот подход менее ООП, но зато более гибкий, теперь можно внедрять разные стратегии вывода зарплат, но я бы остановился на варианте со override property, потому что он более очевидный и сохраняет поведение в классе
Если вам достался процедурый стиль и вы хотите переписать его на ООП, то нужно: Сложить все продуры и данные в один класс и начать по-тихоньку выносить в разные, классы, легко это написать, но реализовать вряд-ли, я бы скорее попробовал свести это к функциональному стилю, убрав состояние и данные, оставив только методы, это и будет быстрее
MVC архитектура предполагает разделение приложения на три слоя
- MODEL - предметная область(домен + слой приложения)
- VIEW - вывод результатов обработки в слое Model(например, html page)
- CONTROLLER - связь между View и Model, , чтобы для связывания View и Model
MVC, тем самым VIEW зависит от MODEL, т.е все типы в том числе и DTO(POCO) лежат в MODEL, задача VIEW только получить данные и как-то на них среагировать, передав реакцию в контроллер, который вызовет сервисы из MODEL Поэтому не нужно в VIEW и в CONTROLLER'e размещать BL и типы сущностей, типы сущностей должны быть в MODEL и мапиться в DTO(POCO) для использования во VIEW.Этот рефакторинг,объясняет как сформировать MODEL, типы и поведения определены во VIEW и CONTROLLER'e
Прием выделения иерархии из бесформенного класса, который отвечает за слишком многое, при проведении этого рефакторинга задействованы: Extract Classes, Extract Subclasses, Replace Code Type with Subclasses, Replace Code Type with State or Strategy,Form Template Method и многие другие, более мелкие