LINUX.ORG.RU

История изменений

Исправление kaldeon, (текущая версия) :

Про наследование

«[DatabaseClient] может отправлять запросы, но ещё ты можешь унаследоваться и вмешаться в пул задач» — это не частичная инкапсуляция.

От реализации не зависит?

У DatabaseClient есть пул задач (или очередь запросов) — это реализация. Мы её сделали protected, чтобы дочерние классы могли в неё вмешаться и сделать по-своему. Любой доступ кем-то кроме тебя самого — не инкапсуляция.

Можно, конечно, сделать так, чтобы все protected члены не были реальной реализацией, а интерфейсом между родителем и дочерними классами. Скорее всего, так делают. Только тогда это не «частичная инкапсуляция», а полная. И тогда это буквально композиция в другой форме: вместо DatabaseClient implements IClient и MyClient(IClient) мы имеем MyClient extends DatabaseClient, при этом IClient никуда не делся, а растворился в тумане protected членов. Я люблю динамичные техники, но в данном случае это скорее неконтролируемый процесс.

Т.е. ты уже согласен, что правильное использование наследования требует хорошей квалификации?

Квалификация нужна для продуктивной работы, а для «правильного» использования наследования нужно всего-лишь понимать что такое наследование, чем оно не является и какие есть риски.

Если родительский класс используется только в нескольких местах, грош ему цена.

А если родительский класс - часть тиражируемой ООП библиотеки?

Если оставить за скобками нужность самого класса, а только говорить о наследуемости, то у библиотечного класса множество клиентов, поэтому он стоит дороже, чем «гроши». Но библиотеки нужно по-другому оценивать, через их абстракции, а не наследуемость. Может оно повсюду наследуется, но всё равно неудобно в использовании.

Про инкапсуляцию

Ну так C# гибкий. Можно опцианально выбирать только private и будет полная инкапсуляция.

Да. Но что это отрицает? Я не говорил, что ОО-языки мешают делать инкапсуляцию. Я говорил о том, что protected будет вреден для тех ситуаций, где инкапсуляция необходима.

Кроме того, если везде использовать private, то смысла в наследовании не будет, кроме сохранения интерфейса. Приватный метод даже переопределить нельзя.

И ещё уже несколько раз было упомянуто, что private — это якобы полная инкапсуляция. Это неудобное определение. Мы можем сделать поле приватным, но открыть его наружу с помощью геттеров/сеттеров. Ни о какой инкапсуляции, в таком случае, речь не идёт. public/private/protected — модификаторы доступа.

Да с чего ты взял, что при дизайне руководствовались стремлением к полной инкапсуляции, а не выгодам, получаемым от частичной инкапсуляции при её правильном использовании?

Частичной инкапсуляции не существует, по крайне мере в моих понятиях (здесь, дублировать не буду). А проблемы нет, если учитывать это обстоятельство при дизайне. Я говорил об этом:

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

А так да, не одним абстрактным кодом едины.

Про множественное наследование

Все люди вообще мало в чём согласны друг с другом. Но раз уж в язык добавили множественное наследование, значит кем-то оно считается полезным и на это есть основания, которые нужно принять к сведению, а не избегать.

Если следовать твоей логике, то раз множественное наследование не добавили ни в Java, ни в C#, то значит оно очень многими считается бесполезным? Вообще разве можно натягивать частный случай крестов на все остальные?

Я не отрицаю того, что оно многими считается бесполезным. Так и есть. И это не мой аргумент, а твой, ты с самого начала об этом говоришь. Я уже сказал, что у множественного наследования есть причины на существование, что его нельзя отбрасывать без раздумий как «плохую практику».

Множественное наследование не «натягивается» на все остальные языки, оно подходит (может быть полезным, удобным) к любому языку, в котором есть обычное наследование.

Perhaps composition? This is true for inheritance, and so, it’s even more true for multiple inheritance.

Т.е. таки вредная? [multiple inheritance]

Вредная — неподходящее слово. Если проект уже сделал для себя решение использовать наследование, множественное наследование может быть необходимо. Если следовать принципу composition over inheritance, то следует избегать наследование само по себе.

придумали тупицы, используют идиоты, разработчики C++ дураки».

Я такое где-то упоминал? Я лишь думаю, что от него больше вреда, чем пользы в отличие от обычного наследования.

Ты говорил, что это плохая практика. Смысл примерно тот же, только без агрессии. Но да, я не хотел создать впечатление, что ты это упоминал.

Про то, что у множнственного наследования есть отрицательные стороны, я сказал в самом первом упоминании, это не нужно доказывать:

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

(У множественного наследования есть и другие проблемы, но я упомянул именно это, потому что оно подходило под приводимый пример.)

Про то, что я неправильно применяю наследование

