[Pattern] Android의 설계 패턴 2 : MVP by Java
안드로이드에서의 MVC를 살펴보았으니, 이제 MVP를 살펴볼 차례이다. Eric Maxwell 선생님께서는 안드로이드에서 어떻게 MVP를 활용하는지 역시 TicTacToe 예제를 통해 보여주셨다. 대단하신 분이다. 우선 MVP가 무엇인지부터 살펴보고 가자. MVP는 모델(Model)-뷰(View)-프레젠터(Presenter)라는 책임 집합으로 나뉘는 설계 패턴이다.
https://github.com/ericmaxwell2003/ticTacToe
https://full-stack.tistory.com/22
MVP
모델(Model)
MVC의 모델과 동일하다
모델은 앱의 상태 및 데이터와 비즈니스 로직이라고 보면된다. 즉 앱의 두뇌 역할을 한다. 모델을 클래스로 표현하면 클래스의 멤버 변수들은 상태, 멤버 메서드들은 비즈니스 로직이라고 하면 되겠다. 여기서 멤버 메서드는 프레젠터와 상호작용하는데 쓰이는 API역할을 수행하며 Public 으로 작성되었다고 보면 된다. 메서드를 호출할 때 전달하는 매개변수를 상호작용하는 데이터라고 보면 된다!
예제에서는 Board라는 클래스가 모델을 담당하고 있다.
뷰(View)
뷰는 모델을 표현한 것으로 보통 안드로이드에서는 UI로 보이며 사용자(User)와 상호작용하는 부분이다. MVC에서의 뷰와 같은 역할을 하는 것은 맞으나, 구조적으로 조금 다르다. 우선 MVC에서 뷰와 컨트롤러가 너무 강하게 연결되어 있다는 것은 유연성에 문제가 있었다. 이 유연성 문제를 어떻게 해결하는지는 코드로 설명하겠다. 따라서 MVP의 뷰는 사용자와 상호작용 및 UI에 무언가를 표시하는 방법(MVC에서 컨트롤러가 맡았던)까지 담당한다. 안드로이드에서는 layout.xml과 activity/fragment가 한 덩어리로 '뷰'라고 보면 되겠다.
예제에서는 Xml 코드로 작성된 Layout코드와 view 인터페이스를 구현한 activity이다.
프레젠터(Presenter)
프레젠터는 뷰와 모델이 데이터를 주고받도록 다리 역할을 한다. 컨트롤러와 역할이 비슷하지만 뷰와 연결되어있지 않고 인터페이스 역할을 하기에 유연성을 보장하고, 테스트가 용이하다. MVC에서는 컨트롤러가 뷰에 표시할 방법과 내용을 모두 담당하였는데 프레젠터는 내용(데이터)만 전달한다. 안드로이드에서는 프레젠터가 안드로이드 API에 대한 의존도를 낮게 설계할 수 있다. 이러한 이유들로 인해 MVP는 MVC에 비해 유연성이 높다.
예제에서는 presenter라는 별도의 인터페이스와 이를 구현한 TicTacToePresenter이다.
MVP의 동작
MVP는 다음과 같이 동작한다. 이제 하나하나 예제 코드를 보며 이야기해 보겠다. (모델은 MVC 때와 동일하니 생략하도록 하겠다.)
뷰(View)
뷰부터 먼저 살펴보면 다음과 같다. 일단 뷰는 별도의 인터페이스를 먼저 구현하여, 프레젠터와 상호작용하고 유연성을 높인다.
public interface TicTacToeView {
void showWinner(String winningPlayerDisplayLabel);
void clearWinnerDisplay();
void clearButtons();
void setButtonText(int row, int col, String text);
}
그리고 액티비티는 이를 구현(implement)한다. 그리고 프레젠터와 상호작용하기 위해 프레젠터 인스턴스를 생성 후 생성자에 자신을 전달한다. 이것으로 뷰는 프레젠터와 상호작용할 수 있다. 프레젠터는 위의 메서드들만 쓰면 된다. (깔끔하다)
public class TicTacToeActivity extends AppCompatActivity implements TicTacToeView {
.. 중략
TicTacToePresenter presenter = new TicTacToePresenter(this);
.. 중략
@Override
public void setButtonText(int row, int col, String text) {
Button btn = (Button) buttonGrid.findViewWithTag("" + row + col);
if(btn != null) {
btn.setText(text);
}
}
public void clearButtons() {
for( int i = 0; i < buttonGrid.getChildCount(); i++ ) {
((Button) buttonGrid.getChildAt(i)).setText("");
}
}
public void showWinner(String winningPlayerDisplayLabel) {
winnerPlayerLabel.setText(winningPlayerDisplayLabel);
winnerPlayerViewGroup.setVisibility(View.VISIBLE);
}
public void clearWinnerDisplay() {
winnerPlayerViewGroup.setVisibility(View.GONE);
winnerPlayerLabel.setText("");
}
}
이렇게 되면 TicTacToeView를 구현한 어떤 뷰던간에 프레젠터는 쉽게 상호작용할 수 있다. 이것으로 인해 유연성이 높아졌다고 할 수 있는 것이다. 또한 UI를 그리는 방법에 대해 뷰에서 모두 처리하므로 프레젠터는 이러한 방법까지 담당할 필요가 없다. MVC에서는 XML(뷰)에 변동이 생길 때마다 Activity(컨트롤러) 를 변경해야 하는 번거로움이 있었다. 하지만 MVP에서는 XML+Activity(뷰)에 변동이 생겨도 프레젠터를 바꿀 필요가 전혀 없다! (모든 뷰는 view 인터페이스를 구현하는 게 기본일 테니. 이로 인해 유연성, 모듈화 가능성 증대).
프레젠터(Presenter)
프레젠터는 뷰와 모델의 다리 역할을 한다고 했다. 어떻게 하는지 보자. 먼저 안드로이드의 Presenter는 액티비티의 생명주기를 무시할 수 없기 때문에 다음과 같은 프레젠터 인터페이스는 있어야 한다. Eric Maxwell 선생님께서 말씀하시길 극단적인 MVP 강성론자들은 프레젠터에 어떤 안드로이드 API도 들어가선 안된다고 주장한다고 한다. 중요한 이야기는 아니고 대충 프레젠터가 그런 성향을 가져야 한다 정도로 받아들이면 될 것 같다.
public interface Presenter {
void onCreate();
void onPause();
void onResume();
void onDestroy();
}
이제 이를 구현한 TicTacToePresenter를 살펴보면 다음과 같다.
public class TicTacToePresenter implements Presenter {
private TicTacToeView view;
private Board model;
public TicTacToePresenter(TicTacToeView view) {
this.view = view;
this.model = new Board();
}
@Override
public void onCreate() {
model = new Board();
}
@Override
public void onPause() {
}
@Override
public void onResume() {
}
@Override
public void onDestroy() {
}
public void onButtonSelected(int row, int col) {
Player playerThatMoved = model.mark(row, col);
if(playerThatMoved != null) {
view.setButtonText(row, col, playerThatMoved.toString());
if (model.getWinner() != null) {
view.showWinner(playerThatMoved.toString());
}
}
}
public void onResetSelected() {
view.clearWinnerDisplay();
view.clearButtons();
model.restart();
}
}
private 변수로 깔끔하게 모델과 뷰가 선언되어있다. 뷰는 프레젠터의 onButtonSelected, onResetSelected 메서드로 사용자와의 상호작용 이벤트를 전달하고 프레젠터는 이 정보를 모델로 전달한다. 모델로부터 받은 결과를 view 인스턴스를 통해 뷰로 전달한다. 확실히 MVC에 비해 정돈된 느낌이 든다. 여기서 view를 구현한 인스턴스의 메서드를 사용하는데, 이것은 테스트 또한 용이하게 하여 뷰와의 상호작용을 쉽게 테스트할 수 있다.
MVP의 장단점
장점
프레젠터는 안드로이드 고유의 뷰와 API에 구애받지 않고, View 인터페이스를 구현했다면 어떤 뷰와도 작업할 수 있어 프레젠터 로직을 쉽게 테스트할 수 있다. 이는 모듈화 가능성과 유연성을 증대시킨다. (MVC에서 발생하는 어려움을 다소 해소한 것이 MVP)
단점 (프레젠터의 문제)
MVC의 여러 어려움을 해결했다 하더라도, 같은 문제는 하나 남아있다. 시간이 지남에 따라 코드가 늘어, 프레젠터가 지나치게 비대해지는 경향이 있다. 컨트롤러나 프레젠터나 비즈니스 로직은 계속 쌓일 수밖에 없어 유지보수가 어려워진다. 거대한 프레젠터는 분리하기도 어렵다.
그런데 MVVM은 이를 시작할 때부터 해결할 수 있도록 도움을 준다(!!)
끝.
'Architecture & Engineering > Design Pattern' 카테고리의 다른 글
[GoF Design Pattern] 3. 데코레이터(Decorator) 패턴 (0) | 2023.06.18 |
---|---|
[GoF Design Pattern] 2. 옵저버(Obsever) 패턴 (0) | 2023.05.29 |
[GoF Design Pattern] 1. 전략(Strategy) 패턴 (0) | 2023.05.14 |
[Pattern] Android의 설계 패턴 1 : MVC by Java (0) | 2022.04.01 |
[Pattern] 옵저버 패턴 (Observer Pattern) by Kotlin (0) | 2022.03.28 |
댓글