MVC (Model-View-Controller) — архитектонски образац који дијели апликацију на три компоненте: Model одговара за податке и пословну логику, View — за кориснички интерфејс, Controller — за обраду улаза и координацију Model-а и View-а. У iOS-у MVC се имплементира кроз UIViewController, у Android-у — кроз Activity и Fragment. MVC остаје основни образац на којем су изграђени MVVM, MVP и Clean Architecture. Детаљније — на MVC in Cocoa Core.
Главно
MVC (Model-View-Controller) — архитектонски образац који је предложио Trygve Reenskaug 1979. године за језик Smalltalk-80. Образац дијели апликацију на три слоја: Model садржи податке и пословну логику, View одговара за приказ, Controller обрађује кориснички улаз и ажурира Model и View. Раздвајање одговорности омогућава мијењање сваког слоја независно — на примјер, замјену View-а са UIKit на SwiftUI без промјене пословне логике у Model-у.
Интеракција компоненти у MVC-у слиједи циклус: корисник интерагује са View-ом → Controller прима догађај → Controller ажурира Model → Model обавјештава Controller о промјенама → Controller ажурира View. У класичној имплементацији Model користи Observer образац: при промјени података Model шаље обавјештења, Controller се претплаћује и ажурира View. У Apple-овој имплементацији Key-Value Observing (KVO) или NotificationCenter обављају ову улогу.
| Компонента | Одговорност | Примјер у iOS-у | Примјер у Android-у |
|---|---|---|---|
| Model | Подаци, пословна логика, мрежа | Struct User, CoreData | Data class, Repository |
| View | Приказ UI | Storyboard, XIB, UIView | XML распоред, Jetpack Compose |
| Controller | Обрада улаза, координација | UIViewController | Activity, Fragment |
MVC у савременом развоју мобилних апликација користи се рјеђе него прије 10 година, али остаје обавезан за разумијевање. Apple препоручује MVC за једноставне екране у UIKit апликацијама. Google не препоручује чисти MVC за Android — званична документација предлаже MVVM са Jetpack-ом. Ипак, познавање MVC-а је неопходно за рад са насљеђеним пројектима и за разумијевање еволуције архитектонских образаца.
Apple MVC — прилагођена имплементација обрасца уграђена у UIKit. UIViewController игра улогу Controller-а: управља животним циклусом екрана (viewDidLoad, viewWillAppear, viewDidDisappear), обрађује додире и радње корисника, ажурира View кроз IBOutlets. View се креира у Interface Builder-у (storyboard или XIB) или програмски. Model — су произвољни објекти података: мрежни сервиси, CoreData стекови, Swift структуре.
final class UserViewController: UIViewController {
// View (кроз storyboard outlet)
@IBOutlet private var nameLabel: UILabel!
@IBOutlet private var emailLabel: UILabel!
// Model
private let userService = UserService()
override func viewDidLoad() {
super.viewDidLoad()
loadUser()
}
private func loadUser() {
userService.fetchUser { [weak self] user in
// Controller ажурира View
self?.nameLabel.text = user.name
self?.emailLabel.text = user.email
}
}
}
Проблем Apple MVC-а — View и Controller су чврсто повезани. UIViewController истовремено управља и View-ом и логиком. Storyboard чува View у XML-у, али контролер има директне референце на UI елементе кроз IBOutlets. Ово крши принцип јединствене одговорности: контролер одговара за животни циклус, делегате, datasource, target-action и анимације. Као резултат, стандардни екран iOS апликације садржи 200–500 редова у контролеру.
Животни циклус ViewController-а — Apple обезбјеђује 6 метода животног циклуса: loadView (ручно креирање View-а), viewDidLoad (након учитавања View-а у меморију), viewWillAppear (прије појављивања на екрану), viewDidAppear (након анимације), viewWillDisappear (прије напуштања екрана), viewDidDisappear (након напуштања). Свака метода — мјесто за смјештање логике у MVC-у. Коришћење ових метода за пословну логику убрзава раст контролера.
Android MVC — Activity и Fragment играју улогу Controller-а, XML датотеке распореда — View, било која POJO класа са подацима — Model. Activity управља животним циклусом екрана: onCreate, onStart, onResume, onPause, onStop, onDestroy. Fragment — подекран унутар Activity-ја са сопственим животним циклусом. View (XML) је одвојен од Controller-а и учитава се кроз setContentView или LayoutInflater. Model — репозиторијуми, базе података, мрежни позиви.
class UserActivity : AppCompatActivity() {
// View кроз XML распоред
private lateinit var binding: ActivityUserBinding
// Model
private val userRepository = UserRepository()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityUserBinding.inflate(layoutInflater)
setContentView(binding.root)
loadUser()
}
private fun loadUser() {
userRepository.getUser { user ->
runOnUiThread {
binding.nameText.text = user.name
binding.emailText.text = user.email
}
}
}
}
Android ViewBinding и DataBinding — савремени алати који смањују повезаност Controller-а и View-а. ViewBinding генерише класу са директним референцама на View из XML-а, елиминишући findViewById. DataBinding додаје могућност повезивања података са UI-јем у XML ознакама кроз @{user.name}. DataBinding — корак ка MVVM-у, јер омогућава пренос података из Model-а у View без кода у Activity-ју. Google препоручује DataBinding за све нове пројекте.
Животни циклус Android-а је сложенији од iOS-а: Activity може бити уништена и поново креирана при ротацији екрана, недостатку меморије или промјени конфигурације. У чистом MVC-у контролер (Activity) садржи логику која се губи при уништењу. Ово захтијева чување стања кроз onSaveInstanceState или ViewModel из Jetpack-а, што излази из оквира чистог MVC-а и приближава архитектуру MVVM-у.
Massive View Controller — термин који описује главни проблем MVC-а у развоју мобилних апликација. Контролер у iOS-у и Android-у преузима превише одговорности: обраду улаза, валидацију података, мрежну интеракцију, навигацију, кеширање, анимације, управљање животним циклусом. Као резултат, контролер нараста до 500–2000 редова кода, постаје тежак за читање, тестирање и одржавање.
Узроци Massive View Controller-а — архитектура UIKit-а и Android Framework-а подстиче смјештање логике у контролер. Мрежни позиви, обрада JSON-а, навигација — све се то природно пише у Activity-ју или UIViewController-у, јер они имају приступ животном циклусу и UI-ју. Програмер мора свјесно измјештати логику у одвојене класе (Service, Manager, Interactor), што захтијева дисциплину и разумијевање архитектонских принципа.
| Проблем MVC-а | Опис | Рјешење |
|---|---|---|
| Чврста повезаност | Controller зна за View и Model | MVVM — ViewModel не зна за View |
| Тешкоћа тестирања | Controller зависи од UIKit/Android-а | Изношење логике у сервисе |
| Животни циклус | Стање се губи при ротацији | ViewModel из Jetpack/SwiftUI |
| Недостатак навигације | Controller управља прелазима | Coordinator образац, Router |
Тестирање MVC-а — Model се тестира изоловано јединичним тестовима. Controller је тешко тестирати због зависности од UIKit/UIFoundation. XCTest не дозвољава креирање UIViewController-а без прозора приказа. За Android, ActivityTestRule и Robolectric дјелимично рјешавају проблем, али су тестови спори. View се обично не тестира јединичним тестовима — за UI се користе тестови снимака екрана и UI тестови (XCUITest, Espresso).
Када је MVC оправдан — једноставни екрани са једним-два елемента (екран за пријаву, профил, подешавања). Прототипови и MVP за провјеру хипотеза — MVC се пише брже без додатних слојева. Пројекти са малом базом кода до 10–15 екрана. У сложеним пројектима MVC води до нагомилавања техничког дуга и захтијева рефакторисање сваких 6–12 мјесеци.
MVC vs MVVM — главна разлика: у MVVM-у контролер је замијењен ViewModel-ом који нема референцу на View. Подаци се преносе кроз Observable (SwiftUI), LiveData/StateFlow (Android) или Combine/RxSwift. ViewModel се тестира јединичним тестовима без UI зависности. Apple препоручује MVVM са SwiftUI-јем од 2019. године, Google — MVVM са LiveData/Flow као званичну архитектуру Android-а. MVVM захтијева више код за повезивање, али значајно побољшава тестираност.
MVC vs MVP — у MVP-у (Model-View-Presenter) Presenter је тестирани слој који прима View кроз интерфејс. За разлику од MVC-а, гдје Controller директно управља View-ом кроз UIKit, Presenter не зависи од фрејмворка — ради кроз апстракцију ViewInterface. MVP је био популаран у развоју Android-а до појаве Jetpack-а и користи се у старим пројектима. Presenter живи дуже од Activity-ја и чува стање при ротацији екрана.
MVC vs Clean Architecture — Clean Architecture додаје слојеве Use Cases (Interactors), Entities, Gateways и Repository. MVC остаје у Presentation слоју, али се пословна логика измјешта у Domain слој са Use Cases-има. Clean Architecture рјешава проблем Massive View Controller радикално — Controller садржи само позиве Use Cases-а и ажурирање View-а. Недостатак је значајно повећање броја класа и датотека, што је оправдано за пројекте од 50+ екрана.
// MVC у iOS-у: Controller садржи све
class OrderViewController: UIViewController {
func placeOrder() {
// Валидација + мрежа + UI ажурирање
guard Validation.isValid(total) else { return }
NetworkService.shared.submit(order) { [weak self] result in
self?.handleResult(result)
}
}
}
// MVVM: логика у ViewModel-у
class OrderViewModel: ObservableObject {
@Published var state: OrderState = .idle
func placeOrder() { /* бизнес-логика */ }
}
Избор архитектуре зависи од величине тима, пројекта и потребне тестираности. За тим од 1–2 програмера и пројекат до 20 екрана одговара MVVM. За велики тим од 5 програмера и пројекат од 50 екрана — Clean Architecture са модуларном структуром. MVC остаје актуелан за разумијевање еволуције архитектура, за подршку насљеђених пројеката и за једноставне екране у UIKit-у без сложене пословне логике.
Често постављана питања
Главни проблем је Massive View Controller. У iOS-у контролер UIViewController одговара за све: обраду улаза, ажурирање View-а, рад са мрежом, навигацију и животни циклус. У Android-у Activity/Fragment обавља аналогне функције. Као резултат, контролер нараста до хиљада редова кода, постаје тежак за тестирање и одржавање, кршећи принцип јединствене одговорности.
У MVC-у контролер директно ажурира View и обрађује кориснички улаз. У MVVM-у улогу контролера обавља ViewModel који нема референцу на View — подаци се преносе кроз механизме повезивања. MVVM се боље тестира, јер ViewModel не зависи од UIKit-а или Android Framework-а. Apple препоручује MVVM са SwiftUI-јем, Google — MVVM са Jetpack Compose-ом.
Да, MVC остаје функционалан образац за једноставне екране и прототипове. Apple препоручује MVC за UIKit апликације са једноставним екранима. За сложене пројекте са више екрана, мрежним захтјевима и кеширањем боље је изабрати MVVM, VIPER или Clean Architecture. Програмерима почетницима се препоручује да савладају MVC прије учења сложенијих образаца.
Model се тестира изоловано — то су обични објекти података и пословна логика. Controller је тешко тестирати због зависности од UIKit-а или Android Framework-а. Препоручује се измјештање пословне логике из контролера у засебне сервисе или интеракторе, који се тестирају јединичним тестовима. View се обично не тестира јединичним тестовима — за њега се користе UI тестови и тестови снимака екрана.
На iOS-у — MVVM са SwiftUI и Combine, Apple стандард од 2019. године. На Android-у — MVVM са LiveData или StateFlow, званично препоручен од Google-а. За велике пројекте са тимовима од 5 програмера — Clean Architecture са VIPER-ом на iOS-у или Clean Architecture на Android-у са подјелом на модуле по функцијама. За насљеђене пројекте са MVC-ом — постепено рефакторисање са измјештањем логике у засебне сервисе.
Закључци
Развићемо мобилну апликацију под кључ
IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.
Прочитајте такође