Ответ
Используйте интерфейсы, когда нужно определить контракт для классов без привязки к их реализации. Основные случаи:
- Множественное наследование – класс может реализовать несколько интерфейсов.
- Слабосвязанный код – например, колбэки или слушатели:
interface OnItemClickListener {
fun onItemClick(position: Int)
}
- Тестирование – легко подменить реальную реализацию mock-объектом.
- API-контракты – когда разные команды работают над разными модулями.
Интерфейсы особенно полезны в архитектурных подходах типа Clean Architecture или MVP/MVVM для разделения слоёв.
Ответ 18+ 🔞
Слушай, вот реально часто вижу, как народ путается — когда классы, а когда интерфейсы впилить. Давай разжую, как есть, без этой вашей академической хуйни.
Представь, что интерфейс — это как контракт, бумажка с пунктами «ты обязан уметь делать: раз, два, три». А класс — это уже конкретный работяга, который этот контракт подписывает и говорит: «Окей, хуле, я эти три штуки делать умею, вот смотри как». И главная фишка в том, что один чувак может подписать несколько контрактов сразу. Это и есть твой первый кейс — множественное наследование. Наследоваться от десяти классов — это пиздец и говнокод, а реализовать десять интерфейсов — пожалуйста, раз плюнуть.
Второй момент, прям овердохуища важный — слабосвязанный код. Вот смотри, у тебя есть RecyclerView. Ему похуй, кто там будет клики обрабатывать. Его дело — показывать список. А ты ему говоришь: «Слушай, вот тебе объект, который реализует контракт OnItemClickListener. У него есть метод onItemClick(position: Int). Когда юзер тыкнет — дергай этот метод, а что внутри — тебя не ебёт».
interface OnItemClickListener {
fun onItemClick(position: Int)
}
// А дальше твой класс, хоть Activity, хоть Fragment, хоть ViewModel — подписывает контракт
class MyActivity : AppCompatActivity(), OnItemClickListener {
override fun onItemClick(position: Int) {
// Ну тут логика, открывай экран, покажи тост, ебашь в сеть — делай что хочешь
Toast.makeText(this, "Тыкнули на $position", Toast.LENGTH_SHORT).show()
}
}
Видишь? RecyclerView не привязан намертво к MyActivity. Ему доверия ебать ноль, он просто знает, что у переданного ему объекта есть нужный метод. Сегодня клик обрабатывает Activity, завтра вынесешь логику в Presenter — и хуй с ним, главное, контракт соблюдай.
Третий пункт — тестирование. Это вообще песня. Допустим, у тебя есть класс PaymentProcessor, который ходит в интернет за деньгами. В юнит-тестах тебе этот интернет нахуй не сдался, там нужно проверить бизнес-логику. Что делаешь? Выносишь контракт на работу с сетью в интерфейс, типа PaymentGateway.
В продакшене у тебя будет класс RealPaymentGateway, который идёт в банковский API. А в тестах ты подсунешь FakePaymentGateway, который просто возвращает «успех» или «неудача» по твоей указке. И твой PaymentProcessor нихуя не заметит подмены, потому что он работает с интерфейсом, а не с конкретной реализацией. Красота, я д р ё н а в о ш ь!
Ну и четвёртое — API-контракты для команд. Одна команда пишет модуль «Аналитики», другая — модуль «Платежей». Чтобы не орать друг на друга «а где твой метод, сука?!», они заранее договариваются об интерфейсе AnalyticsTracker. Первая команда начинает его реализовывать, вторая — использовать. Параллельно работать можно, главное, контракт не менять без веского повода, а то будет хиросима и нигерсраки.
И да, все эти модные архитектуры — Clean, MVP, MVVM — держится на этом как держится. Repository — это интерфейс. UseCase — это интерфейс. View в MVP — это интерфейс, который реализует твоя Activity или Fragment. Всё для того, чтобы можно было взъебнуть один кусок кода, не трогая другие. Связь слабая, тестируемость — огонь, а мозги не вытекают, когда через полгода нужно что-то поменять.
Короче, суть: хочешь определить способности — интерфейс. Хочешь реализовать способности и состояние — класс. Всё просто, ёпта.