Язык программирования Перфолента.Net - Официальный сайт

 Язык программирования Перфолента.Net - Официальный сайт.

Поиск   
Главная :: О проекте :: Контакты :: Обратная связь :: Благодарности :: ВходГость

   >   >   >   > 



Конструируем класс. Свойства.

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


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

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

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

Отлично, это работает и решает все проблемы, кроме одной, то что логически выглядит как свойства (характеристики) объекта больше не доступно для изменения и даже скрыто от внешнего наблюдателя. Вместо свойств объект получает множество методов в лучшем случае начинающихся со слов Получить и Установить. Не красиво, не удобно, громоздко.

Для решения проблемы было предложено ввести новое понятие – Свойство, которое объединило достоинства обоих подходов. Свойство выглядит для внешнего наблюдателя и программиста как поле, но для получения и установки значения имеет соответствующие методы, в которых можно выполнить проверку значения и выполнить другие необходимые действия при его изменении.

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

 

Объявление свойства.

Свойство можно представить, как своеобразную «обёртку» над полем. Свойство имеет оба или только один из методов Получить/Установить, а также может иметь одно или несколько полей для хранения связанных с ним данных. Свойство может не иметь «своих» полей, а оказывать влияние на поля других свойств или на поля, логически не связанные со свойствами, но такое поведение встречается довольно редко.

В языке Перфолента для создания свойства существуют два разных синтаксиса – полный и краткий (авто). Полный позволяет контролировать все аспекты работы свойства, но содержит много кода по сравнению с полем. Краткий способ (авто) позволяет компилятору автоматически добавить скрытое поле для хранения значения и дописать код методов Получить/Установить, делая синтаксис определения свойства почти не отличимым от определения поля.

Что бы понять конструкцию свойства сначала рассмотрим полный синтаксис:

Класс Собака Родитель Животное
        &ВидноВсем
        Свойство Полное Имя тип Строка
                //скрытое поле для хранения значения свойства
                Поле _Имя тип Строка = "Без имени"
                //метод для получения значения свойства
                Получить
                            Возврат _Имя
                КонецПолучить
                //метод для установки значения свойства
                Установить(НовоеЗначение тип Строка)
                            _Имя=НовоеЗначение
                КонецУстановить
        КонецСвойства
КонецКласса

Умнику на заметку: ключевое слово Полное в этом примере является обязательным.

Как видите, по сравнению с полем кода стало значительно больше. Однако, у нас появилось два метода Получить и Установить, в которых мы можем делать любые проверки, например, корректно ли новое значение, или можем выполнить какие-то действия необходимые при изменении значения свойства.

Теперь посмотрим на краткий (авто) способ определения свойства:

Класс Собака Родитель Животное
        &ВидноВсем
        Свойство Авто Вес тип Целое = 0
КонецКласса

Почти не отличается от определения поля, правда? Особенно, если учесть, что ключевое слово Авто можно пропустить. Инициализацию свойства тоже можно пропустить. А вот ключевое слово Полное в предыдущем примере пропускать нельзя!

 

Множественное объявление свойтв одного типа.

Так же, как и для полей, краткий синтаксис объявления свойства позволяет определять несколько свойств одновременно.

Не забывайте при этом, что инициализирующее выражение вычисляется столько раз, сколько свойств инициализируется, например:

&ВидноВсем Свойства Фамилия, Имя, Отчество тип Строка = "<>" 

Умнику на заметку: Хотя для объявления нескольких свойств существует ключевое слово Свойства, использование для этой же цели ключевого слова Свойство не считается ошибкой.

 

А не лучше ли вместо свойств использовать поля?

Глядя на краткий (авто) способ определения свойства, у многих возникает вопрос: «А зачем вместо поля определять свойство, если ни каких дополнительных проверок при этом не выполняется, а данные всё равно хранятся в скрытом поле? Излишество!». И да и нет. Поле было бы экономичнее, т.к. отсутствует излишний вызов методов при получении и установке значения, однако, если в последствии вам понадобится проверка значения и вы решите изменить поле на свойство, вы сломаете работу ранее откомпилированных программ, ожидающих поле, а не свойство. Как вы понимаете, особенно это актуально для библиотек. Вам придется перекомпилировать абсолютно все программы, использующие библиотеку, если вы измените поле на свойство!

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

После всего сказанного хочется совсем отказаться от полей и перейти к использованию свойств. Однако, лучше размышлять над этим вопросом каждый раз, когда вам понадобилось хранить значение. Бывают случаи, когда совершенно ясно, что этому полю можно присвоить любое значение указанного типа и работа класса при этом нарушена быть не может. В этом случае использование свойства вместо поля действительно является излишеством. Бывают так же случаи, когда идет настолько жесткая борьба за производительность, что даже дополнительный вызов метода для получения значения свойства кажется затратным. Но, бывает и так, что корпоративные стандарты требуют использовать только свойства. Жизнь богата, сложна и разнообразна.

 

