Лекция 6. ООП – Объектно-Ориентированное Программирование
В этой короткой, теоретической лекции вы подробней изучите концепцию Объектно-Ориентированного Программирования, каким образом программист использует объекты, что такое свойства, события и методы.
Объект
Объект – это совокупность свойств, методов и событий. То есть объект состоит из этих свойств, методов и событий, а они обеспечивают его полноценную работу. Представим себе кнопку. Она обладает:
Свойствами – цвет, текст на кнопке, шрифт текста и так далее.
Событиями – события пользовательских действий, например, пользователь нажал на кнопку, указатель мыши оказался над кнопкой, и т.п.
Методами – обеспечивающими работу кнопки, например прорисовка кнопки в нажатом и не нажатом виде, прорисовка фокуса (то есть, фокус ввода находится на кнопке).
Раньше приходилось затрачивать много времени и усилий, чтобы нарисовать такую кнопку на форме, код кнопки мог занимать страницу – полторы. Теперь мы имеем автономный объект, который достаточно бросить на форму, и он уже готов к употреблению. Нужно две кнопки? Три? Нет проблем – кидаем на форму столько кнопок, сколько нужно, и не заботимся о том, как программно эти объекты описаны в коде. Итак,
- Свойства – это переменные, которые влияют на состояние объекта. Например, ширина, высота, положение кнопки на форме или надпись на ней.
- Методы – это те же процедуры и функции, то есть это то, что объект умеет делать (вычислять). Например, объект может иметь процедуру для вывода какого-то текста на экран. Кнопка при нажатии меняет форму – это метод кнопки, процедура прорисовки вида нажатой и не нажатой кнопки.
- События – это те же процедуры и функции, которые вызываются при наступлении определенного события. Например, пользователь нажал на кнопку, вызывается процедура обработки этого нажатия. Или мышка оказалась над кнопкой – вызывается процедура обработки этого события, если программист ее создал.
Как уже упоминалось в первой лекции, программирование с применением объектов для программиста существенно упростилось. Вместо того, чтобы вписывать сотни строк кода, описывающего поведение одной единственной кнопки, программист просто объявляет объект типа "Кнопка". Далее появились системы визуального программирования, такие как Delphi, которые используют компонентную модель программирования. Здесь уже программисту не нужно самостоятельно задавать такие свойства объекта, как его положение на форме, высоту и ширину. Вы просто устанавливаете нужный компонент на форму, и мышкой или клавиатурой двигаете, растягиваете или сжимаете его до нужных размеров. Серьезное программирование превратилось в детский конструктор, где Вы создаете интерфейс программы даже не задумываясь над тем, сколько кода для этого нужно было бы написать! Delphi это делает за Вас.
Компоненты – это более совершенные объекты. То есть, это объекты, с которыми можно работать визуально. Справедливости ради следует отметить, что существуют и не визуальные компоненты, например, диалоги, с которыми нам скоро предстоит познакомиться. Не следует путать понятия "объект" и "компонент". Каждый компонент – это объект, но не каждый объект является компонентом.
Представим себе отвлеченный пример. Допустим, у нас на палитре компонентов есть компонент ТЧел, который представляет собой усредненного человека. Если мы мышкой щелкнем по этому компоненту, а затем щелкнем по форме, то мы создадим отдельный, автономный объект этого компонента, который уже обладает всеми свойствами, методами и событиями, присущими каждому человеку.
Как у любого объекта, у него есть свойство Name – имя компонента, то имя, по которому мы будем в дальнейшем обращаться к этому объекту. Delphi по умолчанию присвоит ему текст "Чел1". Если мы установим на форму еще один такой компонент, то будет создан еще один автономный объект, у которого свойство Name будет содержать строку "Чел2". Итак, свойство Name – это переменная строкового типа, принадлежащая любому объекту, и являющаяся идентификатором (опознавателем), по которому к этому объекту нужно обращаться.
Далее, у нашего воображаемого объекта есть и другие свойства. К примеру, свойства Имя, Фамилия, Отчество. Это также строковые переменные, принадлежащие этому объекту. Мы можем вписать в них нужный текст программно:
Чел1.Имя := 'Иван';
Чел1.Отчество := 'Иванович';
Чел1.Фамилия := 'Иванов';
Обратите внимание, что свойство Name содержит имя объекта, по которому мы к этому объекту обращаемся. Если бы мы изменили в свойстве Name Чел1 на Человек, то код выглядел бы иначе:
Человек.Имя := 'Иван';
Точно также, эти свойства мы можем менять в Инспекторе объектов, в момент создания программы. Так, мы меняли свойства Caption у кнопок, выводя на них нужный нам текст. А перемещая кнопку на нужную позицию, мы тем самым меняли свойства Left и Top.
Далее, этот объект имеет не только строковые, но и другие типы свойств – переменных. Например, возраст (в годах) и рост (в сантиметрах) – это будут переменные целого типа:
Чел1.Возраст := 30;
Чел1.Рост := 180;
Объект может иметь и символьную переменную, например, свойство Пол (м – мужской, ж – женский):
Чел1.Пол := 'м';
Также такой объект может иметь и логический тип данных, например, Военнообязанный, то есть, был в армии или нет:
Чел1.Военнообязанный := True;
Очень часто бывает, что в различных компонентах мы указываем в инспекторе объектов начальные значения, как бы по умолчанию, а затем во время работы программы меняем их на нужные. В программе с убегающей кнопкой, мы задали кнопке начальное положение, а затем, во время работы программы, мы изменяли свойства Left и Top. А когда пользователь вводил текст в компонент Edit, программно менялось его свойство Text.
Итак, мы получили более-менее оформленный объект человека. Мы можем накидать целую форму таких объектов, используя компонент ТЧел, и создать целый город Челов. И у каждого заполнить приведенные в примерах свойства, так что все объекты будут разными, хотя и произошли от одного общего компонента.
Вернемся к реальным компонентам. Возьмем компонент TEdit. Если мы кинем на форму такой компонент, то Delphi автоматически установит свойство Name равным строке Edit1. В дальнейшем мы будем обращаться к этому объекту по этому имени. Если же мы в Инспекторе объектов изменим свойство Name с Edit1 на, скажем, MyEdit, то обращаться придется уже не к Edit1, а к MyEdit. Предположим, мы изменили это свойство. Не забываем, что свойство Name – переменная типа строка.
Далее, в компоненте TEdit нас интересуют еще несколько свойств. Это свойства Left и Top, которые имеют целый тип, и обеспечивают положение компонента на форме. Свойство Left указывает в пикселях расстояние от левой границы формы до компонента, а свойство Top – такое же расстояние от верхней границы.
Еще есть свойства Width (ширина компонента) и Height (высота компонента). Это тоже целые типы, они указывают значение в пикселях.
Также нас интересует свойство Text. Это строковая переменная, она указывает, какой текст отображается в этом компоненте. Мы можем ввести в него текст в Инспекторе объектов, и тогда при работе программы он сразу же будет отображаться в поле ввода. Гораздо чаще его оставляют пустым. В Инспекторе объектов просто очищают это свойство, а программно можно присвоить компоненту пустую строку:
MyEdit.Text := '';
Затем, во время выполнения программы, пользователь вводит какой то текст в наш Edit. Нам нужно обработать этот текст, и получить к нему доступ мы можем, указав имя нужного объекта и его свойство:
s := MyEdit.Text;
В данном примере мы присвоили строковой переменной s тот текст, который в данный момент хранился в поле ввода нашего компонента Edit.
Напомним, что присвоение происходит слева – направо, то есть вначале указывается переменная, которой мы собираемся присвоить значение, затем идет знак присваивания ":=", после чего идет то значение, которое мы присваиваем этой переменной.
Мы имеем возможность программно изменить текст в этом объекте, и в данном случае будем указывать переменную – свойство Text:
MyEdit.Text := 'Новый текст';
Теперь наш объект будет отображать строку с новым текстом. Кроме того, мы программно можем изменять и другие свойства. Например, свойство Left – положение от левой границы формы. Мы можем указать:
MyEdit.Left := MyEdit.Left – 5;
Если мы укажем такой текст, например, в процедуре обработки нажатия кнопки, то каждый раз, когда мы нажимаем на кнопку, объект MyEdit будет сдвигаться влево на 5 пикселей. Попробуйте сами! Создайте новое приложение, установите на форму компонентTEdit и кнопку. Если Вы не изменили свойство Name, то придется обращаться к объекту по имени, которое по умолчанию присвоила ему Delphi:
Edit1.Left := Edit1.Left - 5;
Конечно, если Вы пишете не какую-нибудь шуточную программу, то менять положение объектов не стоит, даже если у Вас есть такие возможности. Кроме того, Вы имеете возможность программно изменить свойство Name у объекта, но это будет грубейшей ошибкой – как только программа обратится к этому объекту по старому имени, тут же произойдет ошибка выполнения программы – она просто не найдет этот объект. Можете попробовать в данном примере, дописав строку:
Edit1.Name := 'MyEdit';
Как только Вы нажмете кнопку в первый раз, объект послушно сместится влево, и сразу же за этим поменяет имя. Но попробуйте нажать кнопку еще раз, и программа сразу зависнет – она уже не видит объект с новым именем, ведь в коде обработки кнопки мы по прежнему обращаемся к объекту по старому имени, а объекта с таким именем уже не существует!
Не волнуйтесь, ничего страшного не произошло. Просто выберите в меню команду Run – Program Reset. Это заставит программу досрочно прекратить выполнение и закрыться.
Не забывайте еще вот о чем: не у всех компонентов, которые выводят текст на экран, есть свойство строкового типа Text. Например, у компонента TLabel таким свойством является Caption, и если мы хотим в инспекторе объектов вписать в объект какой-то текст, вписывать его нужно именно в свойство Caption. Точно также, Если мы желаем программно изменить текст в объекте Label1, то делаем присвоение нового текста его свойству Caption:
Label1.Caption := 'Новый текст';
Однако, есть объекты и посложней. Возьмем, к примеру, TMemo. У него нет свойства Text в инспекторе объектов, однако это свойство доступно программно. Переменная – свойство Text объекта Memo1 содержит весь текст, все строки, которые находятся в данный момент в компоненте. Однако гораздо чаще применяют свойство Lines. Это свойство уже не простая строковая переменная, а целый массив строк, где каждый элемент содержит отдельную строку текста. Можно сказать, что Lines – это объект в объекте, который также имеет свои методы, то есть функции и процедуры. Чаще всего используют методы SaveToFile() и LoadFromFile(), которые соответственно, сохраняют текст в файл и читают текст из файла. Свойство Lines имеет тип TStrings, то есть не просто строку, а набор строк.
Многие компоненты имеют свойства такого типа. Например, компонент TListBox имеет свойство Items, которое принадлежит к тому же типу TStrings, следовательно, имеет такие же методы, как и Lines у компонента TMemo. Это значит, что вызвав нужный метод, мы можем, к примеру, считать текст из файла и в Memo, и в ListBox:
Memo1.Lines.LoadFromFile('myfile.txt');
ListBox1.Items.LoadFromFile('myfile.txt');
Вы неоднократно будете встречаться со свойствами типа TStrings в разных компонентах. Это очень мощный инструмент, и нужно учиться пользоваться им.
События
События – это процедуры, которые выполняются всякий раз, когда это событие происходит. Например, событие OnChange компонента Edit происходит всякий раз, когда меняется текст в поле ввода. События находятся на вкладке Events Инспектора объектов. Если мы ничего не создадим, то при наступлении этого события ничего и не произойдет. Однако, если мы дважды щелкнем мышью по этому событию – создастся процедура обработки этого события. И тот программный код, который мы введем, будет выполняться всякий раз, когда это событие происходит.
Событие OnChange применяют довольно часто, когда нужно проверить правильные ли данные вводит пользователь. Хорошая новость – большинство событий (а также свойств и методов), у большинства компонентов одинаковы. Например, практически любой компонент для ввода пользователем текста имеет событие OnChange. Применяете ли вы Edit или Memo, это событие работает одинаково.