Есть Event и Day — несколько типов объектов, на которые можно подписаться. Допустим, в нашей системе они настолько родственны, что есть основание создать родительский класс Subscribable и включить в него значительную часть общей реализации.

В этот момент происходит привязка к реализации, что нарушает принцип инкапсуляции. Ты не можешь легко заменить Subscribable на, допустим, IntenseSubscribable — то же самое поведение, но с другой механикой.

Разве нельзя было унаследовать Event и Day от IntenseSubscribable, а IntenseSubscribable от Subscribable ?

В Event и Day уже происходят механики, жёстко привязанные к особенностям Subscribable.

Это твоя очередная архитектурная ошибка ?

[Как её можно избежать?]

Разве я не показывал, как?

Пришлось всю ветку процитировать. Возможно, было непонятно то, что в начале наследование хорошо работало, когда был только один родитель (Subscribable), но когда со временем (я не упомянул время) появился новый родитель (IntenseSubscribable), то из-за зависимости от реализации возникли проблемы? Сорян, теперь упомянул.

Понятно, что если бы нам заранее было известно, что множество классов (Event, Day, …) могут проявлять свойства разных частей системы (Subscribable, IntenseSubscribable, …), то тут нужно думать по-другому. Вся суть проблем с наследованием в том, что заранее мы не знаем куда будет развиваться проект, что оно заставляет выбрать иерархию перед тем, как на наш выбор может повлиять опыт.

Когда количество сущностей больше единицы, и тем более, если их неопределённо много, то используют динамические структуры данных, а не статическое наследование?

Мой пример был про объекты, на которые можно подписаться. Подписаться обозначает получать обновления. Можно получать обновления от события (время проведения, участники и т.д.), от одного дня (какую-то общую календарную информацию: загруженность, занятость), от человека, от чего угодно. Этих объектов много, но это не массив.

Структуры данных про другое, в них геометрические отношения между данными (жук является родителем человека в бинарном дереве), а не абстракциями.

Архитектурные ошибки — слишком широкое понятие. Что я должен был архитектурно сделать, чтобы избежать описанных проблем?

Изучить ООП дизайн? Почитать соответствующие книги?

Да. И не использовать наследование (после прочтения тоже).

Забиваю микроскопом гвозди, но они плохо забиваются и микроскоп почему-то потом плохо работает? … Поэтому сделал вывод, что микроскоп ущербен?

Метафора — не аргумент.

Вася, учи матчасть! Читай книжки про ООП.

Что хуже: убивать своё время, следуя непрошеным советам, или давать такие советы другим людям?

Исходная версия kaldeon, :

«[DatabaseClient] может отправлять запросы, но ещё ты можешь унаследоваться и вмешаться в пул задач» — это не частичная инкапсуляция.

От реализации не зависит?

У DatabaseClient есть пул задач (или очередь запросов) — это реализация. Мы её сделали protected, чтобы дочерние классы могли в неё вмешаться и сделать по-своему. Любой доступ кем-то кроме тебя самого — не инкапсуляция.

Можно, конечно, сделать так, чтобы все protected члены не были реальной реализацией, а интерфейсом между родителем и дочерними классами. Скорее всего, так делают. Только тогда это не «частичная инкапсуляция», а полная. И тогда это буквально композиция в другой форме: вместо DatabaseClient implements IClient и MyClient(IClient) мы имеем MyClient extends DatabaseClient, при этом IClient никуда не делся, а растворился в тумане protected членов. Я люблю динамичные техники, но в данном случае это скорее неконтролируемый процесс.

Т.е. ты уже согласен, что правильное использование наследования требует хорошей квалификации?

Квалификация нужна для продуктивной работы, а для «правильного» использования наследования нужно всего-лишь понимать что такое наследование, чем оно не является и какие есть риски.

Если родительский класс используется только в нескольких местах, грош ему цена.

А если родительский класс - часть тиражируемой ООП библиотеки?

Если оставить за скобками нужность самого класса, а только говорить о наследуемости, то у библиотечного класса множество клиентов, поэтому он стоит дороже, чем «гроши». Но библиотеки нужно по-другому оценивать, через их абстракции, а не наследуемость. Может оно повсюду наследуется, но всё равно неудобно в использовании.

Про инкапсуляцию

Ну так C# гибкий. Можно опцианально выбирать только private и будет полная инкапсуляция.

Да. Но что это отрицает? Я не говорил, что ОО-языки мешают делать инкапсуляцию. Я говорил о том, что protected будет вреден для тех ситуаций, где инкапсуляция необходима.

Кроме того, если везде использовать private, то смысла в наследовании не будет, кроме сохранения интерфейса. Приватный метод даже переопределить нельзя.

И ещё уже несколько раз было упомянуто, что private — это якобы полная инкапсуляция. Это неудобное определение. Мы можем сделать поле приватным, но открыть его наружу с помощью геттеров/сеттеров. Ни о какой инкапсуляции, в таком случае, речь не идёт. public/private/protected — модификаторы доступа.