Связанное поле.

Обычно со свойством связано поле, которое хранит значение свойства. Это поле не является обязательным и в некоторых случаях его может не быть. Ещё один редкий случай, когда со свойством связано несколько полей.

В языке Перфолента поле, принадлежащее свойству, можно определять, как внутри определения свойства, так и снаружи, но внутри это смотрится естественней.

//скрытое поле расположено снаружи свойства
Поле _Имя тип Строка = "Без имени"
&ВидноВсем
Свойство Полное Имя тип Строка
        //…    тут должны быть методы Получить и Установить
КонецСвойства

&ВидноВсем
Свойство Полное Имя тип Строка
        //скрытое поле расположено внутри свойства
        Поле _Имя тип Строка = "Без имени"
        //…    тут должны быть методы Получить и Установить
КонецСвойства

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

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

Допустим, мы создали такое свойство:

&ВидноВсем Свойство Фамилия, Имя, Отчество тип Строка = "<>"

В этом случае компилятор автоматически создаст три скрытых поля:

Поле _Фамилия тип Строка = "<>"
Поле _Имя тип Строка = "<>"
Поле _Отчество тип Строка = "<>"

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

_Фамилия = "Иванов"

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

 

Методы Получить и Установить.

Свойство обязательно имеет один или оба метода Получить/Установить.

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

При обращении к свойству из кода программы, компилятор неявно вызывает методы Получить/Установить в тот момент, когда Вы хотите получить или установить значение свойства. Явно эти методы вызывать нельзя, т.к. их как бы не существует. С точки зрения вызывающего программиста свойства выглядят как поля.

Умнику на заметку: на самом деле, «под капотом Net» эти методы существуют, но имеют специальные имена get_ИмяСвойства и set_ИмяСвойства. При желании их можно вызвать по этим именам.

Собака2.set_Имя("Лайка")
ВыводСтроки Собака2.get_Имя

Рассмотрим пример установки значения свойства.

МояСобака.Имя = "Жучка"    //тут будет вызван метод Установить(НовоеЗначение), которому в качестве нового значения будет передано значение «Жучка»

А теперь пример получения значения свойства.

ВыводСтроки МояСобака.Имя    //тут будет вызван метод Получить(), который вернет значение «Жучка»

При кратком синтаксисе определения свойства компилятор сам создаёт необходимые методы Получить/Установить, но вызываться при получении или установке значения они будут точно так же, как и при полном определении. Для того, кто использует свойство, не важно, как оно было создано, кратким или полным синтаксисом.

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

        &ВидноВсем
        Свойство Полное РазмерФайла тип Целое
                Получить
                        //в свойстве ПолноеИмя хранится путь и имя текущего файла
                            Возврат ВычислитьРазмерФайла(ЭтотОбъект.ПолноеИмя)
                КонецПолучить
        КонецСвойства

В этом примере, возможно, вместо свойства можно было бы создать метод ПолучитьРазмерФайла(). Всегда, когда получение значения свойства должно производить вычисления, подумайте над тем, а не сделать ли вместо свойства метод.

В методе Установить Вы можете произвести любые проверки нового значения.

В том случае, когда новое значение выходит за диапазон допустимых значений, Вы можете выполнить такие действия:

- установить свойству значение по умолчанию;

- игнорировать новое значение;

- вызвать исключение;

&ВидноВсем
Свойство Полное ФИО тип Строка
        Поле _ФИО тип Строка = "<>"    //значение по умолчанию
        Установить(НовоеЗначение тип Строка)
                Если НовоеЗначение Это Неопределено
                        //игнорируем значение… не спрашивайте почему :)
                            Возврат
                ИначеЕсли НовоеЗначение=""
                        //используем значение по умолчанию
                        _ФИО ="<>"
                ИначеЕсли НЕ ПроверитьФИО(НовоеЗначение)
                        //вызываем исключение
                            ВызватьИсключение "Не верное ФИО!"
                Иначе
                        //все проверки пройдены – устанавливаем новое значение
                        _ФИО =НовоеЗначение
                КонецЕсли
        КонецУстановить
КонецСвойства

Это синтетический пример, «притянутый за уши», но он демонстрирует все три возможности действий, когда в метод Установить передано не допустимое значение.

 

Индексируемые свойства.

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

Рассмотрим случай, когда поле, связанное со свойством, является массивом:

