Skip to content

Instantly share code, notes, and snippets.

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

Replace constrcutor of factory method

Конструктор нужно заменить фабричным методом, если не хватает возможностей самого конструктора, например нужно создавать экземпляры разных типов имеющих общего наследника, такое бывает, в проценссе рефакторинга Replace Type Code Subclasses, когда мне нужно создать подклассы, а конструктора не хватает, поскольку он ничего не возвращает, а только создает объект

  public enum EmployeeType
    {
        Fix,
        Hourly
    }

    public class Employee
    {
        public double SalaryByHour { get; protected set; }
        public double FixedSalary { get; protected set; }
        private EmployeeType Type { get; }

        public Employee(EmployeeType type)
        {
            Type = type;
        }
    }

После проведения Replace Type Codee Subsclassess:

public class Employee
    {

        public static Employee Create(EmployeeType type)
        {
            switch (type)
            {
                case EmployeeType.Fix:
                    return new FixedEmployee();
                case EmployeeType.Hourly:
                    return new HourlyEmployee();
                default:
                    throw new ArgumentOutOfRangeException(nameof(type), type, null);
            }
        }
    }

    public class FixedEmployee : Employee
    {
        public double FixedSalary { get; protected set; }
    }

    public class HourlyEmployee : Employee
    {
        public double SalaryByHour { get; protected set; }
    }

Вызовы конструтора Employee я заменил на вызов фабричного метода у всех клиентов, тем самым не пришлось сразу рефакторить много клиенткого кода, далее нужно пройтись по всем вызовам фабричного метода и решить сотрудника какого типа нужно создавать.

Также интересная особенность статических фабричных методов в том, что они имеют доступ к приватным полям типа: public class FileLogger : ILogger { private readonly string _path;

    public static FileLogger FileLoggerCreate(string path)
    {
        return new FileLogger(path);
    }

    private FileLogger(string path)
    {
        _path = path;
    }

    public void Log(string text)
    {
        Console.WriteLine(text);
    }
}

Это полезно когда нужен контроль за созданием экземпляров

Encapsulate Downcast

Принцип в уменьшении количества приведений вниз в коде, и в простосте пользования клиентов, суть в том, что если какой-то метод декларирует более широкий тип чем возвращает на самом деле и клиенты знают об этом, то они будут делать приведение сами:

  public static FileLogger FileLoggerCreate(string path)
        {
            if (IsWinPath(path))
                return new WinFileLogger(path);

            if (IsLinuxPath(path))
                return new LinuxFileLogger(path);
            
            throw new NotImplementedException();
        }

        public static FileLogger GetWinLogger(string path)
        {
            return FileLoggerCreate(path);
        }

Например вот в таком случае все клиенты метода GetWinLogger будут вынуждены сами делать приведение

FileLogger.GetWinLogger("path") as WinFileLogger

Вместо этого можно сделать приведение в самом методе GetWinLogger:

        public static FileLogger GetWinLogger(string path)
        {
            return FileLoggerCreate(path) as WinFileLogger ;
        }

Тем самым облегчив жизнь клиентам и инкапсуривовав этот участок кода, что дает возможность в будущем его аккуратно отрефакторить

Replace Error Code With Exception

Такой рефакторинг часто приходится использовать при разработке домена, если при проектировании были допущены неточности, например, в какой-то метод невозможно попасть находясь в определенном состоянии(не проведен рефакторинг Replace Type Code State/Factory Method), тогда можно поставить guard на вход в метод, чтобы ясно дать понять назначение этого метода, это поможет при дальнейшем рефакторинге, также генерация исключительных ситуаций в конструкторе полезна, поскольку определяет контракт класса, но выкидывать Exception'ы рекомендуется только в домене, слой приложения должен обрабатывать возможные исключительные ситуации

Replace Exception with Test

Это рефакторинг для слоя приложения, возможен в случае, когда я поставил try catch, поскольку вызов определенного кода иногда падает, и нет времени разбираться когда, но, когда время появляется, нужно найти место, где код падает, и вернуть сообщение об ошибке, сообщение об ошибке лучше заключить в тип Result

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