Düşünce Liderleri

SOLID Prensiplerini Android Geliştirmeye Uygulama

mm
Unite.AI sitesini Google'daki tercih ettiğiniz kaynaklara ekleyin

Yazılım yazmak bir yaratma eylemidir ve Android geliştirme de buna istisna değildir. Sadece çalışır duruma getirmekten daha fazlasıdır. Uygulamaları tasarlamak, büyüyebilmek, uyum sağlayabilmek ve zaman içinde yönetilebilir halde kalabilmektir.

Bir Android geliştirici olarak, birçok mimari zorluğu karşılaştım ve SOLID prensiplerine uymak, nawet en karmaşık kod tabanlarını temiz sistemlere dönüştürebileceğini keşfettim. Bunlar soyut prensipler değil, sağlam, ölçeklenebilir ve bakımı kolay kod yazmak için sonuç odaklı ve tekrar edilebilir yollar.

Bu makale, SOLID prensiplerinin Android geliştirmeye nasıl uygulanabileceğine dair gerçek dünya örnekleri, pratik teknikler ve Meta WhatsApp ekibinin deneyimleri aracılığıyla fikir verecektir.

SOLID Prensiplerini Anlama

SOLID prensipleri, Robert C. Martin tarafından önerilen, nesne yönelimli programlama için beş tasarım prensibidir ve temiz ve verimli bir yazılım mimarisi garanti eder.

  • Tek Sorumluluk Prensibi (SRP): Bir sınıfın sadece bir nedeni olmalıdır.
  • Açık/Kapalı Prensibi (OCP): Yazılım varlıkları genişletmeye açık, ancak değişikliklere kapalı olmalıdır.
  • Liskov Değiştirme Prensibi (LSP): Alt tipler, temel tiplerin yerine geçebilecek olmalıdır.
  • Arayüz Ayrıştırma Prensibi (ISP): Arayüzler, istemciye özgü olmalı ve kullanılmayan yöntemlerin uygulanmasını zorlamamalıdır.
  • Bağımlılık Tersine Çevirme Prensibi (DIP): Yüksek seviyeli modüller, düşük seviyeli modüllere değil, soyutlamalara bağımlı olmalıdır.

Bu prensipleri Android geliştirmeye entegre ederek, ölçeklenebilir, test edilebilir ve bakımı kolay uygulamalar oluşturabiliriz.

Tek Sorumluluk Prensibi (SRP): Sorumlulukları Basitleştirme

Tek Sorumluluk Prensibi, bakımı kolay kod yazmanın temelidir. Her sınıfın tek bir sorumluluğu olmalıdır. Bir anti-patern, Activiti veya Fragment’lerin “Tanrı sınıfları” olarak düşünülmesidir; bunlar UI işlemlerinden, veri alma ve hata işleme gibi sorumlulukları üstlenmektedir. Bu yaklaşım, test ve bakım için bir kâbus yaratır.

SRP ile farklı sorumlulukları farklı bileşenlere ayırın. Örneğin, haber uygulamasında haber oluşturma veya okuma.


class NewsRepository {
fun fetchNews(): List {
// Veri alma mantığını işler
}
}

