Android’de MVP Mimarisi

Şevval Özdamar
5 min readFeb 22, 2024

--

Herkese merhaba, bu yazımda MVP yazılım mimarisinin ne olduğundan ve Android projelerinde nasıl kullanıldığından bahsedeceğim. Hemen başlayalım! 💃

Nedir bu MVP Mimarisi?

MVP (Model-View-Presenter), Android uygulamalarında mantıksal işlemleri kullanıcı arayüzünden ayıran bir yazılım mimarisidir. MVP, uygulamayı üç farklı katmana ayırır ve bu katmanlardan her biri kendi sorumluluklarına sahip olur.

Bu mimariyi kullanmamızdaki temel hedef, kullanıcı ile etkileşim içinde olan View’da mantıksal işlemlere yer vermemek ve veri kaynağı ( local veya remote veritabanı) ile olan iletişimi katmanlara ayırarak hem okunması kolay hem de test edilebilir kod yazmaktır.

View katmanı, uygulamanın kullanıcı arayüzünde görülen katmanıdır. Ekran üzerinde kullanıcılardan gelen etkileşimleri almak View katmanının sorumluluğundadır. Tüm kullanıcı girdilerini ve etkileşimlerini yakalayarak Presenter katmanına iletilir. Aynı zamanda, Presenter katmanındaki ilgili verileri alarak (bu veriler, Model katmanı tarafından Presenter katmanına sağlanan verilerdir) ekranda kullanıcıya sunar.

Presenter katmanı, View ile Model katmanları arasında köprü işlevi görür. Kullanıcı girdilerini View üzerinden almak ve işledikten sonra Model katmanına iletmekten sorumludur. Aynı zamanda, Model katmanı tarafından gerçekleştirilen veri değişikliklerinin uygulama üzerinde kullanıcıya sunulabilmesi için bir aracı olarak hareket eder.

Model katmanı, uygulamanın verilerini ve iş mantığını yönetir. Verileri bir veritabanından, ağdan veya local depolama gibi kaynaklardan almak Model katmanının sorumluluğundadır. Bu kaynaklardan aldığı verileri, köprü işlevi yapan Presenter katmanına aktarır.

Genel olarak MVP mimarisi katmanlarının dışında, bu mimariyi projeye implement ederken bize yardımcı olan bir başka yapıdan daha söz etmeliyiz: Contract Interface. Bu interface’i Model, View ve Presenter katmanlarının birbirleri arasında kuracakları iletişimin metodlarını atamak için kullanıyoruz. Bir nevi, hangi katmanın hangi işleri yapacağının kontratını yazıyoruz 🙃 Contract Interface’ı kullanmaktaki bir diğer amacımız ise, Presenter katmanının View’ı tanımasını sağlamak oluyor.

MVP mimarisini bir Android projesinde uygulamak için en temel bilgileri aldığımızı düşünüyorum. Şimdi, basit bir Login ekranındaki kontrollerden sonra Home ekranına geçiş yapan uygulamayı baştan beraber MVP mimarisi ile geliştirelim! 🥳

MVP Mimarisi ile Android Projesi Geliştirme

Bu adımları anlatırken kendi kullandığım yöntemler ile ilerliyor olacağım.

  1. Contract Interface

Contract Interface, içerisinde Model Interface, View Interface ve Presenter Interface barındırabilir. Bu interface’leri oluştururken, MVP katmanlarının birbirleri ile etkileşim kurarken hangi işleri yaptığını metodlaştırarak / fonksiyonlaştırarak yazmak çok fayda sağlamaktadır.

Katmanlar arasındaki ilişkileri kendi uygulamamıza göre dizayn edersek:

Uygulamamızın MVP mimari dizaynı

Bir login ekranında, kullanıcı ekranda biri email biri password olmak üzere iki adet edit text ve bir de login olma butonu ile etkileşime geçecektir. Bu kullanıcı etkileşimlerini View’dan Presenter’a sağlarken loginClick() fonksiyonu kullanılır. Bu fonksiyon, email ve password verilerini taşır.

fun loginClick(email: String, password: String)

Email ve password bilgilerinin işlenebilmesi için bu veriler Presenter’dan Model’e sağlanırken checkLoginInfo() fonksiyonu kullanılır. Bu fonksiyon, yine email ve password verilerini taşır ve Model’de yapılan kontroller sonucunda verilerin doğru olup olmamasına göre bir Boolean dönüş değeri üretir.

fun checkLoginInfo(email: String, password: String): Boolean

Email ve password verileri, Model’de işlendikten sonra login olmaya izin verip vermeme işleminin sonucu Model’den Presenter’a aktarılırken ekstra bir metoda/fonksiyona gerek yoktur. Çünkü zaten checkLoginInfo() fonksiyonundan dönen değer bu bilgiyi sağlayacaktır.

Son olarak, checkLoginInfo() fonksiyonundan dönen Boolean değerin kullanıcı ekranında bir etki yaratabilmesi için Presenter’dan View’a UI güncellemeleri sağlanır. Bu, showSuccessLoginResult() ve showFailLoginResult() fonksiyonları ile gerçekleştirilir.

fun showSuccessLoginResult()
fun showFailLoginResult()

Contract Interface’inin son hali şu şekilde olmalıdır:

