Назови примеры качественного кода
Комментарии (2)
Ответ сгенерирован нейросетью и может содержать ошибки
Примеры качественного кода для Android-разработчика
Качественный код — это не просто работающий код, а читаемый, поддерживаемый, тестируемый и эффективный код. Вот ключевые примеры и принципы, которые я применяю за 10+ лет разработки под Android.
1. Читаемость и ясность
Код должен быть понятен другим разработчикам (и вам через полгода) без лишних комментариев.
Пример плохого кода:
fun p(u: User) {
if (u.a > 18 && u.s == "active") {
// ... 20 строк логики
}
}
Пример качественного кода:
fun processUserIfEligible(user: User) {
val isAdult = user.age > MIN_ADULT_AGE
val isActive = user.status == UserStatus.ACTIVE
if (isAdult && isActive) {
processEligibleUser(user)
}
}
private fun processEligibleUser(user: User) {
// Четкая, выделенная логика
validateUserData(user)
prepareUserForProcessing(user)
executeUserProcessing(user)
}
2. Соблюдение принципов SOLID
Особенно Single Responsibility и Dependency Inversion.
Пример качественной архитектуры:
// Вместо монолитного класса
interface UserRepository {
suspend fun getUserById(id: String): User
}
class UserRepositoryImpl @Inject constructor(
private val localDataSource: UserLocalDataSource,
private val remoteDataSource: UserRemoteDataSource,
private val dispatcher: CoroutineDispatcher = Dispatchers.IO
) : UserRepository {
override suspend fun getUserById(id: String): User {
return withContext(dispatcher) {
val localUser = localDataSource.getUserById(id)
if (localUser != null) {
return@withContext localUser
}
val remoteUser = remoteDataSource.getUserById(id)
localDataSource.saveUser(remoteUser)
remoteUser
}
}
}
3. Эффективное использование Kotlin
Использование возможностей языка для написания безопасного и выразительного кода.
Качественный пример с sealed классами и extension-функциями:
sealed class Result<out T> {
data class Success<T>(val data: T) : Result<T>()
data class Error(val exception: Exception) : Result<Nothing>()
object Loading : Result<Nothing>()
}
// Extension для удобной обработки
fun <T> Result<T>.onSuccess(action: (T) -> Unit): Result<T> {
if (this is Result.Success) {
action(data)
}
return this
}
// Использование в ViewModel
class UserViewModel @Inject constructor(
private val userRepository: UserRepository
) : ViewModel() {
private val _userState = MutableStateFlow<Result<User>>(Result.Loading)
val userState: StateFlow<Result<User>> = _userState
fun loadUser(userId: String) {
viewModelScope.launch {
_userState.value = Result.Loading
_userState.value = try {
Result.Success(userRepository.getUserById(userId))
} catch (e: Exception) {
Result.Error(e)
}
}
}
}
4. Корректная работа с асинхронностью
Использование Coroutines вместо callback hell.
Качественный пример обработки нескольких асинхронных операций:
class OrderProcessor @Inject constructor(
private val inventoryService: InventoryService,
private val paymentService: PaymentService,
private val shippingService: ShippingService
) {
suspend fun processOrder(order: Order): ProcessingResult {
return coroutineScope {
// Параллельное выполнение независимых операций
val inventoryDeferred = async { inventoryService.reserveItems(order.items) }
val paymentDeferred = async { paymentService.processPayment(order.payment) }
val inventoryResult = inventoryDeferred.await()
val paymentResult = paymentDeferred.await()
if (inventoryResult.isSuccess && paymentResult.isSuccess) {
val shippingResult = shippingService.scheduleDelivery(order)
ProcessingResult.Success(shippingResult.trackingId)
} else {
// Компенсирующие транзакции при ошибке
if (inventoryResult.isSuccess) {
async { inventoryService.releaseItems(order.items) }
}
ProcessingResult.Failure(listOf(inventoryResult.error, paymentResult.error))
}
}
}
}
5. Тестируемость
Код, который легко покрывать unit-тестами.
Пример тестируемого Presenter/ViewModel:
class LoginPresenter(
private val authRepository: AuthRepository,
private val validator: EmailValidator,
private val dispatcher: CoroutineDispatcher = Dispatchers.Main
) {
suspend fun login(email: String, password: String): LoginResult {
return withContext(dispatcher) {
if (!validator.isValid(email)) {
return@withContext LoginResult.InvalidEmail
}
try {
val user = authRepository.authenticate(email, password)
LoginResult.Success(user)
} catch (e: AuthException) {
LoginResult.Error(e.message ?: "Authentication failed")
}
}
}
}
// Соответствующий тест
@Test
fun `login with invalid email returns InvalidEmail`() = runTest {
// Arrange
val mockValidator = mock<EmailValidator> {
on { isValid(anyString()) } doReturn false
}
val presenter = LoginPresenter(mock(), mockValidator, UnconfinedTestDispatcher())
// Act
val result = presenter.login("invalid-email", "password")
// Assert
assertTrue(result is LoginResult.InvalidEmail)
}
6. Безопасность и обработка ошибок
Пример качественной обработки edge cases:
class ImageLoader @Inject constructor(
private val cache: LruCache<String, Bitmap>,
private val networkClient: NetworkClient,
private val bitmapDecoder: BitmapDecoder
) {
suspend fun loadImage(url: String, maxSize: Size): Result<Bitmap> {
return try {
// Проверка кэша
cache.get(url)?.let { cachedBitmap ->
return Result.success(scaleBitmap(cachedBitmap, maxSize))
}
// Загрузка с сети с таймаутом
val imageData = withTimeout(IMAGE_LOAD_TIMEOUT) {
networkClient.downloadImage(url)
}
// Декодирование с обработкой памяти
val options = BitmapFactory.Options().apply {
inJustDecodeBounds = true
bitmapDecoder.decode(imageData, this)
inSampleSize = calculateInSampleSize(this, maxSize)
inJustDecodeBounds = false
}
val bitmap = bitmapDecoder.decode(imageData, options)
?: return Result.failure(DecodingException("Failed to decode bitmap"))
// Кэширование с учетом ограничений
if (bitmap.allocationByteCount < MAX_CACHE_ITEM_SIZE) {
cache.put(url, bitmap)
}
Result.success(bitmap)
} catch (e: TimeoutCancellationException) {
Result.failure(NetworkTimeoutException("Image loading timeout", e))
} catch (e: IOException) {
Result.failure(NetworkException("Network error", e))
} catch (e: Exception) {
Result.failure(ImageLoadingException("Unexpected error", e))
}
}
}
7. Соблюдение Android Best Practices
Пример работы с Lifecycle:
class LocationManager(
private val context: Context,
private val lifecycleOwner: LifecycleOwner
) : DefaultLifecycleObserver {
private val fusedLocationClient = LocationServices.getFusedLocationProviderClient(context)
private var locationCallback: LocationCallback? = null
init {
lifecycleOwner.lifecycle.addObserver(this)
}
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
fun startLocationUpdates() {
val request = LocationRequest.create().apply {
interval = LOCATION_UPDATE_INTERVAL
priority = LocationRequest.PRIORITY_HIGH_ACCURACY
}
locationCallback = object : LocationCallback() {
override fun onLocationResult(result: LocationResult) {
handleNewLocation(result.lastLocation)
}
}
fusedLocationClient.requestLocationUpdates(
request,
locationCallback!!,
Looper.getMainLooper()
)
}
@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
fun stopLocationUpdates() {
locationCallback?.let {
fusedLocationClient.removeLocationUpdates(it)
locationCallback = null
}
}
override fun onDestroy(owner: LifecycleOwner) {
lifecycleOwner.lifecycle.removeObserver(this)
stopLocationUpdates()
}
}
Ключевые характеристики качественного кода:
- Самодокументируемость — имена переменных и функций ясно отражают их назначение
- Низкая связанность — компоненты минимально зависят друг от друга
- Высокая связность — каждый класс/метод выполняет одну четкую задачу
- Защита от null — использование Kotlin null-safety, Optional типов
- Правильное использование архитектурных компонентов — ViewModel, LiveData/Flow, Repository
- Энергоэффективность — правильная работа с фоновыми задачами, воркерами
- Безопасность — хранение чувствительных данных в EncryptedSharedPreferences, использование Security Crypto
Качественный код требует больше времени на написание, но экономит в десятки раз больше времени на поддержку, рефакторинг и добавление нового функционала. Он делает код базой для развития, а не препятствием для изменений.