Irbisa · Шпаргалка
ООП, SOLID и паттерны проектирования
ООП и SOLID в Kotlin, DRY, KISS и YAGNI, связность и зацепление, внедрение зависимостей, паттерны GoF и где их используют Android и его библиотеки, антипаттерны
Что означают DRY, KISS и YAGNI, и когда следование DRY делает код хуже?
// DRY: one rule, one home; the UI hint is derived from it, not copiedobject PasswordRule { const val MIN_LENGTH = 8 fun isValid(password: String) = password.length >= MIN_LENGTH val hint = "At least $MIN_LENGTH characters"}…Что такое связанность (coupling) и связность (cohesion), и почему стремятся к низкой связанности и высокой связности?
Что такое паттерн Observer, и в каких формах он встречается на Android?
fun interface CartListener { fun onCartChanged(count: Int)} class Cart { private val listeners = mutableListOf<CartListener>()…Что такое паттерн Strategy, и как его записать на Kotlin?
// a stateless one-method strategy: a function type is enoughtypealias Discount = (subtotalCents: Long) -> Long val noDiscount: Discount = { it }val blackFriday: Discount = { it * 70 / 100 }fun coupon(offCents: Long): Discount = { it - minOf(offCents, it / 5) } // at most 20% off…Что выведет этот код, и какой принцип SOLID нарушает
Square?open class Rectangle { open var width = 0 open var height = 0 val area get() = width * height} class Square : Rectangle() { override var width: Int get() = super.width set(value) { super.width = value; super.height = value } override var height: Int get() = super.height set(value) { super.width = value; super.height = value }} fun resize(r: Rectangle) { r.width = 5; r.height = 4; println(r.area) }fun main() { resize(Rectangle()); resize(Square()) }Как принцип разделения интерфейсов (Interface Segregation) применяется к листенерам и репозиториям на Android, и спасают ли толстый интерфейс методы по умолчанию?
// fat: a screen that only shows the profile also depends on uploads and account deletioninterface UserRepository { suspend fun profile(id: Long): Profile suspend fun updateName(id: Long, name: String) suspend fun uploadAvatar(id: Long, jpeg: ByteArray) suspend fun deleteAccount(id: Long)…Нужен ли каждому классу интерфейс? Когда оправдан интерфейс с единственной реализацией?
Чем фабричная функция отличается от Factory Method и Abstract Factory, и где Android использует каждую?
// factory function: named like the type, validates and hides the implementation classinterface Session { val userId: Long }private class ActiveSession(override val userId: Long) : Session fun Session(userId: Long): Session { require(userId > 0) { "invalid user id: $userId" }…Именованные аргументы и значения по умолчанию заменяют в Kotlin большинство Builder'ов. Когда Builder всё ещё уместен, и почему он остался в OkHttp и Retrofit?
class HttpConfig private constructor( val baseUrl: String, val timeoutMs: Long, val headers: Map<String, String>,) { fun newBuilder() = Builder(this) // derive a variant…Найдите баг: запросы картинок к стороннему CDN уносят access token пользователя.
val base = OkHttpClient.Builder() .connectTimeout(10, TimeUnit.SECONDS) .readTimeout(20, TimeUnit.SECONDS) val apiClient = base .addInterceptor(AuthInterceptor(tokens)) .build() // meant to be: the same timeouts, without authval cdnClient = base.build()Adapter, Decorator, Proxy и Facade все оборачивают другой объект. Как их различить, и является ли
RecyclerView.Adapterнастоящим Adapter?interface ImageSource { suspend fun load(url: String): ByteArray} // Adapter: a callback SDK made to fit the interface the app expectsclass SdkImageSource(private val sdk: LegacySdk) : ImageSource {…Что такое Flyweight и Object Pool, и зачем существуют
Drawable.mutate()иMessage.obtain()?// Flyweight: drawables from one resource share a ConstantState until mutate()val star = ContextCompat.getDrawable(context, R.drawable.ic_star)!!val dimmed = ContextCompat.getDrawable(context, R.drawable.ic_star)!! dimmed.alpha = 80 // wrong: star and every other ic_star fade toodimmed.mutate().alpha = 80 // right: dimmed gets its own copy of the state first…Как работает Chain of Responsibility, и как построить её на Kotlin, например для маршрутизации диплинков?
sealed interface Route { data class Product(val id: String) : Route data class Order(val id: Long) : Route data object Home : Route}…Что такое паттерн Template Method, почему
Activityобязана вызыватьsuper.onCreate(), и что заменило переопределение в новых API Android?sealed interface SyncResult { data object Skipped : SyncResult data class Done(val count: Int) : SyncResult} // the template: the order of steps is fixed in the base class…Что такое паттерн Command, и как с его помощью сделать undo в редакторе?
interface EditCommand { fun execute(doc: StringBuilder) fun undo(doc: StringBuilder)} class Insert(private val at: Int, private val text: String) : EditCommand {…Как написать конечный автомат на Kotlin с sealed-классами, и чем паттерн State отличается от Strategy?
sealed interface Upload { data object Idle : Upload data class Sending(val file: String, val attempt: Int) : Upload data class Done(val url: String) : Upload data class Failed(val file: String, val reason: String) : Upload}…Open/Closed в Kotlin: когда sealed-иерархия с
whenлучше полиморфного интерфейса, и почему Kotlin редко нужен Visitor?// open to new TYPES: another module adds a provider without touching this codeinterface PaymentProvider { val id: String fun feeCents(amountCents: Long): Long}…Каждый экран приложения наследует
BaseActivityглубиной в пять уровней. Что идёт не так, и как перевести это на композицию?// before: every screen inherits these checks, in super-call orderopen class BaseActivity : AppCompatActivity() { override fun onResume() { super.onResume() if (!session.isLoggedIn) startActivity(Intent(this, LoginActivity::class.java)) analytics.screen(javaClass.simpleName)…Какие антипаттерны вы ищете, когда получаете чужую Android-кодовую базу, и в каком порядке их рефакторите?