Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Почтовая рассылка

Подписчиков: -1
Последний выпуск: 19.06.2015

Плюсы и минусы использования свойств

309
15 ноября 2006 года
el scorpio
1.1K / / 19.09.2006
В одной (и не только) теме возникала мысль о том, что __property - вещь вредная, как противоречащая парадигме С++. Более того, звучали советы отбросить её как (всё зависит от фантазии читающих :) )

Вот скажите, люди, как правильнее (с точки зрения "парадигмы") писать
 
Код:
Что_то_там = Object.GetValue();
//вместо
Что_то_там = Object.Value;

Но против следующего аргумента возразить будет сложно. Какой код будет "читабельней" и понятней:
 
Код:
Object.Value = Очень_Длинная_Формула_Для_Вычисления_Требуемого_Значения;
// или же
Object.SetValue (постараемся_не_запутаться_в_скобках);

Кроме того, __property прозволяет "скрывать реализацию" чтения/записи значения, что куда больше соотвествует парадигме самого ООП.

Хотя недостатки у __property есть, вот примеры
1. изменение читаемого значения
Код:
class TMyClass
{
private:
TMyStruct fField;

virtual void SetField (const TMyStruct &Value);
public:
__property TMyStruct Property = {read = fField, write = SetField};
};
//.....
MyObject.Property.My_Struct_Field = Другое_Значение
// Значение изменится, но без вызова метода SetField :(
// Исправляется использованием метода чтения,
// возвращающего значение либо константную ссылку.
256
15 ноября 2006 года
foxweb
1.0K / / 27.07.2005
Очень интересный вопрос, сам тоже постоянно думаю на эту тему.
Лично я в своих классах явно использую SetValue(zzz) или GetValue(), хотя и оставляю неявный доступ непосредственно к полям класса.
8.4K
15 ноября 2006 года
Dian
91 / / 18.02.2006
С точки зрения С++ может быть все что угодно, а вот с точки зрения ООП и объектной идеологии свойства более правильны
547
16 ноября 2006 года
Hydra
488 / / 20.06.2006
__property, конечно снижает переносимость кода, но штука удобная.
К плюсам можно отнести возможность создания свойств только для чтения
240
16 ноября 2006 года
aks
2.5K / / 14.07.2006
el scorpio
Цитата:

Но против следующего аргумента возразить будет сложно. Какой код будет "читабельней" и понятней


Ну во первых код с __propery вносит путаницу и неоднозначность. При чтении кода не понятно, что происходит - вызов методов или же прямое присваивание публичной переменной.
А во вторых это элементарно делает код зависмым и не соответствующим языку. А ведь я так думаю на Borland C++ Builder не только окошки рисуются, да в окошках что то должно происходить. А вот бизнес логику можно по возможности и универсальной делать, если это явно не сопряженно с какими то трудностями.

547
16 ноября 2006 года
Hydra
488 / / 20.06.2006
Цитата:

А вот бизнес логику можно по возможности и универсальной делать, если это явно не сопряженно с какими то трудностями.


С этим, конечно, трудно поспорить.
Вобще __property задумывался для ObjectInspector'а, т.е. для создания VCL компонент программистом - чтобы кинул на форму свою поделку - а свойства отобразились где надо.

20K
16 ноября 2006 года
kossak
20 / / 10.11.2006
Согласен с GYDRA
309
16 ноября 2006 года
el scorpio
1.1K / / 19.09.2006
Цитата:
код с __propery вносит путаницу и неоднозначность. При чтении кода не понятно, что происходит - вызов методов или же прямое присваивание публичной переменной.


Резонное замечание, порою использование свойства внутри цикла, который не изменяет его значения, может привести к большим расходам, если свойство будет объявлено "вычисляемым".
По-этому, я часто использую следующий код

 
Код:
for (int i = 0, Count = MyClass.Count; i < Count; i++)
    // Код с использованием MyClass.Items ;
    // без изменения кол-ва элементов

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

Цитата:
это элементарно делает код зависмым и не соответствующим языку.


Вообще-то "универсальность" и "переносимость" кода редко когда бывает обязательным критерием. Тем более, что когда вовсю используются классы и функции "диалекта" (в данном случае - VCL), "переносить" что либо всяко не получится.

P.S. А вот является ли "__closure" стандартным средством С++?

