2. Базовые термины ООП

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

Объект – структура, которая объединяет данные и методы, которые эти данные обрабатывают. Фактически, объект является основным строительным блоком объектно-ориентированных программ.

Класс – шаблон для объектов. Каждый объект является экземпляром (instance) какого-либо класса («безклассовых» объектов не существует). В рамках класса задается общий шаблон, структура, на основании которой создаются объекты. Данные, относящиеся к классу, называются полями класса, а программный код для их обработки называется методами класса. Поля и методы иногда называют общим термином – члены класса.

Разница между классом и объектом такая же, как между абстрактным понятием и реальным объектом.

Объект состоит из следующих частей:

  • имя объекта;

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

  • методы (операции) – применяются для выполнения операций с данными, а также для совершения других действий. Методы определяют, как объект взаимодействует с окружающим миром.

Объекты могут отправлять друг другу сообщения. Сообщение (message) - это практически то же самое, что и вызов функции в обычном программировании. В ООП обычно употребляется выражение "послать сообщение" какому-либо объекту. Понятие "сообщение" в ООП можно объяснить с точки зрения ООП: мы не можем напрямую изменить состояние объекта и должны как бы послать сообщение объекту, что мы хотим как-то изменить его состояние. Очень важно понять, что объект сам меняет свое состояние, а мы можем только попросить его об этом с помощью отсылки сообщения.

В объектно-ориентированной программе весь код должен находиться внутри классов!

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

От языка С к объектно-ориентированному программированию

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

Представим себе, что мы пишем программу для книжной лавки на языке C. В какой-то момент мы сталкиваемся с необходимостью хранить информацию о множестве книг: название книги, кто автор книги, год издания и стоимость книги. Как нам это запрограммировать?

Мы можем воспользоваться массивами и хранить данные о книгах в нескольких массивах.

    int book_year[100];
    int book_pages[100];
    char book_author[100][50];
    char book_title[100][100];
    float book_price[100];

Теперь мы можем обратиться к i-му номеру каждого массива для получения информации об i-ой книге.

printf("%s-%s %d page", book_author[3], book_title[3], book_pages[3]);

Какими недостатками обладает данный способ работы с данными? Такой подход может приводить к многочисленным ошибкам (например, ошибки при работе с индексами массивов), такие данные тяжело модифицировать (удаление книги приводит к необходимости смещать влево часть элементов массивов), такой код неудобно читать, поддерживать и модифицировать.

Но самое главное - мы мыслим в контексте структуры компьютера, а не решаемой задачи. Для нас книга - это некий единый объект, который имеет некоторые параметры (атрибуты): название, количество страниц и так далее. Мы же представляем атрибуты этого объекта в виде отдельных записей в разных массивах, потому что на языке C мы вынуждены мыслить в терминах имеющихся структур данных (массивов, очередей, деревьев), а не в терминах отдельных объектов и их взаимоотношений. Это затрудняет понимание решаемой задачи (управление книжной лавкой и продажей книг), увеличивает количество ошибок в программе и на некотором этапе мы вообще перестаем понимать, что происходит в программе.

Для хотя бы какого-то решения этой проблемы и облегчения труда программиста, на некотором этапе в язык C было введено понятие структуры (ключевое слово struct), которое вы должны были изучать в курсе "Алгоритмизация и программирование".

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

Сначала мы должны описать структуру (новый тип данных).

struct Book {
    char author[50];
    char title[100];
    int year;
    int pages;
    float price;
};

Таким образом, некоторое понятие реального мира (то есть, книга вообще, как понятие) в программе описан структурой Book. Экземпляр этого понятия (какая-то конкретная книга) в программе будет представлен экземпляром структуры - переменной типа Book. Мы объявляем экземпляр структуры, после чего мы сможет заполнить поля структуры и работать с ней дальше в программе.

int main() {

    // Объявляем экземпляр структуры
    struct Book book1;

    // Заполняем поля экземпляра
    strcpy(book1.author,"Иванов А.А");
    strcpy(book1.title,"Программирование на Java");
    book1.pages = 255;
    book1.price = 350.25;
    book1.year = 2018;

    // Теперь мы можем работать с созданным экземпляром структуры
    printf("%s - %s, %d страниц", book1.author, book1.title, book1.pages);

    return 0;
}

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

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

Пример использования объектного подхода

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

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

Опишем класс для фигуры «Треугольник». Какие характеристики могут быть у класса «Треугольник»? От чего зависит набор характеристик? В принципе, любой объект является бесконечно сложным и для его описания понадобится бесконечно большое количество характеристик. Но для нашего простого графического редактора важными будут следующие параметры: координаты трех точек и цвет фигуры.

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

