Применяется когда класс стал слишком большим и выпонять слишком много действий, тогда самое просто решение вынести действия которые он не должен выполнять в отдельный класс и в исходном классе использовать его
public class Person
{
private string Name { get; }
private string Surname { get; }
private IEnumerable<Salary> Salaries { get; }
public decimal GetSalary()
{
return Salaries.Sum(x => x.Value);
}
}
Если класс Person не должен уметь считать свою суммарную зарплату:
public class Person
{
public string Name { get; }
public string Surname { get; }
private SalaryCalculator Calculator { get; }
public decimal GetSalary()
{
return Calculator.GetSalary();
}
}
public class SalaryCalculator
{
private IEnumerable<Salary> Salaries { get; }
public decimal GetSalary()
{
return Salaries.Sum(x => x.Value);
}
}
Применяется при рефакторингах Replace Code Type with strategy/state, Replace Code Type on Subclasses Например, я проектировал класс SqlCommand, в котором хранилась информация о параметрах, времени выполнения, sql запросе, и из нее нужно было получать sql запроса, но потом появилась SqlCommand'a для другой базы данных, из которой также нужно получать sql строку запроса,которая формируется по-разному, поэтому я выделяю базовый класс SqlCommand, и делаю два наследника MsSqlCommand, PostgresSqlCommand, класс SqlCommand становится абстрактным, логика формирования sql запроса перемещается в наследников Было:
public class SqlCommand
{
private readonly string _value;
private readonly Dictionary<string, string> _parameters;
public SqlCommand(string sqlCommand)
{
_value = GetSqlRequest(sqlCommand);
_parameters = GetParametersDictionary(sqlCommand);
}
private string GetSqlRequest(string sqlcommand) => "not implemented";
private Dictionary<string, string> GetParametersDictionary(string sqlcommand)
=> new Dictionary<string, string>
{
{"not", "implemented"}
};
}
Стало:
public abstract class SqlCommand
{
protected readonly string _value;
private readonly Dictionary<string, string> _parameters;
public SqlCommand(string sqlCommand)
{
_value = GetSqlRequest(sqlCommand);
_parameters = GetParametersDictionary(sqlCommand);
}
private string GetSqlRequest(string sqlcommand) => "not implemented";
private Dictionary<string, string> GetParametersDictionary(string sqlcommand)
=> new Dictionary<string, string>
{
{"not", "implemented"}
};
}
class PostgresCommand : SqlCommand
{
public PostgresCommand(string sqlCommand) : base(sqlCommand)
{
}
public string GetRequestSql() => _value.ToLower();
}
class MsSqlCommand : SqlCommand
{
public MsSqlCommand(string sqlCommand) : base(sqlCommand)
{
}
public string GetRequestSql() => _value.ToUpper();
}
Интерфейсы нужны чтобы декларировать фунциональность которой обладает класс, например класс умеет вычислять квадратный корень, и есть клиенты которым нужна функциональность вычисления квадратного корня, тогда пусть он явно говорить о том, что умеет вычислять квадратный корень
public interface ICanCalculateSquareRoot
{
double SquareRoot(int a);
}
public interface ICanRaiseNumberToPower
{
int RaiserNumberToPower(int number, int power);
}
public class Calculator : ICanRaiseNumberToPower, ICanCalculateSquareRoot
{
public double SquareRoot(int a) => Math.Sqrt(a);
public int RaiserNumberToPower(int number, int power) => (int) Math.Pow(number, power);
}
Теперь если разным клиентам нужны функции вычисления корня и возведения в степень, они получат разные интерфейсы, вместо того чтобы получить полный объект. Интерфейсы очень ценны тем, что позволяют инвертировать зависимости, например было так:
//Domain
public class Order
{
public decimal Value { get; set; }
}
//
// Data Access Layer
public class OrderRepository
{
public IEnumerable<Order> GetOrders() => Enumerable.Empty<Order>();
}
//
// Application Layer
public class OrderCalculator
{
private readonly OrderRepository _orderRepository;
public OrderCalculator(OrderRepository orderRepository)
{
_orderRepository = orderRepository;
}
public decimal Calculate()
{
return _orderRepository.GetOrders().Sum(x => x.Value);
}
}
//
Application Layer зависит от Data Access Layer, т.е если мы захоти переехать на другую ORM, но придется изменять AppLayer, но можно сделать так:
//Domain
public class Order
{
public decimal Value { get; set; }
}
//
// Data Access Layer
public interface IOrderRepository
{
IEnumerable<Order> GetOrders();
}
public class OrderRepository:IOrderRepository
{
public IEnumerable<Order> GetOrders() => Enumerable.Empty<Order>();
}
//
// Application Layer
public class OrderCalculator
{
private readonly IOrderRepository _orderRepository;
public OrderCalculator(IOrderRepository orderRepository)
{
_orderRepository = orderRepository;
}
public decimal Calculate()
{
return _orderRepository.GetOrders().Sum(x => x.Value);
}
}
//
В Application Layer положим интерфейс IOrderRepository, и реализуем его в DataAccessLayer в классе OrderRepository, в результате получили DAL зависит от Application Layer, а не наоборот
Применяется когда ранее задуманная иерархия оказалась ненужной, тогда, чтобы избежать ненужной сложности выбрасывает наследников и свёртываем иерархию в один класс
Если в базовом классе есть метод, который подразделяется внутри на несколько шагов, которые различны в наследниках, тогда эти шаги нужно вынести в методы, их сделать абстрактными в базовом классе( и базовый класс, также абстрактным) и реализовать новые методы в наследниках.
Если для какого-то наследника хоть один метод в базовом классе не релевантен, то нужно заменить наследование на делегирование, теперь класс будет декларировать только нужные методы.Либо можно выделить родителя у базового класса, и наследование на этот базовый класс
Такое может случиться если раньше базовый класс раньше брал на себя слишком много, и теперь избавился от некоторых обязанностей, и теперь все методы базового класса становятся релевантными для классов, которые раньше делегировали Также такое может случиться, если изменилась семантика какого-то наследника, но такое бывает реже