При моделировании какой-либо системы, построенной с помощью ООП, широко используется унифицированный язык моделирования UML. Взаимосвязи между классами в такой системе принято отображать в виде диаграммы классов. В данной статье рассмотрены основные взаимосвязи между классами и как они отражаются в ABAP коде.
Взаимосвязь — это особый тип логических отношений между сущностями, показанных на диаграммах классов и объектов.
Существуют следующие виды взаимосвязей:
1. Обобщения
Отношение обобщения — это наследование. Диаграмма с отношением обобщения выглядит как показано на рисунке:
Класс lcl_person является более общим классом, от которого наследуется класс lcl_student, таким образом наследование отображается с помощью не закрашенного ромбика следующего от наследника.
В UML классы обозначаются в виде прямоугольников. В верхней части прямоугольника указано имя класса (для абстрактных классов и методов, имя обозначается курсивом), в средней атрибуты, в нижней его методы.
Для наглядности атрибуты и методы классов помечены квантором видимости, который может принимать следующие значения:
- «+» — общедоступный атрибут, метод. В описании ABAP класса, находится в области PUBLIC SECTION.
- «-» — атрибут, метод с закрытой областью видимости. В описании ABAP класса, находится в области PRIVATE SECTION.
- «#» — атрибут, метод с защищенной областью видимости. В описании ABAP класса, находится в области PROTECTED SECTION.
Код для этой диаграммы:
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 |
CLASS lcl_person DEFINITION. PUBLIC SECTION. METHODS: get_name RETURNING value(rv_name) TYPE string, set_name IMPORTING iv_name TYPE string. PRIVATE SECTION. DATA: gv_name TYPE string. ENDCLASS. CLASS lcl_person IMPLEMENTATION. METHOD get_name. rv_name = me->gv_name. ENDMETHOD. METHOD set_name. me->gv_name = iv_name. ENDMETHOD. ENDCLASS. CLASS lcl_student DEFINITION INHERITING FROM lcl_person FINAL. PUBLIC SECTION. METHODS: get_class RETURNING value(rv_class) TYPE string, set_class IMPORTING iv_class TYPE string. PRIVATE SECTION. DATA: gv_class TYPE string. ENDCLASS. CLASS lcl_student IMPLEMENTATION. METHOD get_class. rv_class = me->gv_class. ENDMETHOD. METHOD set_class. me->gv_class = iv_class. ENDMETHOD. ENDCLASS. |
2. Ассоциации
Ассоциации показывают, что объекты одной сущности связаны с объектами другой сущности. Если между двумя классами определена ассоциация, то можно перемещаться от объектов одного класса к объектам другого. Вполне допустимы случаи, когда оба конца ассоциации относятся к одному и тому же классу. Это означает, что с объектом некоторого класса позволительно связать другие объекты из того же класса.
2.1. Бинарная и N-арная ассоциация
Ассоциация, связывающая два класса, называется бинарной. Можно, хотя это редко бывает необходимым, создавать ассоциации, связывающие сразу несколько классов; они называются n-арными. Графически ассоциация изображается в виде линии (со стрелкой или без неё), соединяющей класс сам с собой или с другими классами.
Часто при моделировании бывает важно указать, сколько объектов может быть связано посредством одного экземпляра ассоциации. Это число называется кратностью (Multiplicity) роли ассоциации и записывается либо как выражение, значением которого является диапазон значений, либо в явном виде. Указывая кратность на одном конце ассоциации, вы тем самым говорите, что на этом конце именно столько объектов должно соответствовать каждому объекту на противоположном конце. Кратность можно задать равной единице (1), можно указать диапазон: «ноль или единица» (0..1), «много» (0..*), «единица или больше» (1..*). Разрешается также указывать определенное число (например, 3). С помощью списка можно задать и более сложные кратности, например 0 . . 1, 3..4, 6..*, что означает «любое число объектов, кроме 2 и 5».
Пример бинарной ассоциации. Предположим, что у студента должен быть пропуск, пропуск обычно имеется только в одном экземпляре. Графически взаимосвязь будет выглядеть так:
Код диаграммы:
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 |
CLASS lcl_pass DEFINITION. PUBLIC SECTION. METHODS: get_id RETURNING value(rv_id) TYPE string. PRIVATE SECTION. DATA: gv_id TYPE string. ENDCLASS. CLASS lcl_pass IMPLEMENTATION. METHOD get_id. rv_id = me->gv_id. ENDMETHOD. ENDCLASS. CLASS lcl_student DEFINITION INHERITING FROM lcl_person FINAL. PUBLIC SECTION. METHODS: get_class RETURNING value(rv_class) TYPE string, set_class IMPORTING iv_class TYPE string, get_pass RETURNING value(ro_pass) TYPE REF TO lcl_pass, set_pass IMPORTING io_pass TYPE REF TO lcl_pass. PRIVATE SECTION. DATA: gv_class TYPE string, go_pass TYPE REF TO lcl_pass. ENDCLASS. CLASS lcl_student IMPLEMENTATION. METHOD get_class. rv_class = me->gv_class. ENDMETHOD. METHOD set_class. me->gv_class = iv_class. ENDMETHOD. METHOD get_pass. ro_pass = me->go_pass. ENDMETHOD. METHOD set_pass. CHECK io_pass IS BOUND. me->go_pass = io_pass. ENDMETHOD. ENDCLASS. |
Класс lcl_student имеет поле go_pass, в котором содержится ссылка на объект класса lcl_pass, с помощью метода get_pass мы можем получить этот объект, соответственно взаимосвязь идет от lcl_student к lcl_pass (направление стрелки).
2.2 Агрегации
Ранее рассмотренная простая ассоциация между двумя классами отражает структурное отношение между равноправными сущностями, когда оба класса находятся на одном концептуальном уровне и ни один не является более важным, чем другой.
Специальной формой или частным случаем отношения ассоциации является отношение агрегации, которое, в свою очередь, тоже имеет специальную форму — отношение композиции.
Отношение агрегации имеет место между несколькими классами в том случае, если один из классов представляет собой некоторую сущность, включающую в себя в качестве составных частей другие сущности.
Данное отношение имеет фундаментальное значение для описания структуры сложных систем, поскольку применяется для представления системных взаимосвязей типа «часть-целое».
Агрегирование является частным случаем ассоциации и изображается в виде простой ассоциации с не закрашенным ромбом со стороны «целого».
Пример:
Код (без реализации методов):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
CLASS lcl_ faculty DEFINITION. PUBLIC SECTION. METHODS: add_student IMPORTING io_student TYPE REF TO lcl_student, del_student IMPORTING io_student TYPE REF TO lcl_student. PRIVATE SECTION. DATA: gt_sudents TYPE STANDARD TABLE OF REF TO lcl_student. ENDCLASS. CLASS lcl_ faculty IMPLEMENTATION. METHOD add_student. "... ENDMETHOD. METHOD del_student. "... ENDMETHOD. ENDCLASS. |
В данном примере класс lcl_faculty содержит внутри себя внутреннюю таблицу с обучающимися на факультете студентами (и таким образом их агрегирует) и имеет методы для манипулирования её содержимым. С точки зрения структуры факультет является целым, а студенты его составной частью.
2.3. Композиции
Композиция — более строгий вариант агрегации. Известна также как агрегация по значению. Композиция имеет жёсткую зависимость времени существования экземпляров класса контейнера и экземпляров содержащихся классов. Если контейнер будет уничтожен, то всё его содержимое будет также уничтожено.
Графически представляется, как и агрегация, но с закрашенным ромбиком.
Допустим, у каждого студента есть запись об оценках по предмету, оценки могут храниться до тех пор, пока есть запись о студенте, иначе данные не имеют смысла, таким образом, при удалении записи студента, должны удаляться все записи о его оценках:
При удалении студента из системы (факультета), будет вызываться метод clear_evals класса lcl_student, удаляющий все оценки по студенту, после чего состояние системы (факультета) может быть сохранено в БД.
3. Реализации
Реализация — отношение между двумя элементами модели, в котором один элемент (клиент) реализует поведение, заданное другим (поставщиком). Реализация — отношение целое-часть. Явное выражение реализации в ABAP это реализация классом заданного интерфейса. Графически реализация представляется так же, как и наследование, но с пунктирной линией.
Изменим слегка наш первый пример, класс lcl_person будет реализовывать интерфейс lif_man с методами get_name и set_name:
Код диаграммы:
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 |
INTERFACE lif_man. METHODS: get_name RETURNING value(rv_name) TYPE string, set_name IMPORTING iv_name TYPE string. ENDINTERFACE. CLASS lcl_person DEFINITION. PUBLIC SECTION. INTERFACES: lif_man. ALIASES: get_name FOR lif_man~get_name, set_name FOR lif_man~set_name. PRIVATE SECTION. DATA: gv_name TYPE string. ENDCLASS. CLASS lcl_person IMPLEMENTATION. METHOD lif_man~get_name. rv_name = me->gv_name. ENDMETHOD. METHOD lif_man~set_name. me->gv_name = iv_name. ENDMETHOD. ENDCLASS. |
3. Зависимости
Зависимость (dependency) — это слабая форма отношения использования, при котором изменение в спецификации одного влечёт за собой изменение другого, причём обратное не обязательно. Возникает, например, когда объект выступает в форме параметра или локальной переменной. Зависимость может быть между экземплярами, классами или экземпляром и классом.
Графически представляется пунктирной стрелкой, идущей от зависимого элемента к тому, от которого он зависит.
В качестве примера можно рассмотреть случай использования simple alv в локальном классе отчёта:
Код:
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 |
CLASS lcl_report DEFINITION. PUBLIC SECTION. CLASS-METHODS: show_data, get_data. PRIVATE SECTION. CLASS-DATA: gt_data TYPE spfli_tab. " Таблица с данными ENDCLASS. CLASS lcl_report IMPLEMENTATION. METHOD get_data. SELECT * FROM spfli INTO CORRESPONDING FIELDS OF TABLE gt_data. ENDMETHOD. METHOD show_data. DATA: lo_alv TYPE REF TO cl_salv_table. TRY. cl_salv_table=>factory( IMPORTING r_salv_table = lo_alv CHANGING t_table = gt_data ). CATCH cx_salv_msg . MESSAGE 'Ошибка при создании ALV' TYPE 'E'. ENDTRY. lo_alv->display( ). ENDMETHOD. ENDCLASS. START-OF-SELECTION. lcl_report=>get_data( ). lcl_report=>show_data( ). |
Локальный класс lcl_report в методе show_data вызывает метод класса cl_salv_table, если описание метода поменяется, необходимо будет изменить так же и lcl_report.
Подводя итоги можно сказать, что диаграмма классов UML позволяет разработчикам лучше понимать друг друга при проектировании архитектуры системы, вне зависимости от языка программирования.