Для указания класса в Java используется ключевое слово class, после чего идет название класса, далее мы ставим фигурные скобки и всё, что будет написано внутри фигурных скобок (переменные и функции) будет относиться к этому классу.

class Triangle {
    
}

Какие переменные и типы данных будут моделировать координаты точек и цвет? Для моделирования цвета воспользуемся обычным целым числом (очень часто цвета моделируются обычным целочисленным значением).

class Triangle {
    
    // Цвет треугольника
    int color;
}

Теперь попробуем подумать, а как нам смоделировать координаты трех точек? Если бы мы программировали на языке C, мы бы написали что-то вроде

class Triangle {

    // Цвет треугольника
    int color;

    // Координаты точек треугольника
    int x1,y1,x2,y2,x3,y3;
}

Но давайте подумаем - координаты каждой точки логически связаны между собой и когда мы будем, например, перемещать треугольник или будем менять размер треугольника, будут одновременно меняться две переменные, которые моделируют одну точку. С точки зрения объектной модели, лучшим вариантом будет предусмотреть отдельную сущность, отдельный класс, который моделирует точку на двумерной плоскости. Таким образом, мы сгруппируем данные и код, который будет эти данные изменять (например, менять значение X и Y).

class Point2D {
    
    int x;
    int y;
}

Теперь вернемся к классу Triangle и укажем, что в качестве координат у нас будут выступать три объекта класса Point2D.

class Triangle {

    // Цвет треугольника
    int color;

    // Координаты трех точек
    Point2D point1;
    Point2D point2;
    Point2D point3;
}

Теперь давайте определимся с методами классов – то есть, к тому поведению, которое будут осуществлять объекты этих классов.

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

class Point2D {

    int x;
    int y;

    void changeCoordinates(int new_x, int new_y) {
        x = new_x;
        y = new_y;
    }
}

Теперь давайте подумаем, какое поведение будет у треугольника? Что с ним можно сделать? Ну, например, можно его перекрасить, можно его передвинуть в другое место на плоскости, можно его нарисовать на каком-то полотне и так далее. Давайте опишем некоторые из этих методов.

class Triangle {

    int color; // Цвет треугольника

    // Координаты трех точек
    Point2D point1;
    Point2D point2;
    Point2D point3;

    // Меняем цвет фигуры
    void changeColor(int new_color) {
        color = new_color;
    }

    // Меняем расположение фигуры
    // Для наглядности будем передавать входные параметры
    // в виде 6 целых чисел
    void move(int x1, int y1, int x2, int y2, int x3, int y3) {
        point1.changeCoordinates(x1, y1);
        point2.changeCoordinates(x2, y2);
        point3.changeCoordinates(x3, y3);
    }
}

Итак, мы описали треугольник, но мы определили только понятие «треугольник», у нас нет их физически, код класса - это просто описание. Если вы создали java-проект, у вас, по умолчанию, будет присутствовать класс Mainи функция main(). Пока что мы не будем объяснять, зачем нужен класс Main, а обратим внимание на метод main(). Этот метод является «точкой входа» в программу, с вызова этого метода начинается работа программы. Когда мы дойдем и выполним последнюю инструкцию внутри метода main(), приложение завершится.

Создадим внутри метода main() несколько объектов класса Triangle. Создание объектов мы будем рассматривать подробно в следующих лекциях, на данный момент мы просто скажем, что для создания объекта используется ключевое словоnew. После создания объекта, мы можем обращаться к объекту и вызывать методы у объектов (как мы вызывали функции в C).

public class Main {

    public static void main(String[] args) {

        // Создали один объект треугольника
        Triangle triangle1 = new Triangle();

        // Создали еще один объект треугольника
        Triangle triangle2 = new Triangle();

        // Для первого треугольника поменяли цвет
        triangle1.changeColor(10000);
        // Поменяли цвет для второго треугольника
        triangle2.changeColor(20000);

        // Создали объект первой точки треугольника
        // и назначили точке какие-то координаты
        triangle1.point1 = new Point2D();
        triangle1.point1.changeCoordinates(140,180);

        // Сделаем это же для первой точки второго треугольника
        triangle2.point1 = new Point2D();
        triangle2.point1.changeCoordinates(50,80);
    }
}

Важно! Вы должны понять, что у разных объектов значения переменных будут разные!

То есть, где-то в оперативной памяти у нас создано два разных объекта. Внутри первого есть объект класса Point2D, внутри этого объекта есть две переменные типа int, у которых будут значения 140 и 180. Внутри же второго треугольника будет свой объект Point2D, внутри которого будут совершенно другие две переменные типа int, у которых будут значения 50 и 80. Это же будет касаться переменных color в двух разных объектах (рис.1.5).

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

Last updated