&ВидноВсем
Свойство Полное ФИОРодителей(Индекс тип Целое) тип Строка
        //сразу создадим массив из 2-х элементов, т.к. у человека 2 родителя
        Поле _ФИОРодителей тип Строка[] = Новый Строка[1]   

        //метод Получить фактически будет иметь дополнительный параметр:
        // Получить(Индекс тип Целое)
        Получить    //дополнительный параметр здесь указывать не надо
                //индекс желательно проверить
                Если Индекс < 0 или Индекс > 1
                            ВызватьИсключение "Не допустимый индекс! Индекс должен быть в диапазоне от 0 до 1."
                КонецЕсли
                //возвращаем значение элемента массива по индексу
               Возврат _ФИОРодителей[Индекс]
        КонецПолучить

        //метод Установить фактически будет иметь дополнительный параметр:
        //Установить(Индекс тип Целое, НовоеЗначение тип Строка)
        Установить(НовоеЗначение тип Строка)    //дополнительный параметр здесь указывать не надо
                //индекс желательно проверить
                Если Индекс < 0 или Индекс > 1
                            ВызватьИсключение "Не допустимый индекс! Индекс должен быть в диапазоне от 0 до 1."
                КонецЕсли
                //устанавливаем значение элемента массива по индексу
                _ФИОРодителей[Индекс] = НовоеЗначение
        КонецУстановить
КонецСвойства

Теперь попробуем обратиться к этому свойству:

Человек.ФИОРодителей(0) = "Иванова Инна Ивановна"

Человек.ФИОРодителей(1) = "Петров Пётр Петрович"

ВыводСтроки Человек.ФИОРодителей(0)    //выводит «Иванова Инна Ивановна»

ВыводСтроки Человек.ФИОРодителей(1)    //выводит «Петров Пётр Петрович»

Обратите внимание, что мы используем круглые скобки при обращении к индексированному свойству, т.к. фактически мы передаем параметры методам Получить и Установить.

 

Специальные индексируемые свойства.

В языке Перфолента.Net зарезервированы имена специальных индексированных свойств Item и Элемент, которые являются синонимами и позволяют организовать доступ к данным по индексу или идентификатору с помощью индексаторов или прямого обращения через точку.

Эти свойства должны иметь один параметр и специальное имя, но в остальном это обычные индексированные свойства.

Рассмотрим пример класса с указанными свойствами и обращениями к ним из кода.

&ВидноВсем
Класс КлассСоСвойствомЭлемент
    //это свойство вернёт имя по индексу
    &ВидноВсем
    Свойство Полное Элемент(Индекс тип Целое) тип Строка
        Поле МасЦел тип Строка[] = {"Вася","Петя","Ира"}
        Получить
            Возврат МасЦел[Индекс]
        КонецПолучить
        Установить(Зн тип Строка)
            МасЦел[Индекс] = Зн
        КонецУстановить
    КонецСвойства
    //это свойство вернёт номер месяца по его имени
    &ВидноВсем
    Свойство Полное Элемент(Имя тип Строка) тип Целое
        Поле МасМесяцы тип Строка[] = {"","Янв","Фев","Мар","Апр","Май","Июн","Июл","Авг","Сен","Окт","Ноя","Дек"}
        Получить
            Для Ном = 1 По 12 Цикл
                Возврат Ном Если Лев(Имя,3) = МасМесяцы[Ном]
            КонецЦикла
            Возврат 0
        КонецПолучить
    КонецСвойства
КонецКласса

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

СпецКласс = Новый КлассСоСвойствомЭлемент
//обычные обращения к свойству
ВыводСтроки СпецКласс.Элемент(2) //вернёт Ира
ВыводСтроки СпецКласс.Элемент("Август") //вернёт 8
//специальные обращения поддержанные компилятором
ВыводСтроки СпецКласс[1] //вернёт Петя
ВыводСтроки СпецКласс["Январь"] //вернёт 1
ВыводСтроки СпецКласс.Март //вернёт 3

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

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

 

Ограничение доступа на чтение и запись.

Иногда необходимо ограничить доступ к свойству на чтение или на запись. Например, если свойство содержит значение однозначно определяющее объект, такое как идентификатор, то менять его на другое нельзя, что требует ограничить возможность записи свойства. Гораздо реже встречается необходимость запретить чтение значения свойства, но и это бывает, например, пароль не стоит демонстрировать всем.

В языке Перфолента для ограничения доступа на чтение и запись используются соответствующие атрибуты ТолькоЧтение и ТолькоЗапись, например:

&ВидноВсем, ТолькоЧтение Свойство Идентификатор тип Целое = ++СчетчикОбъектов 

&ВидноВсем, ТолькоЗапись Свойство Пароль тип Строка 