240
16 ноября 2006 года
aks
2.5K / / 14.07.2006
[QUOTE=el scorpio]
Тем более, что когда вовсю используются классы и функции "диалекта" (в данном случае - VCL), "переносить" что либо всяко не получится.
[/QUOTE]
При чем тут диалекты. Ведь VCL - это в первую очередь оконная библиотека ну и с набором некоторых дополнительных фич. При чем тут сам язык. Повторюсь ведь Borland C++ Builder, не только для рисования графических элементов исспользуется так? А графические элементы без смыслового наполнения тоже не нужны.
309
16 ноября 2006 года
el scorpio
1.1K / / 19.09.2006
[QUOTE=aks]При чем тут диалекты. Ведь VCL - это в первую очередь оконная библиотека ну и с набором некоторых дополнительных фич. При чем тут сам язык. Повторюсь ведь Borland C++ Builder, не только для рисования графических элементов исспользуется так? А графические элементы без смыслового наполнения тоже не нужны.[/QUOTE]
В своих программах я использую множество действий, не связанных непосредственно с интерфейсом, при этом:
1. Для работы со строками я использую класс AnsiString, и ещё не встречал "объективных" причин пользовать что-либо другое.
2. Порой мне требуется проверка параметров функций/методов и генерация исключительных ситуаций при некорректных значениях. При этом я, опять таки, генерирую исключения с типами, производными от Exception - удобные они: и обработчик по типу выбрать можно, и текст сообщения выдают, и значения неверных параметров подставить могут, и даже номер страницы справочного файла приложат - красота.
3. Я создаю множество объектов, и порою мне нужно придавать им весьма специфичные особенности, например, двухэтапное создание/удаление - но иных способов, кроме как переопределение виртуальных методов AfterConstruction и BeforeDestruction класса TObject, я не встречал :(. И здесь на этот вопрос ответа тоже не дали.
4. Порою, для облегчения взаимодействия "рабочей" части программы с "интерфейсной", мне требуется использовать "стандартные" классы VCL, например TStrings.
4. Даже в "неинтерфейсной" части программы может потребоваться доступ к данным самого приложения, а то и управление им. Специально для этого, существует глобальный объект Application.

Уф, пока достаточно... Заговорился я что-то :D
3
16 ноября 2006 года
Green
4.8K / / 20.01.2000
[QUOTE=el scorpio]
1. Для работы со строками я использую класс AnsiString, и ещё не встречал "объективных" причин пользовать что-либо другое.
[/QUOTE]
Просто, ты не используешь или очень огранниченно используешь в своих программах другие объекты предоставляемые языком C++ и в частности STL, а так же видимо не используешь такую "родственную" языку библиотеку, как boost.
Кроме того ты, видимо, не используешь множественное наследование.

[QUOTE=el scorpio]
3. Я создаю множество объектов, и порою мне нужно придавать им весьма специфичные особенности, например, двухэтапное создание/удаление - но иных способов, кроме как переопределение виртуальных методов AfterConstruction и BeforeDestruction класса TObject, я не встречал :(. И здесь на этот вопрос ответа тоже не дали.
[/QUOTE]
Видимо, не видел этого вопроса, т.к. смог бы дать ответ.

[QUOTE=el scorpio]
4. Порою, для облегчения взаимодействия "рабочей" части программы с "интерфейсной", мне требуется использовать "стандартные" классы VCL, например TStrings.
[/QUOTE]
Это просто привычка, возможно, вредная.

[QUOTE=el scorpio]
4. Даже в "неинтерфейсной" части программы может потребоваться доступ к данным самого приложения, а то и управление им. Специально для этого, существует глобальный объект Application.
[/QUOTE]
А вот это однозначно вредная привычка.

Что же касается вопроса топика, то в языке C++ есть множество своих способов предоставления и определения доступа к полям данных:
- прямое обращение,
- через специальные методы,
- по средством переопределенных операторов (обычно operator=, operator-> и т.п.).
Вопрос: зачем применять ещё что-то нестандартное?
Это усложняет понимание кода. Когда я вижу запись

Object.Value = value;

я предполагаю, что value - это поле определенного типа, а значит я могу рассматривать всю запись в контексте именно типа Value, не обращая внимания на тип Object. Видя такое выражение я отметаю Object, т.к. он лишь определяет с каким именно экземпляром типа Value я работаю, и далее изучаю запись лишь в терминах Value.
Другими словами, такой код мысленно преобразуется в код вида:

ValueType& Value = Object.Value;
Value = value;


Причем это может быть не просто мысленное разделение, но и в полне реальное, например, при рефакторинге или оптимизации кода.
Понятно, что при использовании __property разделение, как в уме, так и в реальности, более сложное, т.к. вводится дополнительная косвенность: тип Value становится зависимым от типа Object. Т.о. запись Object.Value = value; оперирует не объектом Value, как это кажется на первый взгляд, а объектом Object. Иными словами, я не могу сказать по одной этой записи, на что повлияет такое присвоение.

Кроме субъективных сложностей, возникают и объективные. Две я уже указал - рефакторинг и оптимизация. Другие, возможно, проявляются при наследовании, например, при переопределении способа доступа.
309
17 ноября 2006 года
el scorpio
1.1K / / 19.09.2006
[QUOTE=Green]Просто, ты не используешь или очень огранниченно используешь в своих программах другие объекты предоставляемые языком C++ и в частности STL, а так же видимо не используешь такую "родственную" языку библиотеку, как boost.
Кроме того ты, видимо, не используешь множественное наследование.
[/quote]
Касаемо AnsiString - дело в том, сколько у него есть удобных методов по обработке строки, а также внешних функций, и в том, что с точки зрения производительности, куда лучше использовать его, чем заниматься бесконечной конвертацией в string и обратно при активаной работе с интерфейсом.

Цитата:
Видимо, не видел этого вопроса, т.к. смог бы дать ответ.


Давняя тема, сейчас находится на последней странице раздела Builder - если не удалилась за "ненадобностью".
А за хороший ответ я бы искреннее "спасибо" сказал. Хоть и понимаю, что он не булькает :)

Цитата:
Это просто привычка, возможно, вредная.


StringList - класс тоже довольно "универсальный", как в плане интерфейса, так и в плане алгоритмической части задачи. И кроме него, есть ещё много других классов. Например, для работы с ini или реестром, врядли STL реализует классы для таких действий, а использование API-функций куда меньше подходит под концепцию ООП.
Кроме того, уж если я пишу программу для БД, то там "переносимость" явно отдыхает.


Цитата:
Что же касается вопроса топика, то в языке C++ есть множество своих способов предоставления и определения доступа к полям данных:
- прямое обращение,
- через специальные методы,
- по средством переопределенных операторов (обычно operator=, operator-> и т.п.).


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

Цитата:

Object.Value = value;

я предполагаю, что value - это поле определенного типа, а значит я могу рассматривать всю запись в контексте именно типа Value, не обращая внимания на тип Object. Видя такое выражение я отметаю Object, т.к. он лишь определяет с каким именно экземпляром типа Value я работаю, и далее изучаю запись лишь в терминах Value.
Другими словами, такой код мысленно преобразуется в код вида:

ValueType& Value = Object.Value;
Value = value;


Причем это может быть не просто мысленное разделение, но и в полне реальное, например, при рефакторинге или оптимизации кода.
Понятно, что при использовании __property разделение, как в уме, так и в реальности, более сложное, т.к. вводится дополнительная косвенность: тип Value становится зависимым от типа Object. Т.о. запись Object.Value = value; оперирует не объектом Value, как это кажется на первый взгляд, а объектом Object. Иными словами, я не могу сказать по одной этой записи, на что повлияет такое присвоение.



Собственно говоря, имя свойства (как и медота) всегда должно соответствовать его роли в классе. И по имени зачастую вполне можно понять, что оно делает. Например, для массива/списка Count будет соответствать кол-ву элементов, а Items - указанному элементу. Глвное, что можно читать/писать значения. А уж как работают эти свойства - остальную часть программы интересовать не должно, ибо "инкапсуляция".

Правда, есть ещё два аспекта, на которые следует обратить внимание программистам Builder.
1. Если запись значения открытого свойства выполняется прямым обращением к закрытому полю, то следующий код будет работоспособным, а вот если вдруг программист вместо имени поля укажет метод записи, то будет ошибка компиляции

 
Код:
// Последовательное присваивание значения свойства
Obj1.Value = Obj2.Value = Obj3.Value = MyValue;


2. Если чтение открытого поля сложного типа выполняется прямым обращением к закрытому полю, то следующий код произведёт изменение поля без вызова метода записи поля.
 
Код:
// Поле изменится, но вызов SetValue не произойдёт
Obj.Value.ValueField = xyz;

Хотя это вполне законно используется, когда поле является указателем на дочерний объект класса - изменить указатель нельзя, а вот указываемый объект - вполне можно.

3. Если чтение открытого поля сложного типа выполняется методом, возвращающим константную ссылку/, то п2 не получится - вообще, изменить будет нельзя.

4. Если чтение открытого поля (любого типа) выполняется методом, возвращающим значение данного типа, то при изменении полученного значения, автоматически будет вызван метод записи свойства. Тоже самое будет сделано при изменении значения простого типа, полученного прямым обращением к закрытому полю
 
Код:
// Следующее действие

objArray.Count++; // увеличение кол-ва элементов массива
objArray.Item [0].Property = MyValue;
// Эквивалентно
int tmp = objArray.fCount; //Чтение закрытого поля
tmp ++; // изменение значения
objArray.SetCount (tmp); // вызов функции изменения свойства объекта
TItem tmp2 = objArray.GetItem (0);
tmp2.Property = MyValue;
objArray.SetValue (tmp2);
[/code]
Хорошо всё это или плохо - решайте сами.
Хотя мне п2 порой приносит изрядные сложности :(, вынуждая прибегать к п3, зато п4 при правильном применении - хорошая вещь.

Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог