Главная » Java » Использование реализации интерфейса

0

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

Вот как выглядит реализация интерфейса Attributed, в которой объект AttributedImpl

используется для наделения атрибутами нашего класса небесных тел:

import java.util.Enumeration;

class AttributedBody extends Body implements Attributed

{

AttributedImpl attrImpl = new AtrributedImpl();

AttributedBody() {

super();

}

AttributedBody(String name, Body orbits) {

super(name, orbits);

}

// Перенаправить все вызовы Attributed в объект attrImpl

public void add(Attr newAttr)

{ attrImpl.add(newAttr); }

public Attr find(String name)

{ return attrImpl.find(name); }

public Attr remove(String name)

{ return attrImpl.remove(name); }

public Enumeration attrs()

{ return attrImpl.attrs(); }

}

Объявление, в соответствии с которым AttributedBody расширяет класс Body и реализует интерфейс Attributed, определяет контракт Attributed Body. Реализация всех методов Body наследуется от самого класса Body. Реализация каждого из методов Attributed

заключается в перенаправлении  вызова в эквивалентный метод объекта AttributedImpl и

возврате полученного от него значения (если оно имеется). Отсюда следует, что вам

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

Перенаправление  работает без особых хитростей и требует существенно меньших усилий, чем реализация Attributed “с нуля”. Кроме того, если в будущем появится более удачная реализация Attributed, вы сможете быстро перейти на нее.

4.6. Для чего применяются интерфейсы

Между интерфейсами и абстрактными классами существует два важных отличия:

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

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

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

Тем не менее любой сколько-нибудь важный класс (абстрактный или нет), предназначенный для расширения, должен представлять собой реализацию интерфейса. Хотя для этого потребуется немного дополнительных усилий, перед вами открывается целый спектр новых возможностей, недоступных в других случаях. Например, если бы мы просто создали класс Attributed вместо интерфейса Attributed, реализованного в специальном классе AttributedImpl, то тогда на его основе стало бы невозможно построить другие полезные классы типа AttributedBody — ведь расширять можно всего один класс. Поскольку Attributed является интерфейсом, у программистов появляется выбор: они

могут либо непосредственно  расширить AttributedImpl и избежать перенаправления,  либо, если расширение невозможно, по крайней мере воспользоваться перенаправлением  для реализации интерфейса. Если общая реализация окажется неверной, они напишут свою собственную. Вы даже можете предоставить несколько реализаций интерфейса для

разных категорий пользователей. Независимо от того, какую стратегию реализации

выберет программист, создаваемые объекты будут Attributed.

Упражнение 4.1

Перепишите свое решение упражнения 3.7 с использованием интерфейса, если вы не сделали этого ранее.

Упражнение 4.2

Перепишите свое решение упражнения 3.12 с использованием интерфейса, если вы не сделали этого ранее.

Упражнение 4.3

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

Упражнение 4.4

Спроектируйте иерархию классов-коллекций  с применением одних интерфейсов.

Упражнение 4.5

Подумайте над тем, как лучше представить следующие типы (в виде интерфейсов, абстрактных или обычных классов): 1) TreeNode — для представления узлов N-арного дерева; 2) TreeWalker — для перебора узлов дерева в порядке, определяемом пользователем (например, перебор в глубину или в ширину); 3) Drawable — для представления объектов, которые могут быть нарисованы в графической системе; 4) Application — для программ, которые могут запускаться с графической рабочей поверхности (desktop).

Упражнение 4.6

Как надо изменить условия в упражнении 4.5, чтобы ваши ответы тоже изменились?

Глава 5

ЛЕКСЕМЫ, ОПЕРАТОРЫ И ВЫРАЖЕНИЯ

В этом нет ничего особенного.

Все, что от вас требуется, —

это нажимать нужные клавиши в нужный момент,

а инструмент будет играть сам.

Иоганн Себастьян Бах

В этой главе рассматриваются  основные “строительные блоки” Java —типы, операторы и выражения. Мы уже видели довольно много Java-программ и познакомились с их компонентами. В этой главе приводится детальное описание базовых элементов.

Источник: Арнольд К., Гослинг Д. – Язык программирования Java (1997)

По теме:

  • Комментарии