Страницы

Поиск по вопросам

вторник, 13 ноября 2018 г.

Стоит ли использовать наследование при небольшом изменении реализации?

Написал односвязный список на Java. В нем сделал методы
insertFirst(T item) insertLast(T item) insert(int position, T item) removeFirst() removeLast() remove(int position) get(int position) isEmpty()
В моей реализации односвязного списка есть указатель на первый элемент списка. Этот класс называется LinkedListWithHead. Далее, я хочу написать еще одну реализацию односвязного списка, но используя уже указатель на первый и на последний элемент списка. Этот класс будет называться LinkedListWithHeadAndTail. Изменится реализация методов
insertFirst insertLast removeFirst removeLast`
То есть, по сути, большую часть методов я вообще трогать не буду, поэтому не хочется заводить совсем новый класс и там все эти методы дублировать.
Но и наследование как то не очень тут вяжется, потому что LinkedListWithHeadAndTail не является разновидностью LinkedListWithHead. И вообще, пользователям моих классов может быть важно написать метод, принимающей исключительно объекты класса LinkedListWithHead, потому что они экономичнее по памяти. А если я завяжусь на наследовании, то в такой метод можно будет передать и объект класса LinkedListWithHeadAndTail. Может быть композиция будет выходом для меня? Если вложить объект класса LinkedlistWithHead в LinkedListWithHeadAndTail


Ответ

Наследование нужно использовать если между новым и старым типами есть отношение Is-A: новый тип есть разновидность старого, и везде, где использовался объект старого типа, можно использовать и объект нового.
Использование наследования лишь для того, чтобы скопировать большую часть реализации — злоупотребление наследованием. Так делают довольно часто (особенно если язык не предоставляет удобных возможностей повторного использования реализации без наследования), но это неправильно, и может потенциально привести к проблемам.

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

Комментариев нет:

Отправить комментарий