Во многих современных языках есть возможность создавать для объектов свойства: публичные поля, которые с точки зрения пользователя выглядят и работают как простые переменные, но к которым в объекте можно привязать обработчики на чтение и запись значений (getter'ы и setter'ы).
Отсюда следует, что свойство, у которого есть только доступ на чтение, в работе ничем не отличается от просто функции без параметров:
|
|
Во многих языках будет, правда, отличие в том, что функция вызывается со скобочками, но это не существенно.
Так вот меня всегда мучил этот выбор. Есть какое-то значение в объекте, которое надо выставить наверх, а чем - функцией или свойством? Выбирать с помощью подбрасывания монетки такие вещи как-то неверно, поэтому я подумал и придумал себе правило.
Я рассудил, что суть самих понятий свойства и функции различается:
-
Функция - это значение, которое может измениться, в общем-то, в любой момент в зависимости от каких-то неявных изменений в системе. Например, объект, который периодически проверяет наличие обновлений программы может отдавать признак "есть/нет" публичной функцией.
-
Свойство же отражает состояние самого объекта и поэтому без явных на него воздействий изменяться не должно. Собственно, количество элементов списка - хороший пример (хотя в Delphi TList.Count - это функция, что меня очень удивило).
Если следовать этому правилу, то тогда пользователь класса вправе расчитывать на то, что значение свойства он может сохранить во внутренней переменной и использовать ее повторно, не боясь, что она станет неактуальной. А про функцию он будет знать, что ее надо вызывать каждый раз заново.
А теперь вопрос к аудитории. Не напридумывал ли я чего странного? Как вы решаете для себя такой вопрос? И нет ли где каких-нибудь опубликованных мыслей на эту тему, мне что-то не попадались...
Комментарии: 5
Не возникал такой вопрос ;) Execute - не станет свойством (прикольно было бы увидеть такое свойство в Object Inspector'е), ID не станет функцией.
В случае с Count - это просто "посчитать", а не "количество". Хотя эта функция может тупо возвращать FCount. Вопрос в инкапсуляции и логике, уже заложенной в иерархию классов с их методами и свойствами. Все уже написано, надо просто следовать этой логике. Иначе самому потом будет сложнее разбираться...
Не знаю как где, но в дот нете у свойств могут быть параметры. называется индексеры.
Можно написать что-то вроде:
DataTable table = dataSet.Tables["Customers"];
DataRow firstRow = table.Rows[0];
Также вполне позволительно написать:
DataTable table = dataSet.Tables[1]; (т.е. несколько индексеров в зависимости от параметров)
В некоторых местах библиотеки используются двух размерные индексеры, записываются через запятую - эти все навороты ещё добавляет каши в вопрос о том, когда делать функцию, а когда свойство.
Более того, я где-то в блогах майкрософтовских разработчиков встречал обсуждение, будет ли класс My. Типа новые программисты не знают, что такое файловая система, зато знают что такое Мой Компьютер.
Теоретически без проблем и сейчас написать класс который будет нормально отрабатывать что-то типа:
Console.WriteLine( My.Files["C:\config.sys", OpenMode.ReadOnly].ReadToEnd());
который распечатает конфиг сис. (Риад онли это я с потолка взял показать синтаксис, его может и не быть)
Так что боюсь что все правила придуманные на тему выборв свойста или функции могут быстро устареть. Я в этом деле полагаюсь чисто на интуицию :(, политики не выработал.
Иногда мне кажется что нужно разбивать функции и свойста по цене. Тогда получится Count у массива - свойсто, а у связного списка - функция но назвал бы я её GetCount().
В Delphi тоже есть индексированные свойства. Я о них забыл упомянуть, но и для них я тоже пользуюсь тем же самым правилом: если при одном и том же состоянии объекта в зависимости от параметра возвращается одно и то же значение - это свойство, если могут быть разные - функция.
Еще для этого случая есть и доп. условие. Вызов вида Object.Property[Index] должен "работать" как обращение в массив - то есть, всегда удаваться. А вот если оно в зависимости от каких-то условий может ругнуться Exception'ом, то тут скорее предпочту просто функцию с параметром. Потому что от скобочек ожидается поведение "посмотреть в ячейку и взять значение", а от функции - "выполнить какую-то счетную работу и вернуть результат".
Вот индексные свойства с не Integer счетчиками для меня действителоьно загадка. Например, с Items, Fields, FieldDefs все понятно - фактически связанный список объектов, со сходной семантикой. Что я получу по My.Files[”C:\config.sys”, OpenMode.ReadOnly] понять сложно, итератора нет, я теряюсь.
По большому счету действительно весь вопрос в скобках. Квадратные или круглые. Что примечательно при работе с Excel'ем через COM все эти Range["A1:B2"] превращаются в обычные функции, и уже не знаешь, что имели в виду MS Range - это индексное свойство или функция с другими скобками. Поэтому надязыковый подход скорее всего не годится.
Когда я пишу на Дельфи интерфейсы классов, то представляю, что создаю свою библиотеку, которую может понадобиться использовать другому программисту не сильно вникая в ее "внутренности".
В свойства автоматически уходят:
- все внутренние переменные, которые по какой-то причине нужно высунуть наружу. Иногда это просто красивость, что-то вроде
property Count: integer read fElementsCount write fElementsCount;
. Редко, но бывает, что нужно поле отдать на растерзание, причем с полным доступом.- все, что требует сеттера (это очевидно и тут вроде не обсуждается).
- все, что требует геттера, но логично выглядит как элемент класса. Вот это, наверное, основное. Допустим, у меня есть некий класс
TUsersBase
, имеющий три массива записей пользователей. Ну, допустим, они там раскиданы по каким-то правам доступа. Пример искусственный, но для иллюстрации сойдет. Так вот, человеку со стороны должно быть неважно, как эти пользователи в этом классе хранятся. Для него снаружи интересно количество пользователей. Тогда я сделаюCount
именно property, хотя для него и напишу геттерGetCount;
То есть get-property для меня - некая ячейка, хранящая данные. При взгляде снаружи. Пожалуй да, значение свойства не должно меняться при отсутствии вызовов методов класса.