Я уже давно спрашиваю на разных форумах про множественное наследование и интерфейсы в Delphi, и никто не до сих пор не объяснил на пальцах суть всего этого.
Вот пример задачи, для которой мне нужно множественное наследование. В Delphi есть класс TStream, и наследники TFileStream и TMemoryStream. Я хочу добавить ко всем этим стримам функции для более удобного чтения/записи конкретных типов данных: ReadBoolean, ReadInteger, ReadString, WriteBoolean, WriteInteger, WriteString и т.д. И тут начинаются проблему: я не могу добавить эти методы к классу TStream, а могу только к наследникам. Как мне добавить эти методы к разным типам стримов без дублирования кода?
Насколько я понял, для этого можно использовать интерфейсы, но я пока не знаю как конкретно это реализовать, и вообще в Delphi интерфейсы вроде привязаны к COM, у них у всех есть GUID, и это уже вызывает тоску.
А в других языках они есть? Я пока не начал это осваивать, но вроде понятен их смысл – можно прикрутить к любому типу, например, integer, какую-то функцию, например GetFactorial, и писать в коде fact1:=12.GetFactorial;
Очевидно, хелперы можно применить для моей задачи – прикрутить их напрямую к классу TStream. Но я слышал, что использование хелперов опасно, поскольку оно противоречит парадигме ООП. Хотелось бы узнать об этом подробнее.
Мне кажется, главная опасность использования хелперов с классами возникает там, где есть динамические функции. И я думаю, что для избегания этих проблем я буду делать так: все функции, реализованные через хелперы, будут иметь название, начинающееся с hf, например myfilestream1.hfWriteBoolean();
"Ты должен сделать добро из зла, потому что его больше не из чего сделать". АБ Стругацкие.
K>А в других языках они есть? Я пока не начал это осваивать, но вроде понятен их смысл – можно прикрутить к любому типу, например, integer, какую-то функцию, например GetFactorial, и писать в коде fact1:=12.GetFactorial;
В C# есть Extension Methods.
K>Очевидно, хелперы можно применить для моей задачи – прикрутить их напрямую к классу TStream. Но я слышал, что использование хелперов опасно, поскольку оно противоречит парадигме ООП. Хотелось бы узнать об этом подробнее.
В .NET на методах расширения LINQ сделан, польза там есть на лицо. Не думаю что в Delphi от хелперов есть какая-то опасность, хелперы не добавляют к классам/записям дополнительное состояние, хотя могут менять внутренее.
K>Мне кажется, главная опасность использования хелперов с классами возникает там, где есть динамические функции. И я думаю, что для избегания этих проблем я буду делать так: все функции, реализованные через хелперы, будут иметь название, начинающееся с hf, например myfilestream1.hfWriteBoolean();
Имхо, префиксы в названиях неинформативны и наоборот затрудняют понимание кода, да и в библиотеке Delphi к названиям методов/функций префиксы нигде не добавляются.
Хелперы в Дельфи можно использовать только ограниченно не потому что они каким то там парадигмам противоречат, а потому что в одном юните у тебя есть доступ только к одному хелперу к заданному типу.
ТО есть если у тебя есть хелперы к стриму в юните1 и юните2, то из юнита 3 ты сможешь иметь доступ только к одному из них (регулируется порядком в котором ты юниты в uses подключаешь)
А в целом полно языков где подобные механизмы есть. В .NET экосистеме C#/VB.NET/F# , в Java — Kotlin/Groovy
K>>Мне кажется, главная опасность использования хелперов с классами возникает там, где есть динамические функции. И я думаю, что для избегания этих проблем я буду делать так: все функции, реализованные через хелперы, будут иметь название, начинающееся с hf, например myfilestream1.hfWriteBoolean();
S>Имхо, префиксы в названиях неинформативны и наоборот затрудняют понимание кода, да и в библиотеке Delphi к названиям методов/функций префиксы нигде не добавляются.
С чего бы это?
Я наоборот скорее всего буду теперь очень активно использовать префиксы. Например, я практически никогда не использую секцию private у классов, поскольку на практике изредка оказывается таки нужно обратиться к полю или методу, который скрыт этой функцией. Поэтому лучше названия эти поля и методы объявлять в public, но названия начинать с префикса, например pr: MyIntegerArray.prDataChanged. Собственно обычно для полей, объявленных в private, используется префикс f, это тоже более-менее удобно.
"Ты должен сделать добро из зла, потому что его больше не из чего сделать". АБ Стругацкие.
Extension method это просто другой синтаксис для записи утилитного метода, в который передаётся параметр this. Ты можешь написать класс IntegerUtil и вызывать его как IntegerUtil.GetFactorial(12). Ничего опасного или страшного в них нет. Просто надо понимать, что это т.н. синтаксический сахар, т.е. конструкция, которая тривиально преобразовывается в другую конструкцию и только выглядит как метод.
K>>>Мне кажется, главная опасность использования хелперов с классами возникает там, где есть динамические функции. И я думаю, что для избегания этих проблем я буду делать так: все функции, реализованные через хелперы, будут иметь название, начинающееся с hf, например myfilestream1.hfWriteBoolean();
S>>Имхо, префиксы в названиях неинформативны и наоборот затрудняют понимание кода, да и в библиотеке Delphi к названиям методов/функций префиксы нигде не добавляются.
K>С чего бы это? K>Я наоборот скорее всего буду теперь очень активно использовать префиксы. Например, я практически никогда не использую секцию private у классов, поскольку на практике изредка оказывается таки нужно обратиться к полю или методу, который скрыт этой функцией. Поэтому лучше названия эти поля и методы объявлять в public, но названия начинать с префикса, например pr: MyIntegerArray.prDataChanged. Собственно обычно для полей, объявленных в private, используется префикс f, это тоже более-менее удобно.
Я стараюсь придерживаюсь принсипов SOLID, а префиксы да, и в самом деле не важны, мне они лично не нраятся.
Здравствуйте, Khimik, Вы писали:
K>Я наоборот скорее всего буду теперь очень активно использовать префиксы. Например, я практически никогда не использую секцию private у классов, поскольку на практике изредка оказывается таки нужно обратиться к полю или методу, который скрыт этой функцией. Поэтому лучше названия эти поля и методы объявлять в public, но названия начинать с префикса, например pr: MyIntegerArray.prDataChanged. Собственно обычно для полей, объявленных в private, используется префикс f, это тоже более-менее удобно.
Для контролируемого обращения к приватным полям надо использовать сеттеры и геттеры, в которые можно внедриться, например, с отладкой (даже без изменения кода). А открывать их на общий доступ — всё равно будет где-то проскальзывать, что не заметили, что лезут в приватное поле, несмотря на суффикс.
Вариант с раздельными префиксами для public/protected/private полей я постоянно "испытываю" в Python, такое же происходит в Go. IMO, недостатков больше, чем достоинств.
N>Для контролируемого обращения к приватным полям надо использовать сеттеры и геттеры, в которые можно внедриться, например, с отладкой (даже без изменения кода). А открывать их на общий доступ — всё равно будет где-то проскальзывать, что не заметили, что лезут в приватное поле, несмотря на суффикс.
Мне довольно часто хочется не слишком тормозить программу обращение к геттеру/сеттеру, если можно прочитать/записать поле напрямую.
И бывают ситуации, когда например в геттере выполняется некий вспомогательный код, и нужно именно обойтись без него.
N>Вариант с раздельными префиксами для public/protected/private полей я постоянно "испытываю" в Python, такое же происходит в Go. IMO, недостатков больше, чем достоинств.
А какие конкретно недостатки?
"Ты должен сделать добро из зла, потому что его больше не из чего сделать". АБ Стругацкие.