interface LoginContract {

interface Model {
fun checkLoginInfo(email: String, password: String): Boolean
}

interface Presenter {
fun loginClick(email: String, password: String)
}

interface View {
fun showSuccessLoginResult()
fun showFailLoginResult()
}

}

Unutmamalıyız ki, Model-View-Presenter katmanları oluşturulurken, tüm katmanlar Contract Interface’inde bulunan kendi adına sahip Interface’den kalıtılmalı ve böylece ilgili metodları override etmelidir.

2. View

View katmanımız, LoginActivity adlı bir Activity olacaktır. View katmanı, Presenter ile etkileşimde olacağı için LoginActivity üzerinde bir LoginPresenter oluşturup parametre olarak “this” yaniLoginActivity’inin kendisini veriyoruz.

View katmanında kullanıcının ekran üzerindeki etkileşimlerini yakalamamız gerektiğini biliyoruz. Bu sebeple, kullanıcının edit text üzerindeki girdilerini alıp loginClick() fonksiyonu ile LoginPresenter’a paslıyoruz.

Aynı zamanda, View’ın Presenter’dan ilgili verileri alarak ekranda kullanıcıya sunması gerektiğini biliyoruz. Bu sebeple, LoginPresenter tarafından LoginActivity’e sağlanan UI güncellemelerini de gerçekleştiriyoruz. showSuccessLoginResult() ve showFailLoginResult() bu güncellemeleri sağlayan fonksiyonlardır.

class LoginActivity : AppCompatActivity(), LoginContract.View {

private lateinit var binding: ActivityLoginBinding
private var loginPresenter = LoginPresenter(this)

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityLoginBinding.inflate(layoutInflater)
setContentView(binding.root)

binding.apply {
btnLogin.setOnClickListener {
val email = etEmail.text.toString()
val password = etPassword.text.toString()
loginPresenter.loginClick(email, password)
}
}
}

override fun showSuccessLoginResult() {
Toast.makeText(this, "Login successful.", Toast.LENGTH_SHORT).show()
startActivity(Intent(this, HomeActivity::class.java))
}

override fun showFailLoginResult() {
Toast.makeText(this, "Login failed.", Toast.LENGTH_SHORT).show()
}

}

3. Presenter

Presenter katmanımızın adı LoginPresenter olacak ve View’ı parametre olarak alacaktır. Bu view parametresi, Contract Interface’deki View Interface olmalıdır.

Presenter’ın, View’dan gelen ilgili verileri, işlenmesi için Model’e iletmesi gerektiğini biliyoruz. Bu sebeple, LoginPresenter, LoginActivity’den gelen kullanıcı bilgilerini kontrol etmesi için checkLoginInfo() fonksiyonu aracılığıyla LoginModel’e paslar.

Aynı zamanda Presenter’ın Model’den dönen veri değişikliklerini kullanıcı tarafına aktarması gerektiğini de biliyoruz. Bu sebeple, LoginModel’den dönen değeri loginResult değişkeninde tutup LoginActivity’e gerekli UI güncellemelerini iletiyoruz.

class LoginPresenter(
private val view: LoginContract.View
) : LoginContract.Presenter {

private val model: LoginContract.Model = LoginModel()

override fun loginClick(email: String, password: String) {
val loginResult = model.checkLoginInfo(email, password)
if(loginResult){
view.showSuccessLoginResult()
} else {
view.showFailLoginResult()
}
}
}

4. Model

Model katmanımızın, tamamen logic işlemlerden sorumlu olduğunu biliyoruz. Bu sebeple, kullanıcıların login verilerini userLoginData adlı değişende map olarak bu katmanda tutuyoruz.

checkLoginInfo() fonksiyonunda; View’dan Presenter’a ve sonrasında Presenter’dan da Model’e iletilen login verilerinin doğrulamasını yapıyoruz ve doğrulama işlemi sonrasında oluşacak Boolean değeri return ediyoruz. Böylece LoginPresenter, LoginModel’e erişerek return edilen bu değere sahip olabilecektir.

class LoginModel: LoginContract.Model {

private val userLoginData = mapOf(
"user1@example.com" to "password1",
"user2@example.com" to "password2",
"user3@example.com" to "password3"
)

override fun checkLoginInfo(email: String, password: String): Boolean {
userLoginData.forEach { (e, p) ->
if(email == e && password == p) {
return true
}
}
return false
}
}

Projenin Son Haline Bakış

Projenin tüm kodlarına ve daha gelişmiş versiyonuna ulaşmak isterseniz Github linkini aşağıda iletiyorum 👾

https://github.com/sevvaloz/Login-Project-With-MVP

Sonuç Olarak Neden MVP Mimarisini Tercih Edelim?

Projeyi katmanlara ayırmak, özellikle birçok kişinin üzerinde geliştirme yaptığı büyük projelerde her zaman daha verimlidir. Koddaki hataları daha rahat fark etmemizi ve tabi ki en önemlisi daha temiz kod yazmamızı sağlar.

Android projelerine de rahatça implement edilebilen MVP mimarisini, şimdiye kadar hiç tercih etmediyseniz en azından bir kere denemenizi tavsiye ederim.

Sonraki yazıda görüşmek üzere, sağlıklı günler 🌸

Kaynaklar

--

--

Şevval Özdamar
Şevval Özdamar

Written by Şevval Özdamar

Computer Engineer - Android Developer

No responses yet