Вступление
Я прочитал несколько сообщений об использовании интерфейсов и абстрактных классов здесь на SO. Я нашел, в частности, что я хотел бы ссылку здесь - Link - Интерфейс с методами по умолчанию против абстрактного класса, он охватывает тот же вопрос. В качестве принятого ответа рекомендуется использовать методы интерфейса по умолчанию, когда это возможно. Но комментарий ниже этого ответа, в котором говорится, что "эта функция больше похожа на хак для меня", объясняет мою проблему.
Были введены методы по умолчанию, чтобы сделать реализации интерфейсов более гибкими - при изменении интерфейса он не обязательно требуется в реализующих классах для (пере) писать код. Поэтому, используя метод по умолчанию для интерфейса только для реализации метода во всех реализующих классах - цитата: "больше похож на хак для меня".
Пример моего экзамена:
Обзор классов:
- Элемент - абстрактный суперкласс всех элементов
- Вода - предмет, который расходуется
- Камень - Предмет, который не расходуется
- Расходуемые - Интерфейс с некоторыми методами для расходных элементов (эти методы должны быть переопределены всеми реализующими классами)
Сочетание этих:
Вода - предмет и реализует Расходуемые; Камень также является предметом и не реализует Расходуемые.
Мой экзамен
Я хотел бы реализовать метод, который все элементы должны реализовать. Поэтому я объявляю подпись в элементе класса.
protected abstract boolean isConsumable();
//return true if class implements (or rather "is consumable") Consumable and false in case it does not
Быстрое редактирование: я знаю, что instanceof может решить этот конкретный пример - если возможно, подумайте о более сложном примере, который делает необходимым внедрение метода в первую очередь. (Благодаря Sp00m и Евгению)
Теперь у меня есть несколько вариантов:
- Реализовать метод вручную в каждом подклассе Item (это невозможно сделать при масштабировании приложения).
Как упоминалось выше при масштабировании приложения это было бы непрактичным или крайне неэффективным.
- Внедрение метода внутри интерфейса в качестве метода по умолчанию, поэтому классы Consumable уже реализуют метод, который требуется элементу суперкласса.
Это решение, рекомендованное другим сообщением, я вижу преимущества его реализации таким образом:
Цитата - "Хорошая вещь об этой новой функции заключается в том, что, прежде чем вы были вынуждены использовать абстрактный класс для удобных методов, таким образом ограничивая внедрение на единое наследование, теперь вы можете иметь действительно чистый дизайн с помощью только интерфейса и минимум усилий по внедрению, наложенных на программиста ". Ссылка на сайт
Но, на мой взгляд, все еще кажется противоречивым оригинальной идее методов дефолта, о которых я упоминал в своем вступительном слове. Кроме того, при масштабировании приложения и внедрении большего количества методов, которые используют одну и ту же реализацию для всех Расходных материалов (как пример метода isConsumable()
), интерфейс будет реализовывать несколько методов по умолчанию, что противоречит идее интерфейса, не реализующего фактический метод.
- Представление суб-суперклассов вместо интерфейса - например, класса Consumable как абстрактного подкласса Item и суперкласса Water.
Он предлагает возможность написать случай по умолчанию для метода в Item (пример: isConsumable()//return false
), а затем переопределить это в суб-суперклассе. Проблема, которая возникает здесь: при масштабировании приложения и введении большего количества суб-суперклассов (как класс Consumable) фактические элементы начнут распространять более одного суб-суперкласса. Это может быть не плохо, потому что нужно также делать то же самое с интерфейсами, но это делает дерево наследования сложным. Пример. Теперь элемент может расширить субэлементный ALayer2, который является суб-суперклассом ALayer1, который расширяет Item (layer0),
- Представляем новый суперкласс (таким же слоем, как и Item) - например, класс Consumable как абстрактный класс, который будет другим суперклассом Water. Это означает, что Вода должна будет расширить Предмет и Расходуемые
Этот вариант обеспечивает гибкость. Можно создать совершенно новое дерево наследования для нового суперкласса, все еще имея возможность увидеть фактическое наследование Item. Но недостатком, который я обнаружил, является реализация этой структуры в действительных классах и использование их позже. Пример. Как бы я мог сказать: Расходуемый - это элемент, когда Расходуемый может иметь подклассы, которые не предназначены для элементов, Весь процесс преобразования может вызвать головную боль - скорее, чем структура варианта 3.
Вопрос
Каков был бы правильный вариант для реализации этой структуры?
- Это один из моих перечисленных вариантов?
- Это вариация?
- Или это еще один вариант, о котором я еще не думал?
Я выбрал очень простой пример - пожалуйста, сохраняйте масштабируемость для будущих реализаций при ответе. Спасибо за любую помощь заранее.
Изменить # 1
Java не допускает множественного наследования. Это повлияет на вариант 4. Использование нескольких интерфейсов (потому что вы можете реализовать более одного) может быть обходным путем, к сожалению, снова понадобится метод по умолчанию, который является именно тем видом реализации, который я пытался избежать изначально. Ссылка - проблема множественного наследования с возможным решением