Страницы

Поиск по вопросам

четверг, 9 апреля 2020 г.

Подмена базового класса в сложившейся архитектуре классов

#c_sharp #шаблоны_проектирования

                    
Имеется базовый класс (предположим, Window), у которого огромное число классов-потомков
(BlackWindow, TransperentWindow, ...).

Предположим, что я хочу добавить в архитектуру класс BorderedWindow, который перегружает
пару виртуальных функций родительского класса Window. И далее я хочу, чтобы все имеющиеся
классы-потомки могли создаваться как от Window, так и от BorderedWindow.

Проблему можно проиллюстрировать следующим кодом:

class Window
{
    public virtual Rectangle ClientArea()
    {
        return new Rectangle(0, 0, 100, 50);
    }
}

class BlackWindow : Window
{
    public BlackWindow(Window window) { ... }
}

class BorderedWindow : Window
{
    public overide Rectangle ClientArea()
    {
        return new Rectangle(5, 5, 110, 60);
    }
}

Window window = new Window();
BorderedWindow borderedWindow = new BorderedWindow();
new BlackWindow(window).ClientArea();                // Rectangle(0, 0, 100, 50), ОК
new BlackWindow(borderedWindow).ClientArea();        // Rectangle(0, 0, 100, 50), FAIL!


В целом, здесь можно применить паттерн Декоратор. Но этого делать не хочется, так
как последуют существенные изменения классов-потомков. Придётся для каждой функции
класса Window писать функцию-"обёртку" в каждом классе-наследнике. Для десятка функций
и десятка классов объём кода вырастает драматически.

Как можно исправить ситуацию?
    


Ответы

Ответ 1



Почему бы тогда не «Стратегия»? Вместо довольно неудобного наследования переходите на прогрессивную композицию. Пусть любой из классов получает ClientStrategy как часть конструктора. interface IClientAreaStrategy { Rectangle ClientArea { get; } } class DefaultClientAreaStrategy : IClientAreaStrategy { Rectangle clientArea = new Rectangle(0, 0, 100, 50); public Rectangle ClientArea { get { return clientArea; } } } class BorderedClientAreaStrategy : IClientAreaStrategy { Rectangle clientArea = new Rectangle(5, 5, 110, 60); public Rectangle ClientArea { get { return clientArea; } } } class Window { protected readonly IClientAreaStrategy clientAreaStrategy; public Rectangle ClientArea { get { return clientAreaStrategy.ClientArea; } } public Window(IClientAreaStrategy clientAreaStrategy) { this.clientAreaStrategy = clientAreaStrategy; } } class BlackWindow : Window { public BlackWindow(Window window) : base(window.ClientStrategy) { // ... } } (Кстати, у нужно ли наследование для BlackWindow? Возможно, вам снова нужна композиция?) Для конкретно этого случая вам не нужен целый интерфейс IClientAreaStrategy, а просто Func или даже просто Rectangle. Но в более сложном случае понадобится интерфейс.

Комментариев нет:

Отправить комментарий