SOLID — 2000’li yılların başında Robert C. Martin (Uncle Bob) tarafından formüle edilen beş nesne yönelimli programlama ilkesidir. DigitalOcean, 2024’e göre, SOLID Single Responsibility, Open-Closed, Liskov Substitution, Interface Segregation ve Dependency Inversion anlamına gelir. Bu prensipler Clean Architecture’ın temelini oluşturur ve Android geliştirmede (MVP, MVVM, Clean Architecture) ve iOS’ta (VIPER, TCA) uygulanır.
Ana Noktalar
SOLID — nesne yönelimli tasarımın beş ilkesini temsil eden anımsatıcı bir kısaltmadır. Terim, Robert C. Martin tarafından «Design Principles and Design Patterns» (2000) makalesinde tanıtılmış ve daha sonra «Agile Software Development: Principles, Patterns, and Practices» (2002) kitabında popüler hale getirilmiştir. SOLID bir framework veya kütüphane değildir — kodu daha az bağımlı, daha test edilebilir ve değiştirmesi daha kolay hale getiren bir uygulamalar bütünüdür.
Clean Coder Blog, 2014’e göre, her SOLID prensibi belirli bir tasarım sorununu çözer: SRP God sınıflarıyla savaşır, OCP basamaklı değişiklikleri önler, LSP yanlış kalıtıma karşı korur, ISP büyük arayüzlerden kaçınır ve DIP güçlü bağlılığı azaltır. Birlikte, Android projelerinde MVP, MVVM ve MVI ile kullanılan Clean Architecture’ın temelini oluştururlar.
Single Responsibility Principle (SRP) — tek sorumluluk prensibi. Formülasyon: «Bir sınıfın değiştirmek için yalnızca bir nedeni olmalıdır». Bu, her modül veya sınıfın tam olarak tek bir işlevsellik veya tek bir alan varlığından sorumlu olduğu anlamına gelir. Bir sınıf hem kullanıcıları hem de e-posta göndermeyi yönetiyorsa — değiştirmek için iki nedeni vardır, bu da SRP’yi ihlal eder.
Robert C. Martin, 2002’ye göre, SRP en önemli ve aynı zamanda en çok ihlal edilen prensiptir. Mobil geliştirmede, SRP genellikle Activity/Fragment’te UI mantığı, navigasyon, ağ ve iş mantığını birleştirerek ihlal edilir. Çözüm, her katmanı ayrı bir sınıfa çıkarmaktır: UI mantığı için ViewModel, veriler için Repository, navigasyon için NavController.
Profil yükleyen, ayarları kaydeden ve e-posta gönderen UserManager sınıfını düşünün. Bunlar üç farklı sorumluluktur ve her biri ayrı bir sınıfa çıkarılmalıdır: UserProfileRepository (yükleme), UserSettingsStorage (kaydetme) ve EmailService (gönderme). İstemci kodu (ViewModel) üçünü de Dependency Injection aracılığıyla kullanır ve her sınıf bağımsız olarak kolayca test edilebilir ve diğerlerini etkilemeden değiştirilebilir.
// ❌ SRP İhlali: Activity ağ, DB ve UI hakkında bilgi sahibi
class ProfileActivity : AppCompatActivity() {
fun loadProfile() {
api.getUser() // Ağ çağrısı
db.saveUser() // DB işlemi
updateUI() // UI güncellemesi
}
}
// ✅ SRP uygulandı: katmanlar ayrıldı
class ProfileViewModel : ViewModel() {
private val repo = UserRepository()
fun loadProfile() { repo.getUser() }
}
SRP İhlali Belirtileri: 200 satırdan fazla sınıf, farklı alanlardan yöntemler, farklı nedenlerle sık değişiklik. Android geliştirme için kural basittir: Activity yalnızca ekran yaşam döngüsünü yönetir, ViewModel UI durumunu yönetir, Repository veri kaynaklarını yönetir.
SRP prensibi yalnızca sınıflar için değil, aynı zamanda hizmet düzeyi mimarisi için de geçerlidir. Her mikroservis bir alan varlığını yönetir: UserService — yalnızca kullanıcılar, PaymentService — yalnızca ödemeler, NotificationService — yalnızca bildirimler. Bu, hizmetlerin bağımsız olarak ölçeklenmesini, dağıtılmasını ve test edilmesini sağlar. Mobil uygulamalarda, mikroservis düzeyinde SRP, API istemcilerinin alana göre ayrılmasıyla kendini gösterir.
Open-Closed Principle (OCP) — sınıflar genişletmeye açık (yeni davranış eklenebilir) ve değişikliğe kapalı (mevcut kod değiştirilmez) olmalıdır. Bu, çok biçimlilik, soyut sınıflar ve arayüzler aracılığıyla elde edilir. Mevcut bir yönteme if-else eklemek yerine, yeni bir arayüz uygulaması oluşturulur.
Clean Coder Blog, 2014’e göre, OCP en iyi Strategy deseniyle çalışır. Örneğin, bir uygulama farklı ödeme yöntemlerini (Google Pay, Apple Pay, PayPal) destekliyorsa, ödeme işlemcisine switch-case eklemek gerekmez. Her ödeme yöntemi ortak bir PaymentGateway arayüzünü uygular ve yeni bir ödeme sistemi, mevcut kodu değiştirmeden yeni bir sınıf olarak eklenir.
// ✅ OCP: genişletmeye açık, değişikliğe kapalı
interface PaymentGateway {
fun processPayment(amount: Double): Boolean
}
class GooglePayGateway : PaymentGateway {
override fun processPayment(amount: Double) = true
}
// Yeni ödeme sistemi — mevcut kodu değiştirmeden
class ApplePayGateway : PaymentGateway {
override fun processPayment(amount: Double) = true
}
Liskov Substitution Principle (LSP) — Barbara Liskov’un yer değiştirme prensibi. S, T’nin bir alt türüyse, T türündeki nesneler, program özelliklerini değiştirmeden S türündeki nesnelerle değiştirilebilir. Resmi olarak: bir temel sınıfı kullanan bir işlev, herhangi bir alt sınıfıyla doğru çalışmalıdır. Bir alt sınıf, temel sınıfın fırlatmadığı bir yerde istisna fırlatıyorsa — LSP ihlal edilmiştir.
Robert C. Martin, 2002’ye göre, LSP anlaşılması en zor SOLID prensibidir. Klasik ihlal örneği, Rectangle’dan miras alan Square sınıfıdır. Square’de setWidth hem genişliği hem de yüksekliği ayarlıyorsa, Rectangle davranışı bekleyen istemci kodu beklenmeyen bir sonuç alır. Mobil geliştirmede, LSP genellikle ViewModel’i miras alırken ihlal edilir — bir alt ViewModel zorunlu bağımlılıklar eklediğinde.
// ❌ LSP İhlali: Square, Rectangle’ın davranışını bozuyor
open class Rectangle(open var width: Int, open var height: Int)
class Square(side: Int) : Rectangle(side, side) {
override var width
get() = super.width
set(value) { super.setBoth(value, value) }
}
Interface Segregation Principle (ISP) — istemciler kullanmadıkları arayüzlere bağımlı olmamalıdır. Tek bir «şişman» arayüz yerine, birkaç dar ve özelleşmiş arayüz oluşturun. Bir sınıf bir arayüzü uyguluyor ancak bazı yöntemler UnsupportedOperationException fırlatıyor veya boş kalıyorsa — bu ISP ihlalinin açık bir işaretidir.
DigitalOcean, 2024’e göre, ViewModel ve Repository tasarlarken mobil geliştirmede ISP özellikle önemlidir. Tüm CRUD yöntemlerine sahip tek bir UserRepository arayüzü yerine, QueryUserRepository (salt okunur) ve CommandUserRepository (yazma) oluşturmak daha iyidir. Böylece salt okunur bir istemci (UI öğesi) yalnızca Query arayüzüne bağımlı olur ve yazma yöntemleri hakkında hiçbir şey bilmez.
// ❌ Şişman arayüz — istemci gereksiz yöntemleri uygulamak zorunda
interface UserOperations {
fun getUser(id: String): User
fun saveUser(user: User)
fun deleteUser(id: String)
fun exportUsers(): File
}
// ✅ ISP: ayrılmış arayüzler
interface UserReader { fun getUser(id: String): User }
interface UserWriter { fun saveUser(user: User) }
interface UserDeleter { fun deleteUser(id: String) }
Dependency Inversion Principle (DIP) — üst düzey modüller alt düzey modüllere bağımlı olmamalıdır. Her ikisi de soyutlamalara (arayüzlere) bağımlı olmalıdır. Soyutlamalar ayrıntılara bağımlı olmamalıdır — ayrıntılar soyutlamalara bağımlı olmalıdır. Bu «Dependency Injection» (DI) değildir, ancak DI, DIP’yi uygulamanın yaygın bir yoludur.
Robert C. Martin, 2019’a göre, DIP Clean Architecture’ın temelidir. ViewModel (üst düzey) doğrudan bir RetrofitApi örneği (ayrıntı) oluşturmamalıdır. Bunun yerine, ViewModel bir UserRepository arayüzüne bağımlıdır ve Retrofit ile somut UserRepositoryImpl kurucu aracılığıyla iletilir. Android’de DIP, Hilt/Dagger veya Koin aracılığıyla uygulanır: tüm bağımlılıklar DI kapsayıcısı aracılığıyla sağlanır.
// ✅ DIP: Module soyutlamaya bağımlı, ayrıntılara değil
class UserRepositoryImpl(
private val api: UserApi, // Arayüze bağımlı
private val db: UserDao // Arayüze bağımlı
) : UserRepository {
override suspend fun getUser(id: String): User {
return api.fetchUser(id)
}
}
// Hilt DI: ayrıntılar DI modülü aracılığıyla bağlanır
@Module
object NetworkModule {
@Provides
fun provideUserApi(retrofit: Retrofit): UserApi =
retrofit.create(UserApi::class.java)
}
Mobil geliştirmede SOLID tüm seviyelerde uygulanır: uygulama mimarisinden bireysel sınıflara kadar. Android projelerinde Clean Architecture, kodu üç katmana ayırır: domain (iş mantığı — framework’lerden bağımsız), data (depolar, API, DB) ve presentation (UI, ViewModel). Domain katmanı SOLID prensiplerini kullanır: kullanım durumları (SRP), depo arayüzleri (DIP), varlık sınıfları (OCP + LSP).
Android Developers Guide, 2025’e göre, Android’de SRP, ViewModel, Repository ve Mapper’ın ayrılmasıyla kendini gösterir. OCP — DataSource arayüzü aracılığıyla yeni veri kaynakları eklerken. LSP — farklı depolarda tek tip Result işleme. ISP — CQRS yaklaşımında (Okuma/Yazma depolarını ayırma). DIP — bağımlılık enjeksiyonu için Hilt/Koin aracılığıyla.
| Prensip | Olmadan Sorun | Mobil Projede Çözüm |
|---|---|---|
| SRP | 1000+ satırlık Activity | ViewModel + UseCase + Repository |
| OCP | Ödeme türüne göre switch-case | Strategy: PaymentGateway arayüzü |
| LSP | BaseViewModel değiştirirken hata | Alt sınıf sözleşmesini kontrol et |
| ISP | UnsupportedOperationException | Reader / Writer ayırma |
| DIP | ViewModel manuel olarak Retrofit oluşturur | Hilt / Koin DI kapsayıcısı |
SOLID hataları çoğunlukla kodun aşırı karmaşık hale getirilmesiyle ilgilidir. İlki — prensipleri bağlamı göz önünde bulundurmadan harfiyen takip etmek. Sadece «temiz» bir ISP için bir UserService sınıfını 10 arayüz ve 15 sınıfa bölmek aşırı mühendisliktir. SOLID bir araçtır, amaç değil. İkinci hata — SRP’yi «bir yöntem = bir sorumluluk» ile karıştırmak. Bir sınıf, tümü aynı sorumluluk alanına aitse birden çok yönteme sahip olabilir.
Simple Thread, 2024’e göre, üçüncü hata — Android’de ViewModel’i miras alırken LSP’yi görmezden gelmek. Temel ViewModel LiveData bekliyor ancak alt ViewModel StateFlow kullanıyorsa — LiveData’ya abone olan istemci kodu güncelleme almaz. Dördüncü — testler için DIP’yi ihlal etmek: RepositoryImpl doğrudan bir OkHttpClient örneği oluşturur, bu da birim testlerini imkansız hale getirir.
Altın kural: SOLID’i gerçek bir sorunu (sık değişiklikler, test zorluğu, tekrar) çözdüğünde uygulayın. Basit CRUD ekranları için, beş prensibin tümüne sıkı sıkıya bağlı kalmak aşırıdır. İş mantığı, finansal hesaplamalar ve API etkileşimleri için SOLID şarttır.
Clean Architecture (Robert C. Martin, 2012) — uygulama katmanı düzeyinde SOLID’in doğrudan uygulanmasıdır. SRP, kullanım durumlarının sınırlarını tanımlar (her kullanım durumu — bir sınıf). OCP, depo arayüzleri aracılığıyla uygulanır (Data Layer, Domain’i değiştirmeden değişebilir). ISP, kullanım durumunu girdi/çıktı sınırlarına ayırır. DIP — bağımlılıkların Domain katmanına doğru içe yönünü belirler. LSP, herhangi bir depo uygulamasının kullanım durumlarını bozmadan değiştirilebilir olmasını sağlar.
Sıkça Sorulan Sorular
SOLID — kodu değiştirmeyi, test etmeyi ve anlamayı kolaylaştıran beş kuraldır. Her harf bir prensiptir: büyük sınıflar yazmayın (SRP), mevcut kodu değiştirmeyin — yenisini ekleyin (OCP), alt sınıfların davranışını bozmayın (LSP) ve diğerleri.
SRP (Single Responsibility) en önemlisi olarak kabul edilir çünkü ihlali God sınıflarına — test etmesi ve değiştirmesi zor olan devasa sınıflara — yol açar. Ancak, DIP (Dependency Inversion) olmadan kod sıkı sıkıya bağlı kalır, bu da kritiktir.
Zorunlu değil, ancak uzun yaşam döngüsüne sahip ticari projeler için şiddetle tavsiye edilir. Basit uygulamalar (tek ekran, iş mantığı yok) için SOLID aşırı olabilir. 50+ ekran ve 3+ geliştiriciye sahip projeler için SOLID asgari gerekliliktir.
Sonuçlar: sınıflar «şişman» hale gelir (1000+ satır), bir yerdeki değişiklik diğer üç yeri bozar, birim testleri yazmak imkansız hale gelir, yeni bir özellik eklemek günler yerine haftalar alır. Zamanla kod «Big Ball of Mud» — karmaşık ve kırılgan — haline gelir.
Uyum belirtileri: her sınıf 200 satırdan az, bir özelliği değiştirmek 5+ dosyayı etkilemez, testler 10 bağımlılığı taklit etmeden yazılabilir, yeni bir geliştirici yapıyı bir günde anlar. SonarQube ve detekt gibi araçlar SRP ve DIP ihlallerini belirlemeye yardımcı olur.
Özet
Anahtar teslim bir mobil uygulama geliştireceğiz
IT Sectr, 2017'den beri girişimler ve işletmeler için iOS ve Android uygulamaları oluşturmaktadır. Size danışmanlık yapacak ve en iyi çözümü önereceğiz.
Ayrıca okuyun