ABAP ООП Ч.4. Наследование

ABAP ООП

Наследование

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

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

Однако позже в процессе разработки требование было усилено, чтобы разделить студентов на различные категории — студентов коммерции, студентов ИТ, студентов естественных наук и т. д. — с каждой категорией студентов, требуемой для выполнения определенных задач. По мере того, как требования становятся более конкретными, вы можете просто наследовать исходный класс учащихся, чтобы добавить более конкретные функции для каждой категории учащихся, как показано в коде ниже. Это позволяет вам переходить от общей функциональности к конкретной, не нарушая существующей реализации, при этом повторно используя усилия, которые уже были затрачены на первоначальную разработку.
CLASS lcl_student DEFINITION.
  PUBLIC SECTION.
    METHODS tuition_fee.
ENDCLASS.

CLASS lcl_commerce_student DEFINITION INHERITING FROM lcl_student.
  PUBLIC SECTION.
    METHODS tuition_fee REDEFINITION.
    METHODS subject."Новый метод для дополнительной функциональности
ENDCLASS.

CLASS cl_commerce_student IMPLEMENTATION.
  METHOD tuition_fee.
    "Логика для расчета платы за обучениедля студентов коммерциииди
  ENDMETHOD.
  METHOD subject.
  ENDMETHOD.
ENDCLASS.
Новый класс lcl_commerce_student является подклассом исходного суперкласса lcl_student, от которого он наследуется.

Наследование компонентов

Наследование привносит новое измерение в скрытие реализации. С защищенным разделом ( Protected section ) вы можете ограничить доступ к компонентам из внешних приложений, но разрешить доступ к подклассам. Поэтому для внешних программ компоненты, определенные в защищенном разделе, аналогичны компонентам в частном разделе( Private section ). Это позволяет предоставлять доступ к дочерним классам, не раскрывая компоненты родительского класса для других программа.
CLASS lcl_parent DEFINITION.
  PUBLIC SECTION.
    METHODS set_value IMPORTING iv_value TYPE string.

  PROTECTED SECTION.
    DATA mv_value TYPE string.
    METHODS check_value.

  PRIVATE SECTION.
    METHODS reset_value.
ENDCLASS.
CLASS lcl_parent IMPLEMENTATION.
  METHOD set_value.
  ENDMETHOD.
  METHOD check_value.
  ENDMETHOD.
  METHOD reset_value.
  ENDMETHOD.
ENDCLASS.

CLASS lcl_child DEFINITION INHERITING FROM lcl_parent.

ENDCLASS.
CLASS lcl_child IMPLEMENTATION.

ENDCLASS.

DATA lo_child TYPE REF TO lcl_child.

START-OF-SELECTION.
  CREATE OBJECT lo_child.
  lo_child->set_value( iv_value = 'подкласс').
Класс lcl_child определен как подкласс lcl_parent. Здесь lcl_parent является суперклассом и реализует метод set_value в общедоступном разделе, метод check_value и атрибут mv_value в защищенном разделе и метод reset_value в частном разделе.

Класс lcl_child наследуется от lcl_parent с помощью оператора inheriting from добавлен в определения класса ( class definition ) . Это устанавливает отношения родитель-потомок между двумя классами. Класс lcl_child не перечисляет никаких компонентов сам по себе. Все компоненты в открытых и защищенных разделах суперкласса lcl_parent по умолчанию доступны для подкласса lcl_child. Это показано в программном коде в разделе start-of-selection, где дочерний объект ссылки определяется ссылкой на lcl_child, а доступ к методу set_value для lcl_parent осуществляется с помощью этой ссылки на дочерний класс с оператором lo_child->set_value. (значение = 'подкласс').

Помните о следующих важных моментах, касающихся наследования:

➖Подкласс может получить доступ к компонентам, определенным в общедоступных и защищенных разделах видимости в суперклассе ( Public and Protected section ) .

➖Вы не можете определить компонент в подклассе с тем же именем, что и компонент в открытых или защищенных разделах суперкласса ( Public and Protected section ).

➖Поскольку частный раздел ( private section ) суперкласса невидим для подкласса, вы можете определить компонент в подклассе с тем же именем, что и компонент в суперклассе.

➖Доступ к видимым атрибутам экземпляра в суперклассе можно получить непосредственно из подкласса или с помощью переменной самоссылки me.

➖Доступ к видимым методам экземпляра суперкласса можно получить из подкласса с помощью псевдоссылочной переменной super.

