Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save eterekhin/bb3d4ade0ed5ee914147bff5979c4954 to your computer and use it in GitHub Desktop.
Save eterekhin/bb3d4ade0ed5ee914147bff5979c4954 to your computer and use it in GitHub Desktop.

Tease Apart Inheritance

Такое происходит в случаях, когда иерархия становится многоуровневой и в разных ветвях возможно дублирование функциональности:

 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, потому что он более очевидный и сохраняет поведение в классе

Convert Procedure Design to Objects

Если вам достался процедурый стиль и вы хотите переписать его на ООП, то нужно: Сложить все продуры и данные в один класс и начать по-тихоньку выносить в разные, классы, легко это написать, но реализовать вряд-ли, я бы скорее попробовал свести это к функциональному стилю, убрав состояние и данные, оставив только методы, это и будет быстрее

Separate Domain from Presentation

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 Hierarchy

Прием выделения иерархии из бесформенного класса, который отвечает за слишком многое, при проведении этого рефакторинга задействованы: Extract Classes, Extract Subclasses, Replace Code Type with Subclasses, Replace Code Type with State or Strategy,Form Template Method и многие другие, более мелкие

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment