mirror of
https://github.com/affaan-m/everything-claude-code.git
synced 2026-04-03 23:53:29 +08:00
* Add Turkish (tr) docs and update README Add a full set of Turkish documentation under docs/tr (agents, changelog, CLAUDE guide, contributing, code of conduct, and many agents/commands/skills/rules files). Update README to include a link to the Turkish docs and increment the supported language count from 5 to 6. This commit adds localized guidance and references to help Turkish-speaking contributors and users. * Update docs/tr/TROUBLESHOOTING.md Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com> * Update docs/tr/README.md Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com> * docs(tr): fix license link and update readmes Update Turkish docs: change license badge link to point to repository root (../../LICENSE), increment displayed language count from 5 to 6, and remove two outdated related links from docs/tr/examples/README.md to keep references accurate. * Update docs/tr/commands/instinct-import.md Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com> * Update docs/tr/commands/checkpoint.md Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com> --------- Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
536 lines
15 KiB
Markdown
536 lines
15 KiB
Markdown
---
|
||
name: kotlin-patterns
|
||
description: Coroutine'ler, null safety ve DSL builder'lar ile sağlam, verimli ve sürdürülebilir Kotlin uygulamaları oluşturmak için idiomatic Kotlin kalıpları, en iyi uygulamalar ve konvansiyonlar.
|
||
origin: ECC
|
||
---
|
||
|
||
# Kotlin Geliştirme Kalıpları
|
||
|
||
Sağlam, verimli ve sürdürülebilir uygulamalar oluşturmak için idiomatic Kotlin kalıpları ve en iyi uygulamalar.
|
||
|
||
## Ne Zaman Kullanılır
|
||
|
||
- Yeni Kotlin kodu yazarken
|
||
- Kotlin kodunu incelerken
|
||
- Mevcut Kotlin kodunu refactor ederken
|
||
- Kotlin modülleri veya kütüphaneleri tasarlarken
|
||
- Gradle Kotlin DSL build'lerini yapılandırırken
|
||
|
||
## Nasıl Çalışır
|
||
|
||
Bu skill yedi temel alanda idiomatic Kotlin konvansiyonlarını uygular: tip sistemi ve safe-call operatörleri kullanarak null safety, `val` ve data class'larda `copy()` ile immutability, exhaustive tip hiyerarşileri için sealed class'lar ve interface'ler, coroutine'ler ve `Flow` ile yapılandırılmış eşzamanlılık, inheritance olmadan davranış eklemek için extension fonksiyonlar, `@DslMarker` ve lambda receiver'lar kullanarak tip güvenli DSL builder'lar, ve build yapılandırması için Gradle Kotlin DSL.
|
||
|
||
## Örnekler
|
||
|
||
**Elvis operatörü ile null safety:**
|
||
```kotlin
|
||
fun getUserEmail(userId: String): String {
|
||
val user = userRepository.findById(userId)
|
||
return user?.email ?: "unknown@example.com"
|
||
}
|
||
```
|
||
|
||
**Exhaustive sonuçlar için sealed class:**
|
||
```kotlin
|
||
sealed class Result<out T> {
|
||
data class Success<T>(val data: T) : Result<T>()
|
||
data class Failure(val error: AppError) : Result<Nothing>()
|
||
data object Loading : Result<Nothing>()
|
||
}
|
||
```
|
||
|
||
**async/await ile yapılandırılmış eşzamanlılık:**
|
||
```kotlin
|
||
suspend fun fetchUserWithPosts(userId: String): UserProfile =
|
||
coroutineScope {
|
||
val user = async { userService.getUser(userId) }
|
||
val posts = async { postService.getUserPosts(userId) }
|
||
UserProfile(user = user.await(), posts = posts.await())
|
||
}
|
||
```
|
||
|
||
## Temel İlkeler
|
||
|
||
### 1. Null Safety
|
||
|
||
Kotlin'in tip sistemi nullable ve non-nullable tipleri ayırır. Tam olarak kullanın.
|
||
|
||
```kotlin
|
||
// İyi: Varsayılan olarak non-nullable tipler kullan
|
||
fun getUser(id: String): User {
|
||
return userRepository.findById(id)
|
||
?: throw UserNotFoundException("User $id not found")
|
||
}
|
||
|
||
// İyi: Safe call'lar ve Elvis operatörü
|
||
fun getUserEmail(userId: String): String {
|
||
val user = userRepository.findById(userId)
|
||
return user?.email ?: "unknown@example.com"
|
||
}
|
||
|
||
// Kötü: Nullable tipleri zorla açma
|
||
fun getUserEmail(userId: String): String {
|
||
val user = userRepository.findById(userId)
|
||
return user!!.email // null ise NPE fırlatır
|
||
}
|
||
```
|
||
|
||
### 2. Varsayılan Olarak Immutability
|
||
|
||
`var` yerine `val` tercih edin, mutable koleksiyonlar yerine immutable olanları.
|
||
|
||
```kotlin
|
||
// İyi: Immutable veri
|
||
data class User(
|
||
val id: String,
|
||
val name: String,
|
||
val email: String,
|
||
)
|
||
|
||
// İyi: copy() ile dönüştürme
|
||
fun updateEmail(user: User, newEmail: String): User =
|
||
user.copy(email = newEmail)
|
||
|
||
// İyi: Immutable koleksiyonlar
|
||
val users: List<User> = listOf(user1, user2)
|
||
val filtered = users.filter { it.email.isNotBlank() }
|
||
|
||
// Kötü: Mutable state
|
||
var currentUser: User? = null // Mutable global state'ten kaçın
|
||
val mutableUsers = mutableListOf<User>() // Gerçekten gerekmedikçe kaçın
|
||
```
|
||
|
||
### 3. Expression Body'ler ve Tek İfadeli Fonksiyonlar
|
||
|
||
Kısa, okunabilir fonksiyonlar için expression body'ler kullanın.
|
||
|
||
```kotlin
|
||
// İyi: Expression body
|
||
fun isAdult(age: Int): Boolean = age >= 18
|
||
|
||
fun formatFullName(first: String, last: String): String =
|
||
"$first $last".trim()
|
||
|
||
fun User.displayName(): String =
|
||
name.ifBlank { email.substringBefore('@') }
|
||
|
||
// İyi: Expression olarak when
|
||
fun statusMessage(code: Int): String = when (code) {
|
||
200 -> "OK"
|
||
404 -> "Not Found"
|
||
500 -> "Internal Server Error"
|
||
else -> "Unknown status: $code"
|
||
}
|
||
|
||
// Kötü: Gereksiz block body
|
||
fun isAdult(age: Int): Boolean {
|
||
return age >= 18
|
||
}
|
||
```
|
||
|
||
### 4. Value Objeler İçin Data Class'lar
|
||
|
||
Öncelikle veri tutan tipler için data class'lar kullanın.
|
||
|
||
```kotlin
|
||
// İyi: copy, equals, hashCode, toString ile data class
|
||
data class CreateUserRequest(
|
||
val name: String,
|
||
val email: String,
|
||
val role: Role = Role.USER,
|
||
)
|
||
|
||
// İyi: Tip güvenliği için value class (runtime'da sıfır maliyet)
|
||
@JvmInline
|
||
value class UserId(val value: String) {
|
||
init {
|
||
require(value.isNotBlank()) { "UserId cannot be blank" }
|
||
}
|
||
}
|
||
|
||
@JvmInline
|
||
value class Email(val value: String) {
|
||
init {
|
||
require('@' in value) { "Invalid email: $value" }
|
||
}
|
||
}
|
||
|
||
fun getUser(id: UserId): User = userRepository.findById(id)
|
||
```
|
||
|
||
## Sealed Class'lar ve Interface'ler
|
||
|
||
### Kısıtlı Hiyerarşileri Modelleme
|
||
|
||
```kotlin
|
||
// İyi: Exhaustive when için sealed class
|
||
sealed class Result<out T> {
|
||
data class Success<T>(val data: T) : Result<T>()
|
||
data class Failure(val error: AppError) : Result<Nothing>()
|
||
data object Loading : Result<Nothing>()
|
||
}
|
||
|
||
fun <T> Result<T>.getOrNull(): T? = when (this) {
|
||
is Result.Success -> data
|
||
is Result.Failure -> null
|
||
is Result.Loading -> null
|
||
}
|
||
|
||
fun <T> Result<T>.getOrThrow(): T = when (this) {
|
||
is Result.Success -> data
|
||
is Result.Failure -> throw error.toException()
|
||
is Result.Loading -> throw IllegalStateException("Still loading")
|
||
}
|
||
```
|
||
|
||
### API Yanıtları İçin Sealed Interface'ler
|
||
|
||
```kotlin
|
||
sealed interface ApiError {
|
||
val message: String
|
||
|
||
data class NotFound(override val message: String) : ApiError
|
||
data class Unauthorized(override val message: String) : ApiError
|
||
data class Validation(
|
||
override val message: String,
|
||
val field: String,
|
||
) : ApiError
|
||
data class Internal(
|
||
override val message: String,
|
||
val cause: Throwable? = null,
|
||
) : ApiError
|
||
}
|
||
|
||
fun ApiError.toStatusCode(): Int = when (this) {
|
||
is ApiError.NotFound -> 404
|
||
is ApiError.Unauthorized -> 401
|
||
is ApiError.Validation -> 422
|
||
is ApiError.Internal -> 500
|
||
}
|
||
```
|
||
|
||
## Scope Fonksiyonlar
|
||
|
||
### Her Birini Ne Zaman Kullanmalı
|
||
|
||
```kotlin
|
||
// let: Nullable'ı veya scope edilmiş sonucu dönüştür
|
||
val length: Int? = name?.let { it.trim().length }
|
||
|
||
// apply: Bir nesneyi yapılandır (nesneyi döndürür)
|
||
val user = User().apply {
|
||
name = "Alice"
|
||
email = "alice@example.com"
|
||
}
|
||
|
||
// also: Yan etkiler (nesneyi döndürür)
|
||
val user = createUser(request).also { logger.info("Created user: ${it.id}") }
|
||
|
||
// run: Receiver ile block çalıştır (sonucu döndürür)
|
||
val result = connection.run {
|
||
prepareStatement(sql)
|
||
executeQuery()
|
||
}
|
||
|
||
// with: run'ın extension olmayan formu
|
||
val csv = with(StringBuilder()) {
|
||
appendLine("name,email")
|
||
users.forEach { appendLine("${it.name},${it.email}") }
|
||
toString()
|
||
}
|
||
```
|
||
|
||
## Extension Fonksiyonlar
|
||
|
||
### Inheritance Olmadan Fonksiyonalite Ekleme
|
||
|
||
```kotlin
|
||
// İyi: Domain'e özgü extension'lar
|
||
fun String.toSlug(): String =
|
||
lowercase()
|
||
.replace(Regex("[^a-z0-9\\s-]"), "")
|
||
.replace(Regex("\\s+"), "-")
|
||
.trim('-')
|
||
|
||
fun Instant.toLocalDate(zone: ZoneId = ZoneId.systemDefault()): LocalDate =
|
||
atZone(zone).toLocalDate()
|
||
|
||
// İyi: Koleksiyon extension'ları
|
||
fun <T> List<T>.second(): T = this[1]
|
||
|
||
fun <T> List<T>.secondOrNull(): T? = getOrNull(1)
|
||
|
||
// İyi: Scope edilmiş extension'lar (global namespace'i kirletmez)
|
||
class UserService {
|
||
private fun User.isActive(): Boolean =
|
||
status == Status.ACTIVE && lastLogin.isAfter(Instant.now().minus(30, ChronoUnit.DAYS))
|
||
|
||
fun getActiveUsers(): List<User> = userRepository.findAll().filter { it.isActive() }
|
||
}
|
||
```
|
||
|
||
## Coroutine'ler
|
||
|
||
### Yapılandırılmış Eşzamanlılık
|
||
|
||
```kotlin
|
||
// İyi: coroutineScope ile yapılandırılmış eşzamanlılık
|
||
suspend fun fetchUserWithPosts(userId: String): UserProfile =
|
||
coroutineScope {
|
||
val userDeferred = async { userService.getUser(userId) }
|
||
val postsDeferred = async { postService.getUserPosts(userId) }
|
||
|
||
UserProfile(
|
||
user = userDeferred.await(),
|
||
posts = postsDeferred.await(),
|
||
)
|
||
}
|
||
|
||
// İyi: child'lar bağımsız başarısız olabildiğinde supervisorScope
|
||
suspend fun fetchDashboard(userId: String): Dashboard =
|
||
supervisorScope {
|
||
val user = async { userService.getUser(userId) }
|
||
val notifications = async { notificationService.getRecent(userId) }
|
||
val recommendations = async { recommendationService.getFor(userId) }
|
||
|
||
Dashboard(
|
||
user = user.await(),
|
||
notifications = try {
|
||
notifications.await()
|
||
} catch (e: CancellationException) {
|
||
throw e
|
||
} catch (e: Exception) {
|
||
emptyList()
|
||
},
|
||
recommendations = try {
|
||
recommendations.await()
|
||
} catch (e: CancellationException) {
|
||
throw e
|
||
} catch (e: Exception) {
|
||
emptyList()
|
||
},
|
||
)
|
||
}
|
||
```
|
||
|
||
### Reactive Stream'ler İçin Flow
|
||
|
||
```kotlin
|
||
// İyi: Uygun hata işleme ile cold flow
|
||
fun observeUsers(): Flow<List<User>> = flow {
|
||
while (currentCoroutineContext().isActive) {
|
||
val users = userRepository.findAll()
|
||
emit(users)
|
||
delay(5.seconds)
|
||
}
|
||
}.catch { e ->
|
||
logger.error("Error observing users", e)
|
||
emit(emptyList())
|
||
}
|
||
|
||
// İyi: Flow operatörleri
|
||
fun searchUsers(query: Flow<String>): Flow<List<User>> =
|
||
query
|
||
.debounce(300.milliseconds)
|
||
.distinctUntilChanged()
|
||
.filter { it.length >= 2 }
|
||
.mapLatest { q -> userRepository.search(q) }
|
||
.catch { emit(emptyList()) }
|
||
```
|
||
|
||
## DSL Builder'lar
|
||
|
||
### Tip Güvenli Builder'lar
|
||
|
||
```kotlin
|
||
// İyi: @DslMarker ile DSL
|
||
@DslMarker
|
||
annotation class HtmlDsl
|
||
|
||
@HtmlDsl
|
||
class HTML {
|
||
private val children = mutableListOf<Element>()
|
||
|
||
fun head(init: Head.() -> Unit) {
|
||
children += Head().apply(init)
|
||
}
|
||
|
||
fun body(init: Body.() -> Unit) {
|
||
children += Body().apply(init)
|
||
}
|
||
|
||
override fun toString(): String = children.joinToString("\n")
|
||
}
|
||
|
||
fun html(init: HTML.() -> Unit): HTML = HTML().apply(init)
|
||
|
||
// Kullanım
|
||
val page = html {
|
||
head { title("My Page") }
|
||
body {
|
||
h1("Welcome")
|
||
p("Hello, World!")
|
||
}
|
||
}
|
||
```
|
||
|
||
## Gradle Kotlin DSL
|
||
|
||
### build.gradle.kts Yapılandırması
|
||
|
||
```kotlin
|
||
// En son versiyonları kontrol et: https://kotlinlang.org/docs/releases.html
|
||
plugins {
|
||
kotlin("jvm") version "2.3.10"
|
||
kotlin("plugin.serialization") version "2.3.10"
|
||
id("io.ktor.plugin") version "3.4.0"
|
||
id("org.jetbrains.kotlinx.kover") version "0.9.7"
|
||
id("io.gitlab.arturbosch.detekt") version "1.23.8"
|
||
}
|
||
|
||
group = "com.example"
|
||
version = "1.0.0"
|
||
|
||
kotlin {
|
||
jvmToolchain(21)
|
||
}
|
||
|
||
dependencies {
|
||
// Ktor
|
||
implementation("io.ktor:ktor-server-core:3.4.0")
|
||
implementation("io.ktor:ktor-server-netty:3.4.0")
|
||
implementation("io.ktor:ktor-server-content-negotiation:3.4.0")
|
||
implementation("io.ktor:ktor-serialization-kotlinx-json:3.4.0")
|
||
|
||
// Exposed
|
||
implementation("org.jetbrains.exposed:exposed-core:1.0.0")
|
||
implementation("org.jetbrains.exposed:exposed-dao:1.0.0")
|
||
implementation("org.jetbrains.exposed:exposed-jdbc:1.0.0")
|
||
implementation("org.jetbrains.exposed:exposed-kotlin-datetime:1.0.0")
|
||
|
||
// Koin
|
||
implementation("io.insert-koin:koin-ktor:4.2.0")
|
||
|
||
// Coroutines
|
||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.2")
|
||
|
||
// Test
|
||
testImplementation("io.kotest:kotest-runner-junit5:6.1.4")
|
||
testImplementation("io.kotest:kotest-assertions-core:6.1.4")
|
||
testImplementation("io.kotest:kotest-property:6.1.4")
|
||
testImplementation("io.mockk:mockk:1.14.9")
|
||
testImplementation("io.ktor:ktor-server-test-host:3.4.0")
|
||
testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.10.2")
|
||
}
|
||
|
||
tasks.withType<Test> {
|
||
useJUnitPlatform()
|
||
}
|
||
|
||
detekt {
|
||
config.setFrom(files("config/detekt/detekt.yml"))
|
||
buildUponDefaultConfig = true
|
||
}
|
||
```
|
||
|
||
## Hata İşleme Kalıpları
|
||
|
||
### Domain Operasyonları İçin Result Tipi
|
||
|
||
```kotlin
|
||
// İyi: Kotlin'in Result'ını veya özel sealed class kullan
|
||
suspend fun createUser(request: CreateUserRequest): Result<User> = runCatching {
|
||
require(request.name.isNotBlank()) { "Name cannot be blank" }
|
||
require('@' in request.email) { "Invalid email format" }
|
||
|
||
val user = User(
|
||
id = UserId(UUID.randomUUID().toString()),
|
||
name = request.name,
|
||
email = Email(request.email),
|
||
)
|
||
userRepository.save(user)
|
||
user
|
||
}
|
||
|
||
// İyi: Result'ları zincirle
|
||
val displayName = createUser(request)
|
||
.map { it.name }
|
||
.getOrElse { "Unknown" }
|
||
```
|
||
|
||
### require, check, error
|
||
|
||
```kotlin
|
||
// İyi: Net mesajlarla ön koşullar
|
||
fun withdraw(account: Account, amount: Money): Account {
|
||
require(amount.value > 0) { "Amount must be positive: $amount" }
|
||
check(account.balance >= amount) { "Insufficient balance: ${account.balance} < $amount" }
|
||
|
||
return account.copy(balance = account.balance - amount)
|
||
}
|
||
```
|
||
|
||
## Hızlı Referans: Kotlin İdiyomları
|
||
|
||
| İdiyom | Açıklama |
|
||
|-------|-------------|
|
||
| `val` over `var` | Immutable değişkenleri tercih et |
|
||
| `data class` | equals/hashCode/copy ile value objeler için |
|
||
| `sealed class/interface` | Kısıtlı tip hiyerarşileri için |
|
||
| `value class` | Sıfır maliyetli tip güvenli sarmalayıcılar için |
|
||
| Expression `when` | Exhaustive pattern matching |
|
||
| Safe call `?.` | Null-safe member erişimi |
|
||
| Elvis `?:` | Nullable'lar için varsayılan değer |
|
||
| `let`/`apply`/`also`/`run`/`with` | Temiz kod için scope fonksiyonlar |
|
||
| Extension fonksiyonlar | Inheritance olmadan davranış ekle |
|
||
| `copy()` | Data class'larda immutable güncellemeler |
|
||
| `require`/`check` | Ön koşul assertion'ları |
|
||
| Coroutine `async`/`await` | Yapılandırılmış concurrent execution |
|
||
| `Flow` | Cold reactive stream'ler |
|
||
| `sequence` | Lazy evaluation |
|
||
| Delegation `by` | Inheritance olmadan implementasyonu yeniden kullan |
|
||
|
||
## Kaçınılması Gereken Anti-Kalıplar
|
||
|
||
```kotlin
|
||
// Kötü: Nullable tipleri zorla açma
|
||
val name = user!!.name
|
||
|
||
// Kötü: Java'dan platform tipi sızıntısı
|
||
fun getLength(s: String) = s.length // Güvenli
|
||
fun getLength(s: String?) = s?.length ?: 0 // Java'dan null'ları işle
|
||
|
||
// Kötü: Mutable data class'lar
|
||
data class MutableUser(var name: String, var email: String)
|
||
|
||
// Kötü: Kontrol akışı için exception kullanma
|
||
try {
|
||
val user = findUser(id)
|
||
} catch (e: NotFoundException) {
|
||
// Beklenen durumlar için exception kullanma
|
||
}
|
||
|
||
// İyi: Nullable dönüş veya Result kullan
|
||
val user: User? = findUserOrNull(id)
|
||
|
||
// Kötü: Coroutine scope'u görmezden gelme
|
||
GlobalScope.launch { /* GlobalScope'tan kaçın */ }
|
||
|
||
// İyi: Yapılandırılmış eşzamanlılık kullan
|
||
coroutineScope {
|
||
launch { /* Uygun şekilde scope edilmiş */ }
|
||
}
|
||
|
||
// Kötü: Derin iç içe scope fonksiyonlar
|
||
user?.let { u ->
|
||
u.address?.let { a ->
|
||
a.city?.let { c -> process(c) }
|
||
}
|
||
}
|
||
|
||
// İyi: Doğrudan null-safe zincir
|
||
user?.address?.city?.let { process(it) }
|
||
```
|
||
|
||
**Hatırla**: Kotlin kodu kısa ama okunabilir olmalı. Güvenlik için tip sisteminden yararlanın, immutability tercih edin ve eşzamanlılık için coroutine'ler kullanın. Şüpheye düştüğünüzde, derleyicinin size yardım etmesine izin verin.
|