➖Статические атрибуты класса связаны с полным деревом наследования (все классы в иерархии), а не только с одним классом, поэтому к ним можно получить доступ, используя любую ссылку на класс в иерархии. Например, если три класса A, B и C находятся в отношениях наследования (B наследует A, а C наследует B), а класс A содержит статический атрибут counter, то к этому атрибуту можно получить доступ как A=>counter или B=>counter. или C=>counter.

➖Конструктор суперкласса не наследуется подклассом. Это позволяет подклассу определять свой собственный конструктор, но для обеспечения правильного создания экземпляров компонентов суперкласса обязательно вызывать конструктор суперкласса в конструкторе подкласса, например конструкторы экземпляров. Если подкласс реализует статический конструктор, среда выполнения автоматически вызывает статический конструктор суперкласса при создании экземпляра подкласса, если статический конструктор суперкласса еще не вызван.

➖Конструктор класса будет иметь видимость своих собственных компонентов. Например, если метод переопределен в подклассе и доступ к этому методу осуществляется в конструкторе как суперкласса, так и подкласса, то конструктор подкласса выполнит переопределенный метод в подклассе, а конструктор суперкласса выполнит исходный метод в суперклассе.

➖Метод суперкласса можно переопределить в подклассе, используя дополнение REDEFINITION к оператору METHODS в части определения подкласса. Это позволяет расширить функциональность метода суперкласса в подклассе.

➖Любые изменения в суперклассе будут автоматически доступны для подкласса, тогда как изменения в подклассах не влияют на суперкласс. Например, если метод суперкласса улучшен, то улучшения будут автоматически доступны для подкласса. Однако если тот же метод будет изменен (переопределен) в подклассе, это не повлияет на суперкласс.

➖Определение компонента суперкласса не может быть изменено в подклассе; другими словами, вы можете переопределить метод суперкласса в подклассе, но вы не можете изменить его интерфейс параметров.


CLASS lcl_parent DEFINITION.
  PUBLIC SECTION.
    METHODS constructor.
  PROTECTED SECTION.
    METHODS meth.
ENDCLASS.
CLASS lcl_parent IMPLEMENTATION.
  METHOD constructor.
    me->meth( ).
  ENDMETHOD.
  METHOD meth.
    WRITE /'Родительский класс'.
  ENDMETHOD.
ENDCLASS.
CLASS lcl_child DEFINITION INHERITING FROM lcl_parent.
  PUBLIC SECTION.
    METHODS constructor.
  PROTECTED SECTION.
    METHODS meth REDEFINITION.
ENDCLASS.
CLASS lcl_child IMPLEMENTATION.
  METHOD constructor.
    super->constructor( ).
    me->meth( ).
  ENDMETHOD.
  METHOD meth.
    WRITE /'Дочерний класс'.
  ENDMETHOD.
ENDCLASS.
DATA lo_child TYPE REF TO lcl_child.

START-OF-SELECTION.
  CREATE OBJECT lo_child.
lcl_parent определяется с помощью конструктора экземпляра и метода meth. lcl_child наследует класс lcl_parent, а метод meth переопределяется в подклассе lcl_child. Ссылочный объект, дочерний элемент, определяется в программе, ссылающейся на lcl_child. Когда дочерний объект ссылки создается в начале выбора с помощью оператора CREATE OBJECT, он вызывает конструктор класса lcl_child. Однако, поскольку родительский конструктор необходимо вызывать до того, как можно будет получить доступ к каким-либо компонентам экземпляра, конструктор в lcl_child вызывает родительский конструктор с помощью оператора super->constructor().

Результат работы программы:




 Метод meth вызывается в соответствующих конструкторах суперкласса и подкласса. Конструктор подкласса lcl_child вызывает переопределенный метод meth в lcl_child, тогда как конструктор суперкласса lcl_parent вызывает метод meth, определенный в lcl_parent.

Абстрактные классы и методы

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

    Если вы определяете этот студенческий класс как обычный класс, вам также необходимо поддерживать его реализацию. Однако, поскольку это общий класс, поддерживать реализацию не имеет смысла. В таких сценариях мы можем определить этот студенческий класс как абстрактный класс и определить метод tuition_fee как абстрактный метод.

    Абстрактный класс поддерживает только определение; для такого класса не требуется реализация, если он содержит все абстрактные компоненты, которые унаследованы в подклассе, в котором может поддерживаться конкретная реализация. 
CLASS lcl_student DEFINITION ABSTRACT.
  PUBLIC SECTION.
    METHODS tuition_fee ABSTRACT.
ENDCLASS.

CLASS lcl_commerce_student DEFINITION INHERITING FROM lcl_student.
  PUBLIC SECTION.
    METHODS tuition_fee REDEFINITION.