Да с чего ты взял, что при дизайне руководствовались стремлением к полной инкапсуляции, а не выгодам, получаемым от частичной инкапсуляции при её правильном использовании?

Частичной инкапсуляции не существует, по крайне мере в моих понятиях (здесь, дублировать не буду). А проблемы нет, если учитывать это обстоятельство при дизайне. Я говорил об этом:

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

А так да, не одним абстрактным кодом едины.

Про множественное наследование

Все люди вообще мало в чём согласны друг с другом. Но раз уж в язык добавили множественное наследование, значит кем-то оно считается полезным и на это есть основания, которые нужно принять к сведению, а не избегать.

Если следовать твоей логике, то раз множественное наследование не добавили ни в Java, ни в C#, то значит оно очень многими считается бесполезным? Вообще разве можно натягивать частный случай крестов на все остальные?

Я не отрицаю того, что оно многими считается бесполезным. Так и есть. И это не мой аргумент, а твой, ты с самого начала об этом говоришь. Я уже сказал, что у множественного наследования есть причины на существование, что его нельзя отбрасывать без раздумий как «плохую практику».

Множественное наследование не «натягивается» на все остальные языки, оно подходит (может быть полезным, удобным) к любому языку, в котором есть обычное наследование.

Perhaps composition? This is true for inheritance, and so, it’s even more true for multiple inheritance.

Т.е. таки вредная? [multiple inheritance]

Вредная — неподходящее слово. Если проект уже сделал для себя решение использовать наследование, множественное наследование может быть необходимо. Если следовать принципу composition over inheritance, то следует избегать наследование само по себе.

придумали тупицы, используют идиоты, разработчики C++ дураки».

Я такое где-то упоминал? Я лишь думаю, что от него больше вреда, чем пользы в отличие от обычного наследования.

Ты говорил, что это плохая практика. Смысл примерно тот же, только без агрессии. Но да, я не хотел создать впечатление, что ты это упоминал.

Про то, что у множнственного наследования есть отрицательные стороны, я сказал в самом первом упоминании, это не нужно доказывать:

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

(У множественного наследования есть и другие проблемы, но я упомянул именно это, потому что оно подходило под приводимый пример.)

Про то, что я неправильно применяю наследование

Есть Event и Day — несколько типов объектов, на которые можно подписаться. Допустим, в нашей системе они настолько родственны, что есть основание создать родительский класс Subscribable и включить в него значительную часть общей реализации.

В этот момент происходит привязка к реализации, что нарушает принцип инкапсуляции. Ты не можешь легко заменить Subscribable на, допустим, IntenseSubscribable — то же самое поведение, но с другой механикой.

Разве нельзя было унаследовать Event и Day от IntenseSubscribable, а IntenseSubscribable от Subscribable ?

В Event и Day уже происходят механики, жёстко привязанные к особенностям Subscribable.

Это твоя очередная архитектурная ошибка ?

[Как её можно избежать?]

Разве я не показывал, как?

Пришлось всю ветку процитировать. Возможно, было непонятно то, что в начале наследование хорошо работало, когда был только один родитель (Subscribable), но когда со временем (я не упомянул время) появился новый родитель (IntenseSubscribable), то из-за зависимости от реализации возникли проблемы? Сорян, теперь упомянул.

Понятно, что если бы нам заранее было известно, что множество классов (Event, Day, …) могут проявлять свойства разных частей системы (Subscribable, IntenseSubscribable, …), то тут нужно думать по-другому. Вся суть проблем с наследованием в том, что заранее мы не знаем куда будет развиваться проект, что оно заставляет выбрать иерархию перед тем, как на наш выбор может повлиять опыт.

Когда количество сущностей больше единицы, и тем более, если их неопределённо много, то используют динамические структуры данных, а не статическое наследование?

Мой пример был про объекты, на которые можно подписаться. Подписаться обозначает получать обновления. Можно получать обновления от события (время проведения, участники и т.д.), от одного дня (какую-то общую календарную информацию: загруженность, занятость), от человека, от чего угодно. Этих объектов много, но это не массив.

Структуры данных про другое, в них геометрические отношения между данными (жук является родителем человека в бинарном дереве), а не абстракциями.

Архитектурные ошибки — слишком широкое понятие. Что я должен был архитектурно сделать, чтобы избежать описанных проблем?

Изучить ООП дизайн? Почитать соответствующие книги?

Да. И не использовать наследование (после прочтения тоже).

Забиваю микроскопом гвозди, но они плохо забиваются и микроскоп почему-то потом плохо работает? … Поэтому сделал вывод, что микроскоп ущербен?

Метафора — не аргумент.

Вася, учи матчасть! Читай книжки про ООП.

Что хуже: убивать своё время, следуя непрошеным советам, или давать такие советы другим людям?