Course program← Объектно ориентированное программирование
1История ООП
2Объект и Класс
3Базовые концепции ООП
4Принцип проектирования GRASP
5Принцип проектирования SOLID
6Паттерны GOF

Protected Variations (Устойчивость к изменениям)

This lesson is not translated yet – the Russian original is below.

TODO: отвалидировать – текст собран автоматически из материалов курса на Google Drive, проверьте формулировки и код.

Проблема модификации системы наиболее актуальна в условиях динамически изменяющихся требований.

Зачастую удаётся заранее выделить точки неустойчивости – места, которые будут меняться чаще остальных. Тогда эти точки определяют в качестве интерфейсов, и изменение прячется за границей, не расходясь по всей системе.

// Точка неустойчивости: способ доставки уведомлений будет меняться
public interface INotifier
{
    void Notify(User user, string message);
}

public class EmailNotifier : INotifier { public void Notify(User u, string m) { } }
public class SmsNotifier : INotifier { public void Notify(User u, string m) { } }
// Точка неустойчивости: способ доставки уведомлений будет меняться
class INotifier {
public:
    virtual void Notify(const User& user, const std::string& message) = 0;
    virtual ~INotifier() = default;
};

class EmailNotifier : public INotifier {
public:
    void Notify(const User& user, const std::string& message) override {}
};

class SmsNotifier : public INotifier {
public:
    void Notify(const User& user, const std::string& message) override {}
};
// Точка неустойчивости: способ доставки уведомлений будет меняться
type Notifier interface {
	Notify(user User, message string) error
}

type EmailNotifier struct{}

func (EmailNotifier) Notify(user User, message string) error { return nil }

type SMSNotifier struct{}

func (SMSNotifier) Notify(user User, message string) error { return nil }
# Точка неустойчивости: способ доставки уведомлений будет меняться
from typing import Protocol

class Notifier(Protocol):
    def notify(self, user: User, message: str) -> None: ...

class EmailNotifier:
    def notify(self, user: User, message: str) -> None:
        pass

class SmsNotifier:
    def notify(self, user: User, message: str) -> None:
        pass
// Точка неустойчивости: способ доставки уведомлений будет меняться
interface Notifier {
    notify(user: User, message: string): void;
}

class EmailNotifier implements Notifier {
    notify(user: User, message: string): void {}
}

class SmsNotifier implements Notifier {
    notify(user: User, message: string): void {}
}
// Точка неустойчивости: способ доставки уведомлений будет меняться
public interface Notifier {
    void notify(User user, String message);
}

public class EmailNotifier implements Notifier {
    public void notify(User user, String message) { }
}

public class SmsNotifier implements Notifier {
    public void notify(User user, String message) { }
}
// Точка неустойчивости: способ доставки уведомлений будет меняться
fun interface Notifier {
    fun notify(user: User, message: String)
}

class EmailNotifier : Notifier {
    override fun notify(user: User, message: String) = Unit
}

class SmsNotifier : Notifier {
    override fun notify(user: User, message: String) = Unit
}

Главная сложность

Принцип требует угадать, что именно будет меняться. Защита от изменений, которых не произошло, – это лишние интерфейсы и лишние слои, за которые платят все читатели кода.

Практическое правило: защищайте то, что уже менялось хотя бы раз, а не то, что теоретически может измениться.