ENDCLASS.
CLASS lcl_commerce_student IMPLEMENTATION.
  METHOD tuition_fee.
    "Логика для расчета платы за обучение для студентов коммерции
  ENDMETHOD.
ENDCLASS.
CLASS lcl_science_student DEFINITION INHERITING FROM lcl_student.
  PUBLIC SECTION.
    METHODS tuition_fee REDEFINITION.
ENDCLASS.
CLASS lcl_science_student IMPLEMENTATION.
  METHOD tuition_fee.
    "Логика расчета платы за обучение для студентов, изучающих естественные науки
  ENDMETHOD.
ENDCLASS.
lcl_student определяется как абстрактный класс с помощью дополнения ABSTRACT с оператором CLASS DEFINITION.

Абстрактный метод определяется в классе lcl_student с помощью дополнения ABSTRACT с оператором METHODS. Поскольку этот класс содержит абстрактный метод, его можно реализовать в подклассе только с помощью добавления REDEFINITION. Если класс содержит обычный метод (не абстрактный метод), то нам нужно поддерживать реализацию этого метода в части реализации класса lcl_student. Тогда эта реализация будет частью шаблона, который наследуют подклассы. Поскольку нет обычных методов, мы полностью проигнорировали реализацию класса lcl_student.

Код определяет два подкласса — lcl_commerce_student и lcl_science_student — которые унаследованы от абстрактного класса lcl_student. Метод tuition_fee переопределен в соответствующих подклассах для реализации определенных функций.

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

Заключительные классы и методы

Иногда в дереве наследования дальнейшее расширение класса может не иметь смысла. В таком сценарии вы можете сделать класс final, а значит, его нельзя будет наследовать дальше. Локальный класс можно определить как конечный класс, добавив FINAL к оператору CLASS DEFINITION.
CLASS lcl_final DEFINITION FINAL.
  .....
ENDCLASS.
Методы также могут быть определены как final, чтобы их нельзя было переопределить в дальнейшем в подклассе. Например, может иметь смысл дальнейшее наследование класса, но метод в классе может не нуждаться в дальнейшем расширении, поскольку его цель завершена. Вы можете сделать метод final, добавив FINAL в инструкцию METHODS, как показано в листинге
CLASS lcl_parent DEFINITION.
  PUBLIC SECTION.
    METHODS student FINAL.
ENDCLASS.

Композиция

    Композиция – это когда двигатель не существует отдельно от автомобиля. Он создается при создании автомобиля и полностью управляется автомобилем.

   Используя наследование, мы разрабатываем классы, соответствующие отношениям. Например, студент коммерции — это тип студента, поэтому мы определяем класс commerce_student как подкласс студента. Иногда, пытаясь повторно использовать существующий код, разработчики создают дерево наследования, которое не соответствует отношениям. Например, если у нас есть существующий класс заказов, имеет смысл создать класс sales_order, наследуемый от класса заказов, потому что заказ на продажу — это заказ.

  Однако, если вы хотите определить класс для доставки, может не иметь смысла наследовать класс заказов, поскольку доставка не является заказом. Однако с каждым заказом связана одна или несколько доставок. Если объект связан с другим объектом, мы называем это отношением композиции.

 Композиция позволяет повторно использовать существующие функции, сохраняя существующий объект в качестве атрибута в классе. Следовательно, класс заказов может иметь доставку как атрибут класса. Такая компоновка позволяет использовать существующие в системе функции.
CLASS lcl_delivery DEFINITION.
  PUBLIC SECTION.
    METHODS get_delivery.
ENDCLASS.
CLASS lcl_delivery IMPLEMENTATION.
  METHOD get_delivery.
  ENDMETHOD.
ENDCLASS.
CLASS lcl_orders DEFINITION.
  PUBLIC SECTION.
    METHODS track_order.
  PRIVATE SECTION.
    DATA mo_delivery TYPE REF TO lcl_delivery.
ENDCLASS.
CLASS lcl_orders IMPLEMENTATION.
  METHOD track_order.
    CREATE OBJECT mo_delivery.
    mo_delivery->get_delivery( ).
  ENDMETHOD.
ENDCLASS.

    Класс lcl_delivery поддерживается как атрибут класса lcl_orders. Объект mo_delivery создается в методе track_order для доступа к информации о доставке заказа.

Как показывает опыт, используйте отношения наследования между объектами, если они соответствуют отношениям is ( при котором один класс есть подвидом другого класса ), и используйте композицию, если объекты соответствуют отношениям has ( класс содержит другой класс ). Этот совет должен помочь вам принимать лучшие дизайнерские решения.


Комментарии