Ремонт принтеров, сканнеров, факсов и остальной офисной техники


назад Оглавление вперед




[67]

Глава 3

Модульность, объекты и состояние

Metap&XXov (Ьотаиетса

(Изменяясь, оно остается неподвижным)

Гераклит

Plus §a change, plus cest la meme chose

Альфонс Карр

В предыдущих главах мы ввели основные элементы, из которых строятся программы. Мы видели, как элементарные процедуры и элементарные данные, сочетаясь, образуют составные сущности; мы стали понимать, что без абстракции нельзя справиться со сложностью больших систем. Однако этих инструментов недостаточно для разработки программ. Для эффективного синтеза программ требуются также организационные принципы, которые помогали бы нам сформулировать общий проект программы. В частности, нам нужны стратегии для построения больших программ по принципу модульности (modularity): чтобы программы «естественным» образом делились на логически цельные куски, которые можно разрабатывать и поддерживать независимо друг от друга.

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

Таким образом, способ, которым мы организуем большую программу, в значительной степени диктуется нашим восприятием моделируемой системы. В этой главе мы исследуем две важных организационных стратегии, которые соответствуют двум достаточно различным взглядам на мир и структуру систем. Первая из них сосредотачивается на объектах (objects), и большая система рассматривается как собрание индивидуальных объектов, поведение которых может меняться со временем. Альтернативная стратегия строится вокруг потоков (streams) информации в системе, во многом подобно тому, как в электронике рассматриваются системы обработки сигналов.

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


может изменяться и при этом сохранять свою индивидуальность. Из-за этого нам придется отказаться от подстановочной модели вычислений (раздел 1.1.5) в пользу более механистичной и в то же время менее привлекательной теоретически модели с окружениями (environment model). Сложности, связанные с объектами, их изменением и индивидуальностью являются фундаментальным следствием из потребности ввести понятие времени в вычислительные модели. Эти сложности только увеличиваются, когда мы добавляем возможность параллельного выполнения программ. Получить наибольшую отдачу от потокового подхода удается тогда, когда моделируемое время отделяется от порядка событий, происходящих в компьютере в процессе вычисления. Мы достигнем этого при помощи метода, называемого задержанными вычислениями (delayed evaluation).

3.1 Присваивание и внутреннее состояние объектов

Обычно мы считаем, что мир состоит из отдельных объектов, и у каждого из них есть состояние, которое изменяется со временем. Мы говорим, что объект «обладает состоянием», если на поведение объекта влияет его история. Например, банковский счет обладает состоянием потому, что ответ на вопрос «Могу ли я снять 100 долларов?» зависит от истории занесения и снятия с него денег. Состояние объекта можно описать набором из одной или более переменных состояния (state variables), которые вместе содержат достаточно информации, чтобы определить текущее поведение объекта. В простой банковской системе состояние счета можно охарактеризовать его текущим балансом, вместо того, чтобы запоминать всю историю транзакций с этим счетом.

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

Такая точка зрения на систему может служить мощной парадигмой для организации вычислительных моделей системы. Чтобы такая модель была модульной, ее требуется разделить на вычислительные объекты, моделирующие реальные объекты системы. Каждый вычислительный объект должен содержать собственные внутренние переменные состояния (local state variables), описывающие состояние реального объекта. Поскольку объекты в моделируемой системе меняются со временем, переменные состояния соответствующих вычислительных объектов также должны изменяться. Если мы решаем, что поток времени в системе будет моделироваться временем, проходящим в компьютере, то нам требуется способ строить вычислительные объекты, поведение которых меняется по мере выполнения программы. В частности, если нам хочется моделировать переменные состояния обыкновенными символическими именами в языке программирования, в языке должен иметься оператор присваивания (assignment operator), который позволял бы изменять значение, связанное с именем.


3.1.1 Внутренние переменные состояния

Чтобы показать, что мы имеем в виду, говоря о вычислительном объекте, состояние которого меняется со временем, давайте промоделируем ситуацию снятия денег с банковского счета. Воспользуемся для этого процедурой withdraw, которая в качестве аргумента принимает сумму, которую требуется снять. Если на счету имеется достаточно средств, чтобы осуществить операцию, то withdraw возвращает баланс, остающийся после снятия. В противном случае withdraw возвращает сообщение «Недостаточно денег на счете». Например, если вначале на счету содержится 100 долларов, мы получим следующую последовательность результатов:

(withdraw 25) 75

(withdraw 25) 50

(withdraw 60)

"недостаточно денег на счете"

(withdraw i5) 35

Обратите внимание, что выражение (withdraw 25), будучи вычислено дважды, дает различные результаты. Это новый тип поведения для процедуры. До сих пор все наши процедуры можно было рассматривать как описания способов вычисления математических функций. Вызов процедуры вычислял значение функции для данных аргументов, и два вызова одной и той же процедуры с одинаковыми аргументами всегда приводили к одинаковому результату.1

При реализации withdraw мы используем переменную balance, которая показывает остаток денег на счете, и определяем withdraw в виде процедуры, которая обращается к этой переменной. Процедура withdraw проверяет, что значение balance не меньше, чем значение аргумента amount. Если это так, withdraw уменьшает значение balance на amount и возвращает новое значение balance. В противном случае она возвращает сообщение «Недостаточно денег на счете». Вот определения balance и withdraw:

(define balance i00)

(define (withdraw amount) (if (>= balance amount)

(begin (set! balance (- balance amount))

balance) "Недостаточно денег на счете"))

Значение переменной balance уменьшается, когда мы выполняем выражение

1 На самом деле это не совсем правда. Одно исключение - генератор случайных чисел из раздела 1.2.6. Второе связано с таблицами операций и типов, которые мы ввели в разделе 2.4.3, где значения двух вызовов get с одними и теми же аргументами зависели от того, какие были в промежутке между ними вызовы put. С другой стороны, пока мы не ввели присваивание, мы лишены возможности самим создавать такие процедуры.



[стр.Начало] [стр.1] [стр.2] [стр.3] [стр.4] [стр.5] [стр.6] [стр.7] [стр.8] [стр.9] [стр.10] [стр.11] [стр.12] [стр.13] [стр.14] [стр.15] [стр.16] [стр.17] [стр.18] [стр.19] [стр.20] [стр.21] [стр.22] [стр.23] [стр.24] [стр.25] [стр.26] [стр.27] [стр.28] [стр.29] [стр.30] [стр.31] [стр.32] [стр.33] [стр.34] [стр.35] [стр.36] [стр.37] [стр.38] [стр.39] [стр.40] [стр.41] [стр.42] [стр.43] [стр.44] [стр.45] [стр.46] [стр.47] [стр.48] [стр.49] [стр.50] [стр.51] [стр.52] [стр.53] [стр.54] [стр.55] [стр.56] [стр.57] [стр.58] [стр.59] [стр.60] [стр.61] [стр.62] [стр.63] [стр.64] [стр.65] [стр.66] [стр.67] [стр.68] [стр.69] [стр.70] [стр.71] [стр.72] [стр.73] [стр.74] [стр.75] [стр.76] [стр.77] [стр.78] [стр.79] [стр.80] [стр.81] [стр.82] [стр.83] [стр.84] [стр.85] [стр.86] [стр.87] [стр.88] [стр.89] [стр.90] [стр.91] [стр.92] [стр.93] [стр.94] [стр.95] [стр.96] [стр.97] [стр.98] [стр.99] [стр.100] [стр.101] [стр.102] [стр.103] [стр.104] [стр.105] [стр.106] [стр.107] [стр.108] [стр.109] [стр.110] [стр.111] [стр.112] [стр.113] [стр.114] [стр.115] [стр.116] [стр.117] [стр.118] [стр.119] [стр.120] [стр.121] [стр.122] [стр.123] [стр.124] [стр.125] [стр.126] [стр.127] [стр.128] [стр.129] [стр.130] [стр.131] [стр.132] [стр.133] [стр.134] [стр.135] [стр.136] [стр.137] [стр.138] [стр.139] [стр.140] [стр.141] [стр.142] [стр.143] [стр.144] [стр.145] [стр.146] [стр.147] [стр.148] [стр.149] [стр.150] [стр.151] [стр.152] [стр.153] [стр.154] [стр.155] [стр.156] [стр.157] [стр.158] [стр.159] [стр.160] [стр.161] [стр.162] [стр.163] [стр.164] [стр.165] [стр.166] [стр.167] [стр.168] [стр.169] [стр.170] [стр.171] [стр.172] [стр.173] [стр.174] [стр.175] [стр.176] [стр.177] [стр.178] [стр.179] [стр.180] [стр.181] [стр.182] [стр.183] [стр.184] [стр.185] [стр.186] [стр.187] [стр.188] [стр.189] [стр.190] [стр.191] [стр.192] [стр.193] [стр.194] [стр.195] [стр.196]