This lesson is not translated yet – the Russian original is below.
TODO: отвалидировать – текст собран автоматически из материалов курса на Google Drive, проверьте формулировки и код.
Базовый принцип разделения ответственности.
Ответственность на себя должен брать тот, кто обладает максимумом информации для её исполнения. Если класс – «эксперт» над какими-то данными, то методы для обработки этих данных должны быть в этом классе.
Как это выглядит на практике
Типичное нарушение: сумму заказа считает сервис, которому для этого приходится вытаскивать из заказа список позиций. Данные лежат в одном месте, логика – в другом, и любое изменение структуры позиций ломает сервис.
// Нарушение: считать сумму лезет тот, кто не владеет данными
public class OrderService
{
public decimal CalculateTotal(Order order)
{
decimal total = 0;
foreach (var item in order.Items)
total += item.Price * item.Quantity;
return total;
}
}
// Information Expert: считает тот, у кого есть данные
public class Order
{
private readonly List<OrderItem> _items = new();
public decimal Total => _items.Sum(i => i.Price * i.Quantity);
public void Add(OrderItem item) => _items.Add(item);
}
// Нарушение: считать сумму лезет тот, кто не владеет данными
class OrderService {
public:
double CalculateTotal(const Order& order) const {
double total = 0;
for (const auto& item : order.items()) {
total += item.price * item.quantity;
}
return total;
}
};
// Information Expert: считает тот, у кого есть данные
class Order {
public:
void Add(const OrderItem& item) { items_.push_back(item); }
double Total() const {
double total = 0;
for (const auto& item : items_) {
total += item.price * item.quantity;
}
return total;
}
private:
std::vector<OrderItem> items_;
};
// Нарушение: считать сумму лезет тот, кто не владеет данными
type OrderService struct{}
func (OrderService) CalculateTotal(order Order) float64 {
total := 0.0
for _, item := range order.Items {
total += item.Price * float64(item.Quantity)
}
return total
}
// Information Expert: считает тот, у кого есть данные
type Order struct {
items []OrderItem
}
func (o *Order) Add(item OrderItem) { o.items = append(o.items, item) }
func (o *Order) Total() float64 {
total := 0.0
for _, item := range o.items {
total += item.Price * float64(item.Quantity)
}
return total
}
# Нарушение: считать сумму лезет тот, кто не владеет данными
class OrderService:
def calculate_total(self, order: "Order") -> float:
return sum(i.price * i.quantity for i in order.items)
# Information Expert: считает тот, у кого есть данные
class Order:
def __init__(self) -> None:
self._items: list[OrderItem] = []
def add(self, item: OrderItem) -> None:
self._items.append(item)
@property
def total(self) -> float:
return sum(i.price * i.quantity for i in self._items)
// Нарушение: считать сумму лезет тот, кто не владеет данными
class OrderService {
calculateTotal(order: Order): number {
return order.items.reduce((sum, i) => sum + i.price * i.quantity, 0);
}
}
// Information Expert: считает тот, у кого есть данные
class Order {
private readonly items: OrderItem[] = [];
add(item: OrderItem): void {
this.items.push(item);
}
get total(): number {
return this.items.reduce((sum, i) => sum + i.price * i.quantity, 0);
}
}
// Нарушение: считать сумму лезет тот, кто не владеет данными
public class OrderService {
public BigDecimal calculateTotal(Order order) {
return order.getItems().stream()
.map(i -> i.getPrice().multiply(BigDecimal.valueOf(i.getQuantity())))
.reduce(BigDecimal.ZERO, BigDecimal::add);
}
}
// Information Expert: считает тот, у кого есть данные
public class Order {
private final List<OrderItem> items = new ArrayList<>();
public void add(OrderItem item) {
items.add(item);
}
public BigDecimal getTotal() {
return items.stream()
.map(i -> i.getPrice().multiply(BigDecimal.valueOf(i.getQuantity())))
.reduce(BigDecimal.ZERO, BigDecimal::add);
}
}
// Нарушение: считать сумму лезет тот, кто не владеет данными
class OrderService {
fun calculateTotal(order: Order): Double = order.items.sumOf { it.price * it.quantity }
}
// Information Expert: считает тот, у кого есть данные
class Order {
private val items = mutableListOf<OrderItem>()
fun add(item: OrderItem) {
items += item
}
val total: Double get() = items.sumOf { it.price * it.quantity }
}
Когда принцип не работает
Эксперт по данным не всегда должен брать ответственность целиком. Если для расчёта нужны ещё и налоговые правила, и курсы валют, заказ быстро превратится в класс, который знает всё на свете. Тогда вступает в силу High Cohesion – часть работы уходит отдельному объекту.