Введение
Я прочитал несколько постов о реализации интерфейсов и абстрактных классов здесь, на SO. Я нашел один, в частности, который я хотел бы связать здесь - Ссылка - интерфейс с методами по умолчанию против абстрактного класса , он охватывает тот же вопрос. Как принятый ответ, рекомендуется использовать методы интерфейсов по умолчанию, когда это возможно. Но комментарий под этим ответом о том, что «эта функция мне больше нравится», объясняет мою проблему.
Методы по умолчанию были введены для того, чтобы сделать реализации интерфейсов более гибкими - при изменении интерфейса не обязательно в реализующих классах (пере) писать код. Поэтому, используя метод интерфейса по умолчанию просто для реализации метода во всех реализующих классах - цитата: «мне больше кажется хаком».
Пример моего экзамена:
Обзор классов:
- Предмет - Абстрактный суперкласс всех предметов
- Вода - Предмет, который потребляется
- Камень - Предмет, который не является расходуемым
- Расходуемые - интерфейс с некоторыми методами для расходных материалов (эти методы должны быть переопределены всеми реализующими классами)
Объединение этих:
Вода является Предметом и реализует Расходуемый; Камень также является Предметом и не реализует Расходуемый.
Мой экзамен
Я хотел бы реализовать метод, который должны реализовывать все Предметы. Поэтому я объявляю подпись в классе Item.
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.
Использование нескольких интерфейсов (потому что вы можете реализовать более одного) может быть обходным путем, к сожалению, метод default будет снова необходим, и это именно та реализация, которую я пытался избежать изначально. Ссылка - проблема множественного наследования с возможным решением