#25 ООП Паскале. Введение: Класс, объект, конструктор, метод, поле
Primary tabs
Что такое ООП
ООП (Объектно Ориентированное Программирование) -- подход к разработке программ, в котором в любой задаче пытаются выделить набор "объектов" и с помощью кода описать их свойства и возможное "поведение" внутри программы.
Для работы в этом стиле высокоуровневые языки программирования обладают специальными синтаксическими конструкциями (ключевыми словами и т.д.), которые позволяют описывать группу понятий/подходов, которые являются общими для разных языков программирования (обычно какие-то возможности или идеи сначала появлются в одном языке программирования, а потом перекочовывают в другие, т.к. языки программирования часто влияют друг на друга).
Далее мы начнем знакомиться с основными из этих понятий, активно используя примеры кода
Привет ООП! Начало работы с классами. Методы, конструктор, создание объекта
Класс - это достаточно сложный, но тем не менее просто тип данных, который объявляется в секции type (подобно тому как мы это делаем для массивов или записей).
В отличие от записей и массивов классы не просто хранят данные, они могут действовать ;)
Давайте начнем именно с этого их свойства и рассмотрим пример:
type THelloWorld = class // объявляем класс public // секция публичных - общедоступных элементов класса procedure sayHello; // c единственным методом: процедурой end; procedure THelloWorld.sayHello; // описываем реализацию процедуры begin writeln('Привет Мир!'); end; var primer: THelloWorld; // переменная типа класса THelloWorld begin { создаем объект = "экземпляр класса" THelloWorld вызовом стандартного метода-конструктора create() } primer := THelloWorld.create(); // вызываем метод, который должен здоровься primer.sayHello(); end.
-- добавим следующее:
- Метод - это подпрограмма (в Паскале - процедура или функция), которая относится к какому-либо классу и его объектам.
- Как связаны "класс" и "объект": класс - это тип для объекта, по аналогии, напр. с тем, что конкретный автомобиль является экземпляром класса своей модели. Объект также называют "экземпляром класса" (эти термины в документации часто значат одно и то же).
- Конструктор -- специальный метод, который создает экземпляр класса. Можно сказать, что конструктор как бы строит объект в памяти компьютера, выделяя ее в нужных количествах и заполняя необходимымми структурами, а чертежом/проектом для этого строительства как раз и служит класс, в которому конструируемый объект относится.
- В примере выше использован стандартный конструктор
create()
(еще такие конструкторы называют конструкторами по умолчанию) - мы нигде не определяли его, но изначально есть у любого класса в Паскале, потому и называется стадартным
(такой же подход с наличием стандарного неявно определяемого конструктора используется и во многих других ЯП).
Поля класса - хранение данных. Обращение к собственным полям из методов класса
Выше на очень простом примере мы посмотрели, как классы и их объекты могут действовать (например здороваться).
Теперь пришло прям обсудить вторую их очень важную особенность - хранение данных. В этом они очень похожи на ранее изученный нами тип записей.
Давайте слегка модифицируем ранее изученный пример так, чтобы задействовать в работе кода поля класса:
type THelloWorld = class public whatToSay: string; // поле строкового типа procedure sayHello; end; procedure THelloWorld.sayHello; begin writeln(self.whatToSay); // поле класса в качестве аргумента процедуре печати end; var primer: THelloWorld; // переменная типа класса THelloWorld begin primer := THelloWorld.create(); primer.whatToSay := 'Привет Вася!!!'; primer.sayHello(); end.
в этом примере:
- программа делает все то же самое, но тут мы используем добавленное нами же свойство whatToSay (переводится как "что говорить"), в которое можно записать любую фразу, которую мы хотим использовать для приветствия, это позволяет менять поведение объекта после создания из тела программы.
- Поля класса - это переменные, связанные с классом (и созданными по его схеме объектами), в них могут лежать данные любого, допустимого для данной программы типа
Использование нестандартного конструктора. Передача параметров конструируемому объекту
В реальной разработке данные необходимые объекту для его работы часто передают прямо на этапе конструирования, в этом случае конструктор по-умолчанию нам не подходит. необходимо объвить свой.
Давайте изучим пример, в котором это как раз и делается:
type THelloWorld = class public whatToSay: string; // объявляем нестандартный конструктор constructor create(whatToSayValue: string); procedure sayHello; end; procedure THelloWorld.sayHello; begin writeln(self.whatToSay); end; // Добавляем реализацию собственного конструктора constructor THelloWorld.create(whatToSayValue: string); begin self.whatToSay := whatToSayValue; end; var primer: THelloWorld; // переменная типа класса THelloWorld begin primer := THelloWorld.create('Привет мир!!!'); primer.sayHello(); end.
В этом примере:
- Мы сразу, прямо в конструктор передаем аргумент строкового типа
- Конструктор реализован таким образом, что записывает переданный аргумент в поле объекта whatToSay и именно это поле использует в своей реализации метод sayHello()
Конфликты имен между аругументами метода класса и публичными полями класса
В данном разделе для объявления полей и элементов класса мы рассматривали только одну область видимости - public. Подробнее о том что это мы поговорим в следующих уроках, но сразу заметим что в freepascal для кода вида:
type Cat = class public name: string; // поле класса constructor create(name: string); // публичный (открытый) аргумент метода с имененем как и у поля выше procedure sayHello(); end; constructor Cat.create(name: string); begin self.name := name; end; procedure Cat.sayHello(); begin writeln('Привет, я ' + self.name + '!'); end; var CatItem: Cat; begin CatItem := Cat.create('Мурка'); CatItem.sayHello(); end.
-- при попытке его запустить получим ошибку:
Compile Project, Target: .. Exit code 1, Errors: 1, Hints: 1
project1.lpr(5,24) Error: Duplicate identifier "name"
- формально она вызвана тем что у нас совпадает имя поля класса name и метод конструктора в данном случае тоже имеет аргумент с таким же именем (конфликт актуален не только для конструкторов, но и и вообще для аргументов любых методов).
Если вы сталкнетесь с подобной ошибкой в ходе решения своих задач, то на данном этапе изучения ООП в Паскале (в версии fpc) просто измените имя аргумента:
type Cat = class public name: string; constructor create(nameValue: string); // изменим имя аргумента procedure sayHello(); end; constructor Cat.create(nameValue: string); begin self.name := nameValue; // используем новое имя аргумента end; procedure Cat.sayHello(); begin writeln('Привет, я ' + self.name + '!'); end; var CatItem: Cat; begin CatItem := Cat.create('Мурка'); CatItem.sayHello(); end.
-- в одном из следующих уроков мы подробнее изучим эту проблему, приведенное же выше решение уже позволит исправлять ошибки такого типа.
Пример №1 - разбиение/разделение решения на методы внутри класса
Пользователь передает целое положительное число $N$, выведете на экран последовательность от $1$ до $N$ "ёлочкой", например для $N = 18$:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
-- оформить решения задачи (способом "одним циклом") в виде класса, который:
- получает на выход все значения, необходимые для вывода последовательности
- вынести определение необходимости переноса строки и сам перенос в отдельный метод
Решение:
type Elka = class public count: integer; constructor create(n: integer); procedure newLineIfNeeded(var k, m: integer); procedure show(); end; constructor Elka.create(n: integer); begin self.count := n; end; procedure Elka.newLineIfNeeded(var k, m: integer); begin if (k = m) then begin writeln(); m+=1; // ожидаем на 1 один элемент больше в следующей строке k:=0; end; end; procedure Elka.show(); var i, m, // ожидаемая длина строки k // текущая длина строки :integer; begin m:=1; k:=0; for i:=1 to self.count do begin write(i, ' '); k+=1; self.newLineIfNeeded(k, m); end; end; var n, i, m, // ожидаемая длина строки k // текущая длина строки :integer; ElkaItem: Elka; begin n:= 10; // вводит пользователь // ----- Решение с ООП: ElkaItem := Elka.create(n); ElkaItem.show(); // -----Решение без ООП (одним циклом): m:=1; k:=0; for i:=1 to n do begin write(i, ' '); k+=1; if (k = m) then begin writeln(); m+=1; // ожидаем на 1 один элемент больше в следующей строке k:=0; end; end; end.
-- для лучше понимания можно посмотреть видео-разбор этого решения.
Видео-материалы
- Фрагмент лекции - Введение в ООП в Паскале: https://youtu.be/srEed_D_ABc
Задачи для самостоятельного решения
- Ответьте на вопросы:
- Что такое класс? Что такое объект? Как они связаны и чем отличаются?
- Что такое метод класса? Чем именно могут быть методы в Паскале?
- Что такое поля класса? Для чего нужны?
- Что такое конструктор?
- Напишите программу, которая использует объект класса Posledovatelnost для вывода на экран числа от 1 до N, при это пусть последовательность выводится самим кноструктором класса, который создает объект
- Напишите программу, которая использует объект класса Posledovatelnost для вывода на экран числа от 1 до N, для вывода последовательности реализуйте метод doIt(), который в качестве аргумента принимает значение N
- Напишите программу, которая использует объект класса Posledovatelnost для вывода на экран числа от 1 до N, при этом пусть класс на этапе конструирования объекта сохраняет значение N во внутреннее поле, а для вывода последовательности реализуйте метод doIt()
- Напишите программу, которая выводит матрицу из единиц размерами M на N, напр. для M=2 и N=4 мы должны получить результат:
1 1 1 1 1 1 1 1
-- оформите решение программы в виде класса, который (должны быть выполнены все пункты):
- решает задачу вызовом метода doIt() (без параметров)
- в конструктор принимает значения M и N (и хранит их в виде собственных полей)
- для вывода строки с указанным количеством единиц использует отдельный метод str(), работающий с ранее сохраненными параметрами.
-
Выведете последовательность следующего вида:
Пользователь вводил число $N$ - максимальное значение и число $M$, которое отвечало бы за длину возрастающего фрагмента, например для $M=4$:$ \underbrace{8 \;10 \;12 \;14}_{\text{четыре числа}} \;3 \underbrace{\;16 \;18 \;20 \;22}_{\text{четыре числа}} \;3 \; .... \;3 \;.... \;\text{и т.д.} $
Заметьте. что в предыдущей задаче $M$ было зафиксировано $=2$:
$ \underbrace{8 \;10}_{\text{два числа}} \;3 \underbrace{\;14 \;16}_{\text{два числа}} \;3 \; .... \;3 \;.... \;\text{и т.д.} $Как оформлять решение:
- Напишите класс, который будет принимать в конструкторе, параметры, которые нужны для вывода последовательности
- Пусть за вывод возрастающего фрагмента отвечает отдельный метод
- За вывод тройки тоже пусть отвечает отдельный метод
- Клиентский код, должен иметь возможность вывести последовательность в консоль вызовом одного метода doIt() без параметров
Решите задачи:
Что еще почитать
- Freepascal Class docs: https://wiki.freepascal.org/Class
- Castle Engine - Modern Pascal Classes: https://castle-engine.io/modern_pascal#_...
- Log in to post comments
- 2373 reads
vedro-compota
Sat, 02/04/2023 - 13:26
Permalink
Поля класса также иногда
Поля класса также иногда называют свойствами (property) класса (зависит от языка программирования и контекста, иногда за этими понятиями могут скрываться полностью одинаковые идеи, иногда же свойства могут описывать несколько иные возможности, чем поля).
Часто под свойствами подразумеваются те же поля, для которых есть механизм контроля процессов чтения и записи
_____________
матфак вгу и остальная классика =)
vedro-compota
Sat, 10/19/2024 - 16:11
Permalink
Конфликт имен
Пример неработающего кода с конфликтом имен между аргументом метода и именем поля
_____________
матфак вгу и остальная классика =)