class NewsViewModel(private val newsRepository: NewsRepository) { fun loadNews(): LiveData<List> { // UI durumunu ve veri akışını yönetir } }

class NewsActivity : AppCompatActivity() { // Sadece UI işlemlerini işler }

 

Her sınıfın sadece bir sorumluluğu vardır; bu nedenle test ve değiştirme kolaydır.

Modern Android geliştirmesinde, SRP genellikle Jetpack ile birlikte uygulanır. Örneğin, veri işleme mantığı ViewModel içinde kalabilirken, Activiti veya Fragment’ler sadece UI ile ilgilenir. Veri alma işlemleri, yerel veritabanları gibi Room veya ağ katmanları gibi Retrofit’e devredilebilir. Bu, UI sınıflarının şişkinliğini azaltır ve her bileşenin sadece bir sorumluluğu olmasını sağlar. Aynı zamanda kodun test edilmesi ve desteklenmesi çok daha kolay hale gelir.

Açık/Kapalı Prensibi (OCP): Genişletme için Tasarlanma

Açık/Kapalı Prensibi, bir sınıfın genişletmeye açık ancak değişikliklere kapalı olmasını belirtir. Android uygulamaları için daha makuldir çünkü bunlar sürekli olarak güncellenir ve yeni özellikler eklenir.

OCP prensibini Android uygulamalarında kullanmanın en iyi örneği, arayüzler ve soyut sınıflardır. Örneğin:


interface PaymentMethod {
fun processPayment(amount: Double)
}

class CreditCardPayment : PaymentMethod { override fun processPayment(amount: Double) { // Kredi kartı ödemeleri için uygulama } }

class PayPalPayment : PaymentMethod { override fun processPayment(amount: Double) { // PayPal ödemeleri için uygulama } }

 

Yeni ödeme yöntemleri eklemek, mevcut sınıflarda değişiklikler yapılmasını gerektirmez; yeni sınıflar oluşturulmasını gerektirir. Bu, sistemin esnek ve ölçeklenebilir olmasını sağlar.

Android cihazları için oluşturulan uygulamalarda, Açık/Kapalı Prensibi özellikle özellik anahtarları ve yapılandırmalar için yararlıdır. Örneğin, AnalyticsTracker temel arayüzü, farklı analiz hizmetlerine raporlama yapan bir uygulamada, her yeni hizmet ayrı bir sınıf olarak eklenerek mevcut kodda değişiklik yapılmasını gerektirmez. Bu, analiz modülünün genişletmeye açık olmasını sağlar – yeni takipçiler eklenebilir – ancak değişikliklere kapalı olmasını sağlar: mevcut sınıflar her yeni hizmet eklendiğinde yeniden yazılmaz.

Liskov Değiştirme Prensibi (LSP): Değiştirilebilirlik Sağlama

Liskov Değiştirme Prensibi, alt sınıfların temel sınıfların yerine geçebileceklerini belirtir ve uygulamanın davranışı değişmemelidir. Android’de bu prensip, yeniden kullanılabilir ve öngörülebilir bileşenler tasarlamak için temelidir.

Örneğin, bir çizim uygulaması:


abstract class Shape {
abstract fun calculateArea(): Double
}

class Rectangle(private val width: Double, private val height: Double) : Shape() { override fun calculateArea() = width * height }

class Circle(private val radius: Double) : Shape() { override fun calculateArea() = Math.PI * radius * radius }

 

Hem Rectangle hem de Circle, diğerinin yerine geçebilecek şekilde değiştirilebilir ve sistem başarısız olmaz, bu da sistemin esnek ve LSP’ye uyumlu olduğunu gösterir.

Android’in RecyclerView.Adapter alt sınıflarını düşünün. Her bir alt sınıf, RecyclerView.Adapter<VH> genişletir ve onCreateViewHolder, onBindViewHolder ve getItemCount gibi temel fonksiyonları geçersiz kılar. RecyclerView, bu yöntemler doğru bir şekilde uygulanmışsa, herhangi bir alt sınıfı değiştirerek kullanabilir. Burada LSP sağlanır ve RecyclerView, herhangi bir ada göre değiştirilebilir.

Arayüz Ayrıştırma Prensibi (ISP): İncelikli ve Odaklı Arayüzler

Büyük uygulamalarda, özellikle ağ veya veri depolama etrafında çok fazla sorumluluğa sahip arayüzler tanımlamak yaygındır. Bunun yerine, bunları daha küçük, daha odaklı arayüzlere ayırın. Örneğin, kullanıcı kimlik doğrulama uç noktalarından sorumlu bir ApiAuth arayüzü, blog gönderileri veya sosyal medya akışları için ApiPosts arayüzünden farklı olmalıdır. Bu ayrım, sadece post ile ilgili yöntemlere ihtiyaç duyan istemcilerin, kimlik doğrulama çağrılarını uygulamak ve bağımlı olmak zorunda olmasını önler, bu da kodu ve test kapsamını daha ince tutar.

Arayüz Ayrıştırma Prensibi, büyük arayüzler yerine daha küçük, odaklı arayüzlerin kullanılmasını sağlar. Bu prensip, sınıfların gereksiz yöntemleri uygulamasını önler.

Örneğin, kullanıcı eylemlerini temsil eden tek bir büyük arayüz yerine, kotlin kodunu düşünün:


interface Authentication {
fun login()
fun logout()
}

interface ProfileManagement { fun updateProfile() fun deleteAccount() }

 

Bu arayüzlere uyan sınıflar, sadece gerektirdikleri işlevselliğe odaklanabilir, bu da kodu temizler ve bakımı kolaylaştırır.

Bağımlılık Tersine Çevirme Prensibi (DIP): Bağımlılıkları Soyutlama

Bağımlılık Tersine Çevirme Prensibi, yüksek seviyeli modüllerin somut uygulamalara değil, soyutlamalara bağımlı olmasını sağlar. Bu prensip, özellikle bağımlılık enjeksiyonu çerçeveleri gibi Dagger ve Hilt ile Android’ın modern geliştirme uygulamalarına mükemmel şekilde uyar.

Örneğin:


class UserRepository @Inject constructor(private val apiService: ApiService) {
fun fetchUserData() {
// Soyutlama üzerinden kullanıcı verilerini alır
}
}

 

Burada, UserRepository, ApiService soyutlamasına bağımlıdır, bu da esneklik ve test edilebilirlik sağlar. Bu yaklaşım, uygulamayı değiştirmek için somut uygulamaları değiştirmeyi kolaylaştırır, örneğin test sırasında bir mock hizmeti kullanmak.

Hilt, Dagger ve Koin gibi çerçeveler, bağımlılık enjeksiyonunu kolaylaştırarak Android bileşenlerine bağımlılıkların sağlanmasına olanak tanır, böylece bunları doğrudan örneklemeye gerek kalmaz. Bir depoda, örneğin, Retrofit uygulamasını örneklemektense, bir ApiService arayüzü gibi bir soyutlama enjekte edersiniz. Bu, ağ uygulamasını değiştirmenizi kolaylaştırır – örneğin, yerel test için bir bellekte mock hizmeti – ve depo kodunuzda hiçbir şeyi değiştirmenize gerek kalmaz. Gerçek uygulamalarda, sınıflar @Inject veya @Provides ile işaretlenerek bu soyutlamaları sağlar, bu da uygulamanızı modüler ve test dostu hale getirir.

SOLID Prensiplerinin Pratik Yararları

Android geliştirmesinde SOLID prensiplerini benimsemek somut faydalar sağlar:

