Глава 10 - Объекты
REBOL/Core Руководство пользователя Основное содержание Send Us Feedback
Содержание:
1. Обзор
2. Создание объектов
3. Клонирование объектов
4. Доступ к объектам
5. Функции объекта
6. Прототип объектов
7. Обращение к себе
8. Инкапсуляция
9. Отражающие свойства
1. Обзор
Объекты группируют значения в общий контекст. Объект может включать в себя скалярные значения, серии, функции и другие объекты. Объекты полезны при работе со сложными структурами, поскольку они позволяют инкапсулировать связанные данные и код и передавать их функциям как одно значение.
2. Создание объектов
Новые объекты создаются с помощью функции make. Функция make требует два аргумента и возвращает новый объект. Формат функции make:
new-object: make parent-object new-values
Первый аргумент, parent-object, - это родительский объект, из которого создаётся новый объект. Если родительский объект недоступен, как при определении начального объекта, используйте тип данных object!, как показано ниже:
new-object: make object! new-values
Второй аргумент, new-values, представляет собой блок, который определяет дополнительные переменные и начальные значения для нового объекта. Каждая переменная, которая определена в блоке, является переменной экземпляра объекта. Например, если блок содержит два определения переменных, то они будут переменными объекта:
example: make object! [
var1: 10
var2: 20
]
У объекта example есть две переменные, которые содержат два целых числа.
Блок оценивается, поэтому он может включать любой тип выражения для вычисления значений переменных:
example: make object! [
var1: 10
var2: var1 + 10
var3: now/time
]
После того, как объект создан, он может служить прототипом для создания новых объектов:
example2: make example []
В приведённом выше примере создаётся второй экземпляр объекта example. Новый объект является клоном первого объекта. В блоке задаются новые значения для второго объекта:
example2: make example [
var1: 30
var2: var1 + 10
]
В приведённом выше примере объект example2 имеет другие значения, чем исходный объект example для двух своих переменных.
Объект example2 также может расширять определение объекта, добавляя к нему новые переменные:
example2: make example [
var4: now/date
var5: "пример"
]
В результате получается объект с пятью переменными: три исходные из исходного объекта example и две новые.
Процесс расширения определения объекта можно повторять любое количество раз.
Вы также можете создать объект, содержащий переменные, которые инициализируются некоторым общим значением. Это можно сделать с помощью каскадного набора определений слов:
example3: make object! [
var1: var2: var3: var4: none
]
В приведённом выше примере для четырёх переменных в новом объекте установлено значение none.
Подводя итог, процесс создания объекта включает следующие шаги:
- Использовать make для создания нового объекта на основе родительского объекта или object! тип данных.
- Добавьте к новому объекту любые новые переменные, которые определены в блоке.
- Оцените блок, в результате чего переменные, определённые в блоке, будут установлены в значения в новом объекте.
- В результате возвращается новый объект.
3. Клонирование объектов
Когда вы используете родительский объект для создания нового объекта, родительский объект клонируется, а не наследуется. Это означает, что если родительский объект изменяется, это не влияет на дочерний объект.
В качестве примера следующий код создаёт объект банковского счета, переменные которого пусты:
bank-account: make object! [
first-name:
last-name:
account:
balance: none
]
Чтобы использовать новый объект, можно указать значения для создания учётной записи для клиента:
luke: make bank-account [
first-name: "Luke"
last-name: "Lakeswimmer"
account: 89431
balance: $1204.52
]
Поскольку новые учётные записи создаются на регулярной основе, для их создания полезно использовать функцию и некоторые глобальные переменные:
last-account: 89431
bank-bonus: $10.00
make-account: func [
"Возвращает новый объект учётной записи"
f-name [string!] "Имя"
l-name [string!] "Фамилия"
start-balance [money!] "Начальный баланс"
][
last-account: last-account + 1
make bank-account [
first-name: f-name
last-name: l-name
account: last-account
balance: start-balance + bank-bonus
]
]
Теперь новый объект учётной записи для Fred (Фреда) потребует только:
fred: make-account "Fred" "Smith" $500.00
4. Доступ к объектам
Доступ к переменным внутри объектов осуществляется с помощью путей. Путь состоит из имени объекта, за которым следует имя переменной. Например, следующий код обращается к переменным в объекте example:
example/var1
example/var2
Вот примеры использования объекта bank-account:
print luke/last-name
Lakeswimmer
print fred/balance
$510.00
Используя путь, можно также изменить переменные объекта:
fred/balance: $1000.00
print fred/balance
$1000.00
Вы можете использовать функцию in для доступа к объектным переменным, извлекая их слова из их объектного контекста:
print in fred 'balance
balance
Слово balance возвращается имеет объект fred в качестве контекста. Вы можете получить значение, которое он содержит, используя get::
print get in fred 'balance
$1000.00
Второй аргумент функции in - буквальное слово. Это позволяет вам динамически менять слова в зависимости от того, что вам нужно:
words: [first-name last-name balance]
foreach word words [print get in fred word]
FredSmith
$1000.00
Каждое слово в блоке используется для получения своего значения в объекте.
В функции in также могут быть использованы для установки переменных объекта.
set in fred 'balance $20.00
print fred/balance
$20.00
Если слово не определено в объекте, функция in возвращает none. Это полезно для определения наличия переменной в объекте.
if get in fred 'bank [print fred/bank]
5. Функции объекта
Объект может содержать переменные, которые относятся к функциям, которые определены в контексте объекта. Это полезно, потому что функции инкапсулированы в контексте объекта и могут обращаться к другим переменным объекта напрямую, без необходимости указывать путь.
В качестве простого примера объект example может включать в себя функции для вычисления новых значений внутри объекта:
example: make object! [
var1: 10
var2: var1 + 10
var3: now/time
set-time: does [var3: now/time]
calculate: func [value] [
var1: value
var2: value + 10
]
]
Обратите внимание, что в этом примере функции могут ссылаться на переменные объекта напрямую, а не как пути. Это возможно, потому что функции определены в том же контексте, что и переменные, к которым они обращаются.
Чтобы установить новое время, используйте:
example/set-time
В этом примере оценивается функция, которая устанавливает var3 на текущее время.
Чтобы вычислить новые значения для var1 и var2, используйте:
example/calculate 100
print example/var2
110
В случае объекта bank-account к текущему определению могут быть добавлены функции депозита (deposit) и снятия (withdraw) средств:
bank-account: make bank-account [
deposit: func [amount [money!]] [
balance: balance + amount
]
withdraw: func [amount [money!]] [
either negative? balance [
print ["Отказано. Перерасход "
absolute balance]
][balance: balance - amount]
]
]
Обратите внимание, что в этом примере функции могут ссылаться на balance (баланс) непосредственно внутри объекта. Это потому, что функции являются частью контекста объекта.
Теперь, если создаётся новый счёт, он будет содержать функции для ввода и вывода денег. Например:
lily: make-account "Lily" "Lakeswimmer" $1000
print lily/balance
$1010.00
lily/deposit $100
print lily/balance
$1110.00
lily/withdraw $2000
print lily/balance
-$890.00
lily/withdraw $2.10
Отказано. Перерасход $890.00
6. Прототип объектов
Любой объект может служить прототипом для создания новых объектов. Например, ранее определенный объект учетной записи lily можно использовать для создания новых объектов с такой строкой, как:
maya: make lily []
Это создаёт экземпляр объекта. Объект является копией объекта клиента и имеет идентичные значения:
print lily/balance
-$890.00
print maya/balance
-$890.00
Вы можете изменить новый объект при его создании, указав новые значения в блоке определения:
maya: make lily [
first-name: "Maya"
balance: $10000
]
print maya/balance
$10000.00
maya/deposit $500
print maya/balance
$10500.00
print maya/first-name
Maya
Объект lily служит прототипом для создания нового объекта. Любые слова, которые не были переопределены для нового объекта, продолжают иметь значения старого объекта:
print maya/last-name
Lakeswimmer
Аналогичным образом к объекту добавляются новые слова:
maya: make lily [
email: maya@example.com
birthdate: 4-July-1977
]
7. Обращение к себе
Каждый объект включает предопределённую переменную под названием self. В контексте объекта переменная self относится к самому объекту. Его можно использовать для передачи объекта другим функциям или для возврата его в результате выполнения функции.
В следующем примере функции how-date требуется объект в качестве аргумента, и ей передаётся self:
show-date: func [obj] [print obj/date]
example: make object! [
date: now
show: does [show-date self]
]
example/show
16-Jul-2000/11:08:37-7:00
Другой пример использования переменной В следующем примере функции how-date требуется объект в качестве аргумента, и ей передаётся self: - это функция, которая клонирует себя:
person: make object! [
name: days-old: none
new: func [name' birthday] [
make self [
name: name'
days-old: now/date - birthday
]
]
]
lulu: person/new "Lulu Ulu" 17-May-1980
print lulu/days-old
7366
8. Инкапсуляция
Объект предоставляет хороший способ инкапсулировать группу переменных, которые не должны появляться на глобальном уровне. Когда функциональные переменные определены как глобальные, они могут быть непреднамеренно изменены другими функциями.
Решение этой проблемы глобальных переменных состоит в том, чтобы обернуть объект как переменными, так и функцией. Когда это будет сделано, функция все ещё может получить доступ к переменным, но переменные не будут доступны глобально. Например:
Bank: make object! [
last-account: 89431
bank-bonus: $10.00
set 'make-account func [
"Возвращает новый объект учётной записи"
f-name [string!] "Имя"
l-name [string!] "Фамилия"
start-balance [money!] "Начальный баланс"
][
last-account: last-account + 1
make bank-account [
first-name: f-name
last-name: l-name
account: last-account
balance: start-balance + bank-bonus
]
]
]
В этом примере переменные защищены от случайного изменения. Обратите внимание, что функция make-account была установлена для переменной с помощью функции set, а не с использованием определения переменной. Это было сделано для того, чтобы сделать его глобальной функцией. Функцию можно использовать так же, как функции, заданные с помощью определения переменной, но для неё не требуется путь к объекту:
bob: make-account "Bob" "Baker" $4000
9. Отражающие свойства.
Как и в случае со многими другими типами данных REBOL, вы можете получить доступ к компонентам объектов таким образом, чтобы вы могли писать полезные инструменты и утилиты для их создания, мониторинга и отладки.
Функции first и second позволяет получить доступ к компонентам объекта. Функция first возвращает слова, определённые для объекта. Функция second возвращает значения, объекты, установленные в этих словах. На следующей диаграмме показана взаимосвязь между возвращаемыми значениями first и second:

Преимущество использования first заключается в том, что он позволяет вам получить список слов для функции, ничего не зная о функции:
probe first luke
[self first-name last-name account balance]
Обратите внимание, что в приведённом выше примере список содержит слово self, которое является ссылкой на сам объект. Вы можете исключить self при получении списка слов объекта, используя next:
probe next first luke
[first-name last-name account balance]
Теперь у вас есть способ написать функцию, которая может проверять содержимое объекта:
probe-object: func [object][
foreach word next first object [
print rejoin [word ":" tab get in object word]
]
]
probe-object fred
first-name: Luke
last-name: Lakeswimmer
account: 89431
balance: $1204.52
При доступе к объектам таким образом следует проявлять осторожность, чтобы избежать бесконечных циклов. Например, если вы попытаетесь исследовать определённые объекты, содержащие ссылки на самих себя, ваш код может начать бесконечный цикл. Это причина, по которой вы не можете напрямую исследовать системный объект system. Объект system содержит много ссылок на себя.
|