Дополнение чужого функционала
07.01.2017
|
velkin |
Предположим имеется сторонняя библиотека (пусть будет C++), то есть написанная другими людьми, и в ней от 100 до 1000 классов (парадигма ООП). Причём это не столь важно, теоретически может быть 10, а может и 10000. Далее каждый класс содержит некую функциональность, которую хотелось бы расширить. При этом каждый расширенный класс по функционалу не просто содержит, а именно является тем самым классом из сторонней библиотеки.
В этом случае мне на ум приходят два варианта:
1) Создаём производный класс и уже в нём проводим нужные изменения
2) Изменяем классы в сторонней библиотеке
Если подумать, то недостаток первого варианта это само наследование. Речь даже не о скоростных характеристиках, а просто о том, что в сторонней библиотеке могут быть установлены закрытые (private) модификаторы доступа. Опять же происходит полное дублирование классов, в своей программе придётся использовать только производные и тщательно за этим следить.
Изменение же сторонней библиотеки в случае её изменения от версии к версии повлечёт за собой изменения уже изменённых классов, то есть процесс обновления будет не сказать, чтобы очень простым. При этом есть вероятность поломать чужой функционал. И в отличие от первого варианта, чем больше изменений произведено, тем сложнее потом будет обновляться до новых версий, не говоря уже о перекомпиляции чужой библиотеки.
В принципе и всё, интересно было бы услышать идеи на этот счёт, хотя вопрос скорее теоретический об эволюции ПО и отказа от повторного изобретения велосипедов.
В этом случае мне на ум приходят два варианта:
1) Создаём производный класс и уже в нём проводим нужные изменения
2) Изменяем классы в сторонней библиотеке
Если подумать, то недостаток первого варианта это само наследование. Речь даже не о скоростных характеристиках, а просто о том, что в сторонней библиотеке могут быть установлены закрытые (private) модификаторы доступа. Опять же происходит полное дублирование классов, в своей программе придётся использовать только производные и тщательно за этим следить.
Изменение же сторонней библиотеки в случае её изменения от версии к версии повлечёт за собой изменения уже изменённых классов, то есть процесс обновления будет не сказать, чтобы очень простым. При этом есть вероятность поломать чужой функционал. И в отличие от первого варианта, чем больше изменений произведено, тем сложнее потом будет обновляться до новых версий, не говоря уже о перекомпиляции чужой библиотеки.
В принципе и всё, интересно было бы услышать идеи на этот счёт, хотя вопрос скорее теоретический об эволюции ПО и отказа от повторного изобретения велосипедов.
07.01.2017 8 комментариев |
LVV>Композицию и агрегацию можно вместо наследования.
V>Предположим имеется сторонняя библиотека (пусть будет C++), то есть написанная другими людьми, и в ней от 100 до 1000 классов (парадигма ООП). Причём это не столь важно, теоретически может быть 10, а может и 10000. Далее каждый класс содержит некую функциональность, которую хотелось бы расширить. При этом каждый расширенный класс по функционалу не просто содержит, а именно является тем самым классом из сторонней библиотеки.
Написал чтобы не возникало вопросов про отношение содержит, коим является агрегирование. А вот является это именно наследование. C моей точки зрения агрегирование в данном случае гораздо хуже, чем открытое наследование, так как проблему модификаторов доступа чужих классов не решает, но накладывает ограничение на использование членов класса. Собственно говоря с агрегированием получится ещё более сложная архитектура. Но если есть какие-то мысли, то интересно было бы послушать.
V>>Предположим имеется сторонняя библиотека (пусть будет C++), то есть написанная другими людьми, и в ней от 100 до 1000 классов (парадигма ООП). Причём это не столь важно, теоретически может быть 10, а может и 10000. Далее каждый класс содержит некую функциональность, которую хотелось бы расширить. При этом каждый расширенный класс по функционалу не просто содержит, а именно является тем самым классом из сторонней библиотеки.
V>Написал чтобы не возникало вопросов про отношение содержит, коим является агрегирование. А вот является это именно наследование. C моей точки зрения агрегирование в данном случае гораздо хуже, чем открытое наследование, так как проблему модификаторов доступа чужих классов не решает, но накладывает ограничение на использование членов класса. Собственно говоря с агрегированием получится ещё более сложная архитектура. Но если есть какие-то мысли, то интересно было бы послушать.
Собственно, я подумал, что ОБЫЧНО библиотечные классы не предназначены для наследования.
Если они разрабатывались именно с учетом будущего наследования, то вопросов нет — надо наследовать.
А если нет?
Вот жеж STL никак для наследования не предназначена.
А написать обертку при необходимости с использованием композиции — как 2 байта переслать.
Кроме того, вопросы вызывает потребность изменения модификаторов доступа.
Ведь разработчик не от былды расставил модификаторы.
А вы хотите его модель доступа порушить?
Нафига это нужно?
LVV>Композицию и агрегацию можно вместо наследования.
Паттерн decorator наше всё.
То, чего ими нельзя получить ("расширить состояние" объектов), не уверен, что вообще стоит делать.
На главной странице написано, что заметке 4 месяца. В тексте заметки — что она 2017 года. Короче, обвинения в некропостинге не принимаются.
V>Предположим имеется сторонняя библиотека (пусть будет C++), то есть написанная другими людьми, и в ней от 100 до 1000 классов (парадигма ООП). Причём это не столь важно, теоретически может быть 10, а может и 10000. Далее каждый класс содержит некую функциональность, которую хотелось бы расширить. При этом каждый расширенный класс по функционалу не просто содержит, а именно является тем самым классом из сторонней библиотеки.
V>В этом случае мне на ум приходят два варианта:
V>1) Создаём производный класс и уже в нём проводим нужные изменения
V>2) Изменяем классы в сторонней библиотеке
V>Если подумать, то недостаток первого варианта это само наследование. Речь даже не о скоростных характеристиках, а просто о том, что в сторонней библиотеке могут быть установлены закрытые (private) модификаторы доступа. Опять же происходит полное дублирование классов, в своей программе придётся использовать только производные и тщательно за этим следить.
Если я правильно понимаю, что значат слова «каждый расширенный класс по функционалу не просто содержит, а именно является тем самым классом из сторонней библиотеки», наследование приведёт к тому, что структура кода вступит в противоречие с описываемой им реальностью. Лично я считаю, что после этого код становится плохо поддерживаемым, в частности, незнакомый с ним человек будет долго тупить, зачем там класс-родитель и класс-потомок, ответ придётся прописывать в комментариях, конфлюэнсе или передавать изустно и т.п.
V>Изменение же сторонней библиотеки в случае её изменения от версии к версии повлечёт за собой изменения уже изменённых классов, то есть процесс обновления будет не сказать, чтобы очень простым. При этом есть вероятность поломать чужой функционал. И в отличие от первого варианта, чем больше изменений произведено, тем сложнее потом будет обновляться до новых версий, не говоря уже о перекомпиляции чужой библиотеки.
Так делать, разумеется, тоже не стоит — любой будущий багфикс (со стороны автора библиотеки) будет иметь непредсказуемую стоимость интеграции. То есть, такое решение приведёт к росту рисков, а они должны уменьшаться.
V>В принципе и всё, интересно было бы услышать идеи на этот счёт, хотя вопрос скорее теоретический об эволюции ПО и отказа от повторного изобретения велосипедов.
Если я правильно понимаю, что значат слова «каждый расширенный класс по функционалу не просто содержит, а именно является тем самым классом из сторонней библиотеки», значит библиотека в текущем виде — сырая. Надо просто подождать новую версию или поискать другую, где уже всё написано.
Хорошо, когда недостаточно имплементированный в текущей версии функционал вынесен в заведомо расширяемое место, например командный DSL. Тогда можно писать плохие раздутые запросы с реализацией недостающего функционала, а после обновления — заменять их на хорошие короткие.
В крайнем случае, пусть будут предусмотрены колбэки, которым предоставлен r/w-доступ ко всем необходимым кишкам.
Если ни того, ни другого не будет (и при этом налицо нехватка функционала, который, как подразумевается, должен быть без наследования) — я сто раз подумаю, прежде чем пользоваться такой библиотекой.
Кстати, это причина, по которой у меня аллергия на слово YAGNI.
>Речь даже не о скоростных характеристиках, а просто о том, что в сторонней библиотеке могут быть установлены закрытые (private) модификаторы доступа.
Вот про это хотелось бы сказать отдельно. Лично я считаю, что разработчик класса должен чётко думать над выбором private/protected исходя из того, хочет ли он заблокировать соответствующую кишку от (предполагаемого) автора класса-потомка. В соответствии с этой логикой, что значит «могут быть установлены»?! Если установлены — не трогай, это же не просто так сделано!
К моему большому удивлению, оказалось, что примерно половина программистов считает как я, а другая — тупо лепит по дефолту private, по мере необходимости меняя его на protected. Разумеется, я считаю, что это недостаточно времени потрачено на проектирование
V>Предположим имеется сторонняя библиотека (пусть будет C++), то есть написанная другими людьми, и в ней от 100 до 1000 классов (парадигма ООП). Причём это не столь важно, теоретически может быть 10, а может и 10000. Далее каждый класс содержит некую функциональность, которую хотелось бы расширить. При этом каждый расширенный класс по функционалу не просто содержит, а именно является тем самым классом из сторонней библиотеки.
Очевидно, если есть возможность договорится с авторами просто сделайте пулреквест с нужными правками.
Если код брошен и есть возможность — форкнуть и написать свою версию.
Далее по убывающей уже исходя из возможностей языка.
V>В этом случае мне на ум приходят два варианта:
V>1) Создаём производный класс и уже в нём проводим нужные изменения
Точки кастомизации в библиотеке могут быть организованы множеством способов:
политики, трейтсы, коллбеки, виртуальные функции.
Чисто абстрактные классы (интерфейсы) стоят особняком, само их введение намекает на реализацию пользователем библиотеки.
Если разработчики библиотеки решили использовать кастомизацию через виртуальные функции конкретных (не абстрактных) классов,
то наследование от библиотечных классов будет в рамках их замысла.
Такой подход использовался и в старых ООП библиотеках (OWL, MFC), и в Qt для виджетов применяется.
Например, в MFC можно было пользоваться библиотечным CEdit, а можно было сделать наследованный CMyCoolEdit со свистелками.
V>2) Изменяем классы в сторонней библиотеке
Считаю, что такой подход приемлем только в том случае, когда библиотека не развивается и не правится разработчиком.
Тогда ее надо затаскивать в свой репозиторий и уже рассматривать не как сторонний код.