Пример из жизни.
Пусть мы хотим сделать "компонент", который наследуется от TClientDataSet и добавляет ему некие "прелестные фичи".
Вариант "в лоб"
Сделали? Отлично!
И тут мы задумались о том, "а как нам "подсказать" будущему пользователю нашего "компонента", что в Design-Time его создавать не надо, и НАДО звать НАШ "кошерный" конструктор".
И ТОЛЬКО его!!!
По-моему - решение на поверхности - отказаться от наследования и сделать агрегацию. (Есть ещё вариант - "фабричный метод", это если ПОВЕДЕНИЕ менять не нужно. Но об этом - ПОЗЖЕ)
Создаём так:
Идея понятна?
Пусть мы хотим сделать "компонент", который наследуется от TClientDataSet и добавляет ему некие "прелестные фичи".
Вариант "в лоб"
TmySmartDataSet = class(TClientDataSet) constructor Create(anOwner : TComponent; тут много разных дополнительных параметров); reintroduce; ... inherited Create(anOwner);тут эти параметры используются
Сделали? Отлично!
И тут мы задумались о том, "а как нам "подсказать" будущему пользователю нашего "компонента", что в Design-Time его создавать не надо, и НАДО звать НАШ "кошерный" конструктор".
И ТОЛЬКО его!!!
По-моему - решение на поверхности - отказаться от наследования и сделать агрегацию. (Есть ещё вариант - "фабричный метод", это если ПОВЕДЕНИЕ менять не нужно. Но об этом - ПОЗЖЕ)
TmySmartDataSet = class(TObject) constructor Create(anOwner : TComponent; тут много разных дополнительных параметров); reintroduce; private FInnerSmartDataSet : TClientDataSet; public InnerSmartDataSet : TClientDataSet read FInnerSmartDataSet; implementation TmySmartDataSetImplementation = class(TClientDataSet) //тут переопределяем ПОВЕДЕНИЕ ... FInnerSmartDataSet := TmySmartDataSetImplementation.Create(anOwner); inherited Create;тут эти параметры используются, чтобы довесить "чудные штучки" и "поднастроить" FInnerSmartDataSet
Создаём так:
l_MySmartDataSet := TmySmartDataSet.Create(anOwner, тут остальные "чудные" параметры); SomeDataSource.DataSet := l_MySmartDataSet.InnerSmartDataSet;
Идея понятна?
«Идея понятна?»
ОтветитьУдалить-- Да понятна-то она понятна :-)
Вот только TmySmartDataSet уже никакой не DataSet, его в качестве набора данных для DataSource уже не используешь. Теряется прозрачность, что не есть гут, да и исходной задаче «Пусть мы хотим сделать "компонент", который **наследуется** от TClientDataSet и добавляет ему некие "прелестные фичи".» не соответствует.
Хотя понятно, что всё зависит от задачи. Вероятно, для Ваших целей означенная мною прозрачность не нужна.
Интересно, к чему Вы подняли эту тему? Без контекста - это демонстрация обычной агрегации и совершенно непонятно, как она соотносится с наследованием.
Вообще же, мне казалось, что для реализации заявленной Вами цели «Пусть мы хотим сделать "компонент", который наследуется от TClientDataSet и добавляет ему некие "прелестные фичи".» Ваши любимые mixin-ы и показаны :-)
Но опять же, всё зависит от контекста, который непонятен.
"Вообще же, мне казалось, что для реализации заявленной Вами цели «Пусть мы хотим сделать "компонент", который наследуется от TClientDataSet и добавляет ему некие "прелестные фичи".» Ваши любимые mixin-ы и показаны :-)"
Удалить-- Это кстати - да. Но не хотелось ещё и mixin'ы включать. Боюсь - мысль совсем бы потерялась бы.
"Вот только TmySmartDataSet уже никакой не DataSet, его в качестве набора данных для DataSource уже не используешь."
УдалитьНевнимательно читаете:
l_MySmartDataSet := TmySmartDataSet.Create(anOwner, тут остальные "чудные" параметры);
SomeDataSource.DataSet := l_MySmartDataSet.InnerSmartDataSet;
"Интересно, к чему Вы подняли эту тему?"
УдалитьПросто - хотелось отметить, что наследование далеко не всегда стоит использовать. Далеко не всегда.
"Без контекста - это демонстрация обычной агрегации и совершенно непонятно, как она соотносится с наследованием."
ОтветитьУдалить-- не поверите. Далеко не все люди вообще вспоминают про агрегацию в подобных случаях. Просто хотелось вспомнить, что в данном случае - СТОИТ рассматривать СНАЧАЛА агрегацию, а уж ПОТОМ - наследование.
NameRec: Ok.
УдалитьНо в таком случае, мне казалось, следует делать акцент на внешней непрозрачности полученной конструкции.
Есть много задач (особенно в runtime), где такая непрозрачность ничему не мешает.
С другой стороны, например в design-time, непрозрачность может оказаться неприятным обстоятельством. И mix-in здесь, как раз может оказаться интересным выходом :-)
Согласен.
УдалитьNameRec:
ОтветитьУдалить«Невнимательно читаете:
l_MySmartDataSet := TmySmartDataSet.Create(anOwner, тут остальные "чудные" параметры);
SomeDataSource.DataSet := l_MySmartDataSet.InnerSmartDataSet;»
-- Да нет, внимательно :-)
Я кстати, опасался, что Вы меня именно так и поймёте.
Я имел ввиду, что *сам класс* TmySmartDataSet не может выступать набором данных для DataSource.
Как я рассуждаю. Имя потомка DataSet (не обязательно TClientDataSet) я могу разместить его на форме.
Настроить там его параметры (возможно, их разумно выделить в отдельный класс), а затем указать в качестве набора данных для DataSource.
В описанном Вами варианте это тоже можно сделать, но не так прозрачно. Придётся учитывать нестандартное (уникальное) устройство класса TmySmartDataSet, понимать, что суффикс "DataSet" в его названии - не имеет отношения к его классовой принадлежности. В общем - отступление от унификации интуитивно воспринимается скептически.
Но опять же, повторюсь... Всё зависит от задачи :-)
NameRec:
Удалить*** Поправка: "Как я рассуждаю. Имя потомка DataSet..." следует читать "Как я рассуждаю. ИМЕЯ потомка DataSet..."
"Я кстати, опасался, что Вы меня именно так и поймёте."
УдалитьХорошо, что опасались :-)
Значит мы начинаем в правильном направлении вместе двигаться.
Я сам виноват. Я не написал того, что сама формулировка "Пусть мы хотим сделать "компонент", который наследуется от TClientDataSet" - УЖЕ в корне - неверна. Слово НАСЛЕДОВАНИЕ уже в "постановку задачи" вынесено.
В этом - корень проблемы :-) Жаль конечно, что я её сразу не озвучил :-)
"Но опять же, повторюсь... Всё зависит от задачи :-)"
Тоже - видимо я недописал - КОНЕЧНО в виду имелся случай, когда в перекрытый конструктор DataSet впихивают ссылки на всякие TEdit/TLabel. КОНЕЧНО именно этот случай :-)
"Настроить там его параметры (**возможно, их разумно выделить в отдельный класс**), а затем указать в качестве набора данных для DataSource."
УдалитьПравильно кстати рассуждаете :-)
"Выделить параметры в ОТДЕЛЬНЫЙ класс" - это была СЛЕДУЮЩАЯ мысль, которую я к сожалению не успел/поленился озвучить.
УдалитьNameRec:
Удалить«Тоже - видимо я недописал - КОНЕЧНО в виду имелся случай, когда в перекрытый конструктор DataSet впихивают ссылки на всякие TEdit/TLabel. КОНЕЧНО именно этот случай :-)»
-- Вот тут, признаться, Вы меня совсем заинтриговали :-)
После этих слов я начинаю представлять себе некий контроллер, агрегирующий в себе набор данных (ClientDataSet) и зависящий от передаваемых ему в конструкторе TEdit/TLabel.
Поскольку Вы упомянули TEdit, то вероятно, что это не случайно, и TEdit (возможно TDBEdit) должен быть связан с каким-то полем в ClientDataSet и служить задачам изменения данных в DataSet.
При заданных Вами вводных, очень похоже, что и DataSource будет агрегироваться в TmySmartDataSet, поскольку элементы управления связываются с набором данных через него. Ну, можно предположить ещё DBNavigator, который также можно передать в конструктор обсуждаемого класса.
Я правильно понимаю идею?
"Я правильно понимаю идею?"
УдалитьВы её скорее доразвили.
"После этих слов я начинаю представлять себе некий контроллер"
УдалитьНу контроллер конечно и имелся в виду. Но это ВЫ поняли. Просто БЫСТРО добраться от "агрегация vs наследование" до "контроллеров" - мне показалось сложным.