Для краткого синтаксиса определения свойства атрибуты являются единственным способом установить ограничение на чтение или на запись, а в полном варианте синтаксиса для тех же целей можно определить только один метод, либо метод Получить, либо метод Установить. В полном синтаксисе атрибуты ТолькоЧтение и ТолькоЗапись не обязательны и являются всего лишь способом контроля соответствующего набора методов. Если установлен атрибут ТолькоЧтение, то наличие метода Установить будет ошибкой, аналогично, при установленном атрибуте ТолькоЗапись наличие метода Получить будет ошибкой.   

Посмотрим на определение свойства с доступом только для чтения:

&ВидноВсем
Свойство Полное Идентификатор тип Целое
        Поле _Идентификатор тип Целое = ++СчетчикОбъектов 
        Получить
                    Возврат _Идентификатор
        КонецПолучить
КонецСвойства

Теперь определение свойства доступного только для записи:

&ВидноВсем
Свойство Полное Пароль тип Строка
        Поле _Пароль тип Строка = ""
        Установить(НовоеЗначение тип Строка)
                    _Пароль =НовоеЗначение
        КонецУстановить
КонецСвойства

Понятно, что наличие того или иного метода определяет вид доступа на чтение или на запись.

Атрибуты ТолькоЧтение и ТолькоЗапись не совместимы и не могут присутствовать одновременно.

Важно! Есть только два способа записать значение в свойство имеющее атрибут ТолькоЧтение. Первая – это инициализация свойства или поля связанного со свойством, вторая – установка значения в конструкторе. 

Умнику на заметку: На самом деле, инициализация полей, связанных со свойствами, в любом случае происходит в конструкторе. Компилятор автоматически создаёт и размещает код инициализации в начале каждого имеющегося конструктора или в конструкторе по умолчанию, который компилятор создаёт, когда конструкторы в коде класса отсутствуют.

Создавать свойства доступные только для записи не рекомендуется, в таких случаях лучше использовать методы. Окончательное решение, что использовать, принимать Вам. Например, если у объекта есть свойство ИмяПользователя, то логично иметь и свойство Пароль, а не метод УстановитьПароль. Однако, Вы можете вместо свойств сделать единственный метод УстановитьИмяПользователяИПароль.

 

Свойства общие для класса.

Как и поля, свойства могут быть общими для класса. Что бы сделать свойство общим для класса, достаточно добавить атрибут ОбщийДляКласса. Всё, что было написано об общих для класса полях в статье «Конструируем класс. Поля.» относится и к свойствам.

&ВидноВсем, ОбщийДляКласса Свойство КоличествоОбъектов тип Целое = 0 

 

Свойства в интерфейсах.

В интерфейсах возможен только краткий синтаксис определения свойств. Инициализация значений свойств в интерфейсах не возможна.

 

Свойства в структурах.

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

Структура МояСтруктура
            &ВидноВсем, ОбщийДляКласса Свойство ОбщееСвойство тип Целое = 111     // Правильно. Инициализация допустима.
            &ВидноВсем Свойство ПриватноеПоле1 тип Целое     // Правильно. Нет инициализации.
            &ВидноВсем Свойство ПриватноеПоле2 тип Целое = 222     // ОШИБКА !!! Инициализация НЕ допустима.
КонецСтруктуры

 

Свойства в модулях.

Как мы уже знаем, Модуль — это специализированная версия класса, экземпляры объектов которого создавать нельзя, а все члены являются общими для класса.

Поэтому свойствам, объявленным в модулях (в том числе в модуле Программа), нет необходимости задавать атрибут ОбщийДляКласса, они и так уже общие.

Программа МояПрограмма
            Свойство ВремяСтартаПрограммы тип Дата = ТекущаяДата
КонецПрограммы

Модуль ОбщиеДанные
            &ВидноВсем Свойство ОбщаяСтоимость тип Число = 0
КонецМодуля

 

Свойства в перечислениях.

В перечислениях свойства определять нельзя.

 

Вывод: Свойства заменяют собой поля в тех случаях, когда нужен контроль за получением и установкой значений.




К началу статьи


Предыдущая статья:
Конструируем класс. Конструкторы.

Следующая статья:
Конструируем класс. Завершители и Деструкторы.

Вернуться в раздел:
Объектно ориентированное программирование (ООП) на языке Перфолента
  Поддержи проект!

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

  Новости:
      04.01.2025 Опубликован новый релиз дистрибутива языка программирования Перфолента.Net версии 0.4.15.0_CE
      23.09.2024 Опубликована новая статья: "Конструируем класс. Делегаты."
      30.08.2024 Опубликован новый релиз дистрибутива языка программирования Перфолента.Net версии 0.4.14.0
      24.05.2024 Обновлён справочный раздел сайта
      01.07.2023 Новая версия 0.4.13.0 языка программирования Перфолента.Net
       Все новости