  1. Geliştirilmiş Test Edilebilirlik: Odaklı sınıflar ve arayüzler, birim testlerini yazmayı kolaylaştırır.
  2. Artırılmış Bakım: Net sorumluluk ayrımı, hata ayıklamayı ve güncellemeyi basitleştirir.
  3. Ölçeklenebilirlik: Modüler tasarımlar, özelliklerin sorunsuz bir şekilde eklenmesini sağlar.
  4. İşbirliği: İyi yapılandırılmış kod, işbirliğini kolaylaştırır ve yeni geliştiricilerin entegrasyon süresini azaltır.
  5. Performans Optimizasyonu: İncelikli, verimli mimariler, gereksiz işleme ve bellek kullanımını en aza indirir.

Gerçek Dünya Uygulamaları

Özellik açısından zengin uygulamalarda, örneğin e-ticaret veya sosyal medya uygulamalarında, SOLID prensiplerinin uygulanması, yeni bir özellik veya hizmet eklendiğinde geriye dönük uyumsuzluk riskini büyük ölçüde azaltabilir. Örneğin, bir uygulama içi satın alma akışı gerektiren yeni bir gereksinim için, gerekli arayüzleri (Ödeme, Analitik) uygulayan ayrı bir modül tanıtılabilir. Bu, mevcut modüllere dokunulmaksızın yapılır. Bu tür modüler bir yaklaşım, SOLID tarafından yönlendirilir ve Android uygulamanızın pazar taleplerine nhanh chóng uyum sağlamasını ve kod tabanının zaman içinde karmaşıklığa dönüşmesini önler.

Birçok geliştiricinin işbirliği yaptığı büyük bir projede çalışırken, karmaşık bir kod tabanını SOLID prensipleri ile korumak çok önemlidir. Örneğin, sohbet modülünde veri alma, iş mantığı ve UI işlemlerini ayırarak, yeni özellikler eklenirken geriye dönük uyumsuzluk olasılığını azaltabilirsiniz. Aynı şekilde, DIP’nin uygulanması, ağ operasyonlarını soyutlamak için kritiktir, bu da ağ istemcileri arasında neredeyse hiç kesinti olmadan geçiş yapılmasını sağlar.

SONUÇ

SOLID prensipleri, sadece teorik bir rehber değil, dayanıklı, uyumlu ve bakımı kolay yazılım oluşturmak için pratik bir felsefedir. Android geliştirmenin hızlı değişen dünyasında, gereksinimler teknolojiler gibi sık değiştiğinde, bu prensiplere uymak başarı için sağlam bir zemin sağlar.

İyi kod, sadece çalışır duruma getirmekle ilgili değildir; sürekli çalışabilen ve değişen ihtiyaçlarla büyüyen bir sistem oluşturmakla ilgilidir. SOLID prensiplerini benimsemek, sadece daha iyi kod yazmanızı değil, aynı zamanda geliştirmek, ölçeklemek ve bakımını yapmak keyifli olan uygulamalar oluşturmanızı sağlar.

Farhana, uzman bir mobil uygulama geliştiricisidir ve sıfırdan birçok mobil uygulamayı başarılı bir şekilde teslim etmiştir. Bhutan'daki hükümet ICT memurları için Android geliştirme eğitimleri düzenlemiş, birçok yeni katılımcıyı mentörlük etmiş ve birlikte başarıya ulaşmak için takımları yönetmiştir.