태그 보관물: pattern

Android, MVC, MVVM, MVP

원본링크(Android 와 개발 패턴 1부)

코드를 작성해 가는데 있어서 많은 개발자들은 일관성을 유지하고 유지보수성을 높일 수 있는 많은 개발 모델들을 생각해 왔습니다. 특히나 협업하는 과정에서 로직과 View 를 다루는 코드가 뒤섞이게 되면 작성자뿐만 아니라 동료들조차도 유지보수 하기 힘들어지는 모습을 쉽게 볼 수 있습니다.

이러한 코드 작성때문에 팀단위로, 프로젝트 단위로, 때로는 회사 단위로 여러가지 정책을 가지고 코드와 개발 모델들을 일관되게 하려고 많은 노력을 합니다. 이러한 노력에 개발자들 사이에서 많이 통용되는 개발 패턴들이 나오게 되었습니다.

대표적으로 MVC 모델이라고 할 수 있습니다. 필자가 과거에 했던 웹 프로젝트 중 SpringMVC 프레임워크는 외부로부터의 요청을 처리하는 Controller, 실질적인 비지니스 로직을 수행하는 Model, 그리고 화면 처리를 담당하는 View 를 구분할 수 있도록 지원되어 많은 개발자들에게 사랑받고 있는 프레임워크 중 하나입니다.

차츰 View 자체에서 처리해야하는 로직이 복잡해짐에 따라 MVVM과 MVP 패턴이 나오면서 View 내에서도 Logic과 Presenter 를 구분하려는 노력이 끊임없이 나왔습니다.

이외에도 많은 개발 패턴들이 있지만 가장 많이 통용되는 이야기를 하고자 합니다.

초창기 안드로이드 개발 코드의 모습

안드로이드에서도 초창기 부터 이러한 노력이 끊임없이 나왔습니다. MVC 와 같은 개발 패턴에 익숙해져 있던 많은 개발자들은 안드로이드에도 이와 같은 모습을 적용하려고 노력하였습니다.

다음 코드는 일반적으로 처음 안드로이드를 접하는 사람들이 쓰는 코드들입니다.

[code language=”java”]
public class MainAcivity extends Activity
{

@Override
public void onCreate( Bundle saveInstance )
{
super.onCreate( saveInstance );
setContent( R.layout.main );

TextView textView = ( TextView ) findViewById( R.id.btn_confirm );
textView.setText( "Non-Clicked" );

findViewById( R.id.btn_confirm ).setOnClickListener( new View.OnClickListener()
{

@Override
public void onClick( View view )
{
TextView textView = ( TextView ) findViewById( R.id.btn_confirm );
textView.setText( getClickedText() );
}
} );
}

String getClickedText()
{
return "Clicked!!!";
}

}
[/code]

Activity 내에 이벤트를 핸들링하는 처리나 뷰에 접근하는 코드들이 모두 있습니다.

 

이러한 코드들의 모습은 서버기반 동작시엔 하나의 Activity 내에 네트워크 처리를 위한 쓰레드 처리까지 하게되는 등 코드가 커지면 커질수록 가독성도 떨어지며 유지보수가 힘들어지는 코드로 가기 쉬워집니다.

그래서 기존의 웹에서처럼 좀 더 기능별로 분할하여 코드들을 간결하고 유지보수가 쉬워지도록 하기 위한 방법들이 많이 도입되기 시작하였습니다.

나은 코드들을 위한 패턴들

하지만 화면이 점점 복잡해지게 되고 점점 View 에 의해 제어되는 로직들이 많아짐에 따라 이를 분리하고하는 노력들이 나왔습니다.

다음은 웹 서비스 개발자들에게 가장 쉽게 사용되었던 MVC 패턴을 적용한 코드입니다.

[code language=”java”]
public class MainAcivity extends Activity
{
private MainModel mainModel;

@Override
public void onCreate( Bundle saveInstance )
{
super.onCreate( saveInstance );
setContent( R.layout.main );

mainModel = new MainModel();

TextView textView = ( TextView ) findViewById( R.id.btn_confirm );
textView.setText( "Non-Clicked" );

findViewById( R.id.btn_confirm ).setOnClickListener( new View.OnClickListener()
{

@Override
public void onClick( View view )
{
String text = mainModel.getClickedText();
TextView textView = ( TextView ) findViewById( R.id.btn_confirm );
textView.setText( mainModel.getClickedText() );
}
} );
}
}
[/code]

[code language=”java”]
public class MainModel
{
public String getClickedText()
{
return "Clicked!!!";
}
}
[/code]

View 에 상관없는 로직을 MainModel 로 분리를 하였기 때문에 Activity 는 View 와 Click Event 를 처리하는 모습으로 변화되었습니다.

하지만 Click Event 와 View 에 대한 처리가 함께 있는 것을 유심히 생각해볼 필요가 있습니다. MVC 모델에서 Controller 는 View 에 대한 처리를 직접 하는 것이 아니라 View 에 대한 정보만을 View 에 전달함으로써 화면을 그리는 View 와 동작을 처리하는 로직을 분리하고자 하는데서 시작되었습니다.

헌데 Controller 의 역할을 수행하는 Activity 에서 View 에 대한 직접적인 조작을 수행하는 것은 MVC 모델에 어긋나는 모습을 보여주게 됩니다.

이는 Android 에서 Activity(Fragment) 가 View 와 Controller 두가지의 특성을 모두 가지고 있기 때문에 View 나 Controller 를 한쪽으로 빼게 될 경우 View 에 대한 바인딩이나 처리에서 중복 코드나 일관성을 잃어버리는 코드를 작성할 수 있기 때문입니다. 이를 개선하기 위해서 MVVM 이란 패턴을 적용해봤습니다.

[code language=”java”]
public class MainAcivity extends Activity
{

private MainViewModel mainViewModel;

@Override
public void onCreate( Bundle saveInstance )
{
super.onCreate( saveInstance );
setContent( R.layout.main );

mainViewModel = new MainViewModel( MainActivity.this );

}

}
[/code]

[code language=”java”]
public class MainModel
{

public String getClickedText()
{
return "Clicked!!!";
}

}
[/code]

[code language=”java”]
public class MainViewModel
{

private Activity activity;
private MainModel mainModel;
private TextView textView;

public MainViewModel( Activity activity )
{
this.activity = activity;
this.mainModel = new MainModel();
initView( activity );
}

private void initView( Activity activity )
{

textView = ( TextView ) activity.findViewById( R.id.btn_confirm );
textView.setText( "Non-Clicked" );

activity.findViewById( R.id.btn_confirm ).setOnClickListener( new View.OnClickListener()
{

@Override
public void onClick( View view )
{
String text = mainModel.getClickedText();
textView.setText( text );
}
} );
}

}
[/code]

ViewModel 로 View 에 대한 처리가 분리되었음을 볼 수 있습니다.

하지만 안드로이드에서 MVVM이 가지는 문제점은 View 에 대한 처리가 복잡해질수록 ViewModel 에 거대해지게 되고 상대적으로 Activity 는 아무런 역할도 하지 않는 형태의 클래스로 변모하게 됩니다.

Controller 의 성격을 지닌 Activity 가 실질적으로 아무런 역할을 하지 못하고 ViewModel 에 치중된 모습을 보여줌으로써 다른 형태의 Activity 클래스를 구현한 꼴이 되어버리는 것입니다.

MainViewModel 에 있는 로직을 다시 Activity 로 롤백한다하면 다시 MVC 가 가지고 있는 문제점을 가지게 되는 아이러니한 모습을 가지게 됩니다.

다음은 이러한 View – Model – Controller 의 모습을 명확히 구분하고자 나온 MVP 모델을 보도록 하겠습니다.

[code language=”java”]
public interface MainPresenter
{

void setView( MainPresenter.View view );

void onConfirm();

public interface View
{
void setConfirmText( String text );
}

}
[/code]

[code language=”java”]
public class MainAcivity extends Activity implements MainPresenter.View
{

private MainPresenter mainPresenter;

private Button confirmButton;

@Override
public void onCreate( Bundle saveInstance )
{
super.onCreate( saveInstance );
setContent( R.layout.main );

mainPresenter = new MainPresenterImpl( MainActivity.this );
mainPresenter.setView( this );

confirmButton = ( Button ) findViewById( R.id.btn_confirm );
confirmButton.setOnClickListener( new View.OnClick()
{
@Override
public void onClick( View view )
{
mainPresenter.onConfirm();
}
} );
}

@Override
public void setButtonText( String text )
{
confirmButton.setText( text );
}
}
[/code]

[code language=”java”]
public class MainModel
{

public String getClickedText()
{
return "Clicked!!!";
}

}
[/code]

[code language=”java”]
public class MainPresenterImpl implements MainPresenter
{

private Activity activity;
private MainPresenter.View view;

public MainPresenterImpl( Activity activity )
{
this.activity = activity;
this.mainModel = new MainModel();
}

@Override
public void setView( MainPresenter.View view )
{
this.view = view;
}

@Override
public void onConfirm()
{
if( view != null )
{
view.setConfirmText( mainModel.getClickedText() );
}
}

}
[/code]

MVP 모델의 구분은 다음과 같다.

  1. View 는 실제 view 에 대한 직접적인 접근을 담당한다.
  2. view 에서 발생하는 이벤트는 직접 핸들링하나 Presenter 에 위임하도록 한다.
  3. Presenter 는 실질적인 기능을 제어하는 곳으로써 ViewController 로써 이해하면 쉽다.
  4. Model 은 비지니스 로직을 실질적으로 수행한다.

Presenter : View 는 1:1 로 매칭하며 View Presenter 가 주요 기능을 관장하되 실제 view 에서 발생하는 이벤트는 Presenter (이벤트 : View -> Presenter) 로 전달하여 처리하도록 하고 다시 처리된 결과는 Presenter 가 View 에 전달하도록 하여 처리한다. (처리 결과 표현 : Presenter -> View)

정리

MVC
외부의 모든 이벤트를 Controller(Activity) 에서 처리하되 View 에 관여는 하지 않는 것이 원칙입니다. 하지만 Activity 의 특성상 View 관여하지 않을 수 없습니다. 결국 Android 에서는 MVC 의 구조를 계속적으로 유지하기에는 무리가 있습니다.

MVVM
ViewModel 이 뷰와의 상호작용 및 제어에 집중합니다. ViewModel 에서 직접 뷰의 이벤트를 처리하기 때문에 Controller 의 역할도 함께 수행하게 되어 점점 코드가 집중 되는 구조가 될 수 있습니다. 또한 초기화와 외부 이벤트(뷰에 의한 것이 아닌 이벤트)를 제외 하고는 Activity 의 역할이 모호해지게 됩니다.

MVP
View 에 대한 직접적인 접근이 요구되는 Android 의 Activity 는 직접적인 view 접근은 Activity 가 하도록 하고 이에 대한 제어는 Presenter 가 하도록 하고 있다.

어느 것이 낫다라고 말하기에는 어려운 단계입니다. 하지만 많은 개발자들이 안드로이드에서는 MVC 자체의 모습보다는 MVVM 이나 MVP 가 가장 적합하다는 말을 많이 하고 있습니다. 이는 Activity 가 View 를 포함한 클래스이기 때문에 나타나는 현상이라고 볼 수 있습니다.

참고 블로그

MVC, MVP, MVVM 의 이해 – 이항희

MVC, MVP AND MVVM – tomyrhymond

MVC(Model-View-Controller)

상황

상당수 컴퓨터 시스템의 용도는 데이터 저장소에서 데이터를 검색하여 사용자에게 보여주는 것입니다. 사용자가 데이터를 변경하면 변경된 데이터는 시스템의 데이터 저장소에 저장됩니다. 정보가 주로 데이터 저장소와 사용자 인터페이스 사이에 전달되므로 이 두 개체를 서로 연결하여 코딩의 양을 줄이고 응용 프로그램 성능을 향상시키려고 할 것입니다. 하지만 이 자연스러워 보이는 방법에도 몇 가지 중요한 문제점이 있습니다. 한 가지 문제점은 사용자 인터페이스가 데이터 저장소 시스템보다 훨씬 더 자주 변하기 쉽다는 것입니다. 또 다른 문제점은 데이터 저장소와 사용자 인터페이스를 연결할 때 비즈니스 응용 프로그램에는 데이터 전송을 훨씬 넘어서는 비즈니스 논리를 통합하기 쉽다는 것입니다.

문제점

사용자가 쉽게 개별 파트를 수정할 수 있도록 웹 응용 프로그램의 사용자 인터페이스 기능을 모듈화할 수 있는 방법은 무엇일까요?

강제 요인

이러한 상황에서 다음과 같은 강제 요인이 시스템에 작용하며 위의 문제를 해결할 때 이러한 강제 요인을 조정해야 합니다.

 

  • 사용자 인터페이스 논리는 특히 웹 기반 응용 프로그램에서 비즈니스 논리보다 더 자주 변하는 경향이 있습니다. 예를 들어, 새로운 사용자 인터페이스 페이지가 추가되거나 기존 페이지의 레이아웃이 바뀔 수 있습니다. 무엇보다 웹 기반 씬(thin) 클라이언트 응용 프로그램의 이점 중 하나는 응용 프로그램을 재배포하지 않고도 언제든지 사용자 인터페이스를 변경할 수 있다는 점입니다. 프레젠테이션 코드와 비즈니스 논리가 단일 개체에 연결된 경우, 사용자 인터페이스가 변경될 때마다 비즈니스 논리가 포함된 개체를 수정해야 합니다. 이 경우 오류가 발생하기 쉬우며 사소한 사용자 인터페이스 변경이 있은 후에도 모든 비즈니스 논리를 다시 테스트해야 합니다.
  • 일부 경우에는 응용 프로그램이 동일한 데이터를 다른 식으로 표시합니다. 예를 들어, 분석가는 데이터를 스프레드시트로 보기 원하지만 경영진은 이 데이터를 원 그래프로 보기를 원합니다. 일부 리치(rich) 클라이언트 인터페이스에서는 동일한 데이터를 여러 개의 뷰로 동시에 표시할 수 있습니다. 사용자가 하나의 뷰에서 데이터를 변경하면 시스템이 데이터의 모든 다른 뷰를 업데이트해야 합니다.
  • 보기에 좋고 효율적인 HTML 페이지를 디자인하기 위해서는 대개 복잡한 비즈니스 논리를 개발할 때와는 다른 기술이 필요합니다. 두 가지 기술을 모두 가지고 있는 사람은 흔하지 않습니다. 따라서 이 두 파트에 대한 개발 노력을 구분하는 것이 바람직합니다.
  • 사용자 인터페이스 작업은 대개 프레젠테이션과 업데이트의 두 부분으로 구성됩니다. 프레젠테이션 파트에서는 데이터 소스에서 데이터를 검색하고 서식을 지정한 후 표시합니다. 사용자가 데이터 기반의 작업을 수행하는 경우, 업데이트 파트에서는 데이터를 업데이트하기 위해 제어권을 비즈니스 논리로 넘겨줍니다.
  • 웹 응용 프로그램에서의 단일 페이지 요청은 사용자가 선택한 링크의 작업 처리와 대상 페이지의 렌더링을 결합합니다. 상당수의 경우 대상 페이지가 해당 작업에 직접 연결되지 않을 수 있습니다. 예를 들어, 항목의 목록을 표시하는 간단한 웹 응용 프로그램을 생각해 보겠습니다. 사용자가 목록에 항목을 추가하거나 목록에서 항목을 삭제한 후 주 목록 페이지로 돌아갑니다. 따라서 응용 프로그램이 동일한 HTTP 요청 내에서 상당히 다른 두 명령(추가 또는 삭제)을 실행한 후 동일한 페이지(목록)를 렌더링해야 합니다.
  • 사용자 인터페이스 코드는 비즈니스 논리보다도 더 장치의 영향을 받는 경향이 있습니다. PDA(Personal Digital Assistant) 또는 웹 가능 휴대폰을 지원하기 위해 브라우저 기반 응용 프로그램을 마이그레이션하려는 경우, 사용자 인터페이스 코드는 상당수 교체되어야 하는 반면 비즈니스 논리는 영향을 받지 않을 수 있습니다. 이 두 파트를 명확하게 구분하면 마이그레이션이 촉진되고 비즈니스 논리에 오류가 발생할 위험이 최소화됩니다.
  • 자동화된 사용자 인터페이스용 테스트를 만드는 것은 비즈니스 논리용 테스트를 만드는 것보다 어렵고 많은 시간이 소요됩니다. 따라서 사용자 인터페이스에 직접 연결된 코드의 양을 줄이는 것이 응용 프로그램의 테스트 성능을 향상시킬 수 있습니다.
  • 솔루션MVC(Model-View-Controller) 패턴은 도메인의 모델링과 프레젠테이션 그리고 사용자 입력 기반의 작업을 세 개의 별도 클래스로 구분해 줍니다[Burbeck92].
  • 모델. 모델에서는 응용 프로그램 도메인의 동작(behavior)과 데이터를 관리하고, 대개 뷰로부터의 상태 정보 요청에 응답하고, 대개 컨트롤러로부터의 상태 변경 명령에 응답합니다.
  • . 뷰에서는 정보 표시를 관리합니다.
  • 컨트롤러. 컨트롤러에서는 사용자의 마우스 및 키보드 입력을 해석하여 적절하게 변경하도록 모델과 뷰에서 알려줍니다.그림 1은 세 가지 개체의 구조적 관계를 나타냅니다.

    그림 1: MVC 클래스 구조

    중요한 점은 뷰와 컨트롤러는 모델에 의존하지만 모델은 뷰와 컨트롤러에 의존하지 않는다는 것입니다. 이것이 바로 구분의 가장 큰 이점입니다. 이 구분에서는 시각적 프레젠테이션의 영향을 받지 않으면서 모델을 만들고 테스트할 수 있습니다. 상당수의 리치(rich) 클라이언트 응용 프로그램에서는 뷰와 컨트롤러 사이의 구분이 그다지 중요하지 않으며 실제로 상당수 사용자 인터페이스 프레임워크에서는 두 역할을 하나의 개체로 구현합니다. 반대로 웹 응용 프로그램에서는 뷰(브라우저)와 컨트롤러(HTTP 요청을 처리하는 서버측 구성 요소) 사이의 구분이 명확하게 정의됩니다.

     

    MVC(Model-View-Controller) 는 사용자 인터페이스 논리와 비즈니스 논리를 구분하기 위한 기본적인 설계 패턴입니다. 불행히도 이 패턴의 인기로 여러 잘못된 설명이 나타났습니다. 특히 “컨트롤러”라는 말은 다양한 여러 상황마다 다른 의미를 가지게 되었습니다. 하지만 다행히 웹 응용 프로그램의 등장으로 뷰와 컨트롤러 사이의 구분이 명확해짐에 따라 어느 정도의 모호함이 해결되고 있습니다.

    변형

    Application Programming in Smalltalk-80: How to use Model-View-Controller (MVC)[Burbeck92]에서 Steve Burbeck는 MVC의 두 가지 변형인 수동 모델과 능동 모델에 대해 설명합니다.

     

    수동 모델은 하나의 컨트롤러가 모델을 독자적으로 처리할 때에 사용됩니다. 모델을 수정한 컨트롤러는 해당 모델이 변경되었으며 새로 고쳐야 함을 뷰에 알려줍니다(그림 2 참조). 이 시나리오에서는 모델이 뷰와 컨트롤러로부터 완전히 독립되어 있기 때문에 모델이 상태 변경 사항을 보고할 방법이 없습니다. HTTP 프로토콜이 바로 그 예입니다. 브라우저에는 서버로부터 비동기 업데이트를 받을 수 있는 간단한 방법이 없습니다. 브라우저는 뷰를 표시하고 사용자 입력에 응답하지만 서버의 데이터가 변경되는 것을 감지하지 못합니다. 사용자가 명시적으로 새로 고침을 요청해야만 서버에 변경 사항이 있는지 물어봅니다.

    그림 2: 수동 모델의 동작(behavior)

     

    능동 모델은 컨트롤러의 개입 없이 모델이 상태를 변경할 때에 사용됩니다. 이러한 경우는 다른 소스가 데이터를 변경 중일 때 이 변경 사항을 보기에 반영해야 하는 경우에 발생할 수 있습니다. 증권 시세 표시기를 생각해 보겠습니다. 주식 데이터가 변경되면 외부 소스에서 주식 데이터를 받아 뷰(예: 시세 표시기 및 경보 창)를 업데이트하고 싶습니다. 오직 모델만이 변경된 내부 상태를 감지할 수 있기 때문에 변경이 발생할 경우, 모델은 표시기를 새로 고치도록 뷰에게 통보해야 합니다.

     

    하지만 MVC 패턴을 사용하는 이유 중 하나는 모델이 뷰의 영향을 받지 않기 때문입니다. 모델이 뷰에 변경 사항을 통보해야 한다면 지금까지 피하려고 했던 의존성이 재현되는 것입니다. 다행히도 옵서버 패턴[Gamma95]은 의존성을 재현하지 않고도 상태 변경 사항을 다른 개체에 알려줄 수 있는 메커니즘을 제공합니다. 개별 뷰가 옵서버 인터페이스를 구현하고 모델에 등록합니다. 이 모델은 변경 사항을 구독하는 모든 옵서버의 목록을 추적합니다. 모델이 변경되면 이 모델은 등록된 모든 옵서버를 검색한 후 옵서버에게 변경 사항을 통보합니다. 이 방법을 흔히 “게시-구독”이라고 합니다. 모델에는 뷰에 관한 특정 정보가 전혀 필요 없습니다. 실제로 모델 변경 사항(예: 메뉴 옵션 설정 또는 해제)을 컨트롤러에 알려야 하는 시나리오에서는 모든 컨트롤러가 옵서버 인터페이스를 구현하고 모델 변경 사항을 구독해야 합니다. 많은 뷰가 있는 상황에서는 여러 개의 주제를 정의하고 각 주제로 특정 유형의 모델 변경 사항을 설명하는 것이 좋습니다. 각각의 뷰는 해당 뷰에 관련된 유형의 변경 사항만을 구독할 수 있습니다.

     

    그림 3은 옵서버를 사용하는 능동 MVC의 구조와 모델이 뷰를 직접 참조하지 못하도록 옵서버가 모델을 격리시키는 방법을 보여줍니다.

    그림 3: 능동 모델에서 옵서버를 사용하여 모델과 뷰를 분리

     

    그림 4는 모델이 변경될 때 옵서버가 뷰에 어떻게 변경 사항을 통보하는지 그 방법을 보여줍니다. 불행히도 모델과 뷰의 구분을 UML(Unified Modeling Language) 시퀀스 다이어그램에 적절하게 표시할 수 있는 방법이 없는데 그 이유는 이 다이어그램은 클래스와 인터페이스가 아니라 개체 인스턴스를 표시하기 때문입니다.

    그림 4: 능동 모델의 동작(behavior)

    예제

    ASP.NET에서 MVC(Model-View-Controller 구현)을 참조하십시오.

    테스트 고려 사항

    MVC(Model-View-Controller) 패턴을 사용하면 테스트 성능이 상당히 향상됩니다. 구성 요소가 상호 의존적인 경우 구성 요소(특히 사용자 인터페이스 구성 요소)를 테스트하는 것이 어려워집니다. 이런 구성 요소의 경우는 간단한 기능을 테스트하는 데도 복잡한 설정이 필요합니다. 설상가상으로 오류가 발생한 경우에는 문제를 특정한 구성 요소에 격리시키는 것이 어렵습니다. 바로 이러한 이유 때문에 Architecture를 구성하는 데 있어 구분이 중요해집니다. MVC에서는 데이터의 저장, 표시 및 업데이트 문제를 세 개의 구성 요소로 구분한 후 이 구성 요소를 개별적으로 테스팅할 수 있습니다.

    상호 의존성에 의한 문제는 제외하더라도 사용자 인터페이스 프레임워크의 테스트 자체가 어렵습니다. 사용자 인터페이스를 테스트하기 위해서는 지루한 수작업 테스트(오류가 발생하기 쉬운 테스트)나 사용자 동작을 시뮬레이션하는 테스트 스크립트가 필요합니다. 이 스크립트는 개발에 많은 시간이 소요되며 불안정합니다. MVC가 사용자 인터페이스 테스트의 필요성을 없애주지는 않지만 모델과 프레젠테이션 논리를 구분하기 때문에 프레젠테이션의 영향을 받지 않고 모델을 테스트할 수 있으므로 사용자 인터페이스 테스트 사례의 수가 줄어듭니다.

    결과

    MVC 패턴 주위에 프레젠테이션 레이어를 구성하면 다음과 같은 이점과 단점이 있습니다.

    이점

     

  • 여러 개의 뷰 지원. 뷰와 모델이 구분되어 뷰와 모델이 직접 의존하지 않으므로 사용자 인터페이스에서 동일한 데이터를 여러 개의 뷰로 동시에 표시할 수 있습니다. 예를 들어, 웹 응용 프로그램의 여러 페이지에서 동일한 모델 개체를 사용할 수 있습니다. 또 다른 예는 사용자가 페이지의 모양을 변경할 수 있는 웹 응용 프로그램입니다. 이 페이지는 공유된 모델로부터 동일한 데이터를 표시하지만 다른 방식으로 데이터를 표시합니다.
  • 변경 사항을 수용. 사용자 인터페이스 요구 사항은 비즈니스 규칙보다 더 빨리 변하는 경향이 있습니다. 사용자는 휴대폰 또는 PDA와 같은 신형 장치에 차별화된 색상, 글꼴, 화면 배치 및 지원 수준을 선호할 수 있습니다. 모델은 뷰에 의존하지 않기 때문에 새로운 유형의 뷰를 시스템에 추가하더라도 모델에는 영향을 미치지 않습니다. 따라서 변화의 범위가 뷰로 국한됩니다. 이 패턴은 페이지 컨트롤러 및 프런트 컨트롤러와 같이 이 패턴을 더욱 특수화하기 위한 기반이 됩니다.단점 
  • 복잡성. MVC 패턴에서는 새로운 수준의 간접 참조가 사용하므로 솔루션이 약간 더 복잡해집니다. 또한 사용자 인터페이스 코드의 이벤트 중심적 특성이 강해지므로 디버깅이 더 어려워질 수 있습니다.
  • 빈번한 업데이트의 손실. 모델과 뷰를 분리한다고 해서 모델 개발자가 뷰의 특성을 무시할 수 있다는 의미는 아닙니다. 예를 들어, 모델이 자주 변경되는 경우 뷰에 업데이트 요청이 쇄도할 수 있습니다. 그래픽 디스플레이와 같은 일부 뷰의 경우는 렌더링에 약간의 시간이 소요될 수도 있습니다. 이 결과 뷰가 업데이트 요청을 따라잡지 못할 수 있습니다. 따라서 모델을 코딩할 때도 뷰를 염두에 두는 것이 중요합니다. 예를 들어, 모델은 뷰에 전달되는 단일 통보에 여러 개의 업데이트를 포함할 수 있습니다.변형문서-보기 변형 패턴은 MVC(Model-View-Controller) 의 세 가지 역할을 모드 인식하지만 컨트롤러를 보기에 병합합니다. 문서는 MVC의 모델 역할에 해당합니다. 이 변형 패턴은 기존의 상당수 GUI 플랫폼에 나타납니다. 문서-보기의 가장 좋은 예는 Microsoft Visual C++ 환경의 MFC(Microsoft Foundation Class) 라이브러리입니다. 이 변형 패턴을 사용할 때는 뷰와 컨트롤러가 약간 더 긴밀하게 연결됩니다.

    관련 패턴

    자세한 내용은 다음과 같은 관련 패턴을 참조하십시오.

  • 옵서버. 이 패턴은 뷰와 해당 모델을 동기화시킬 필요성으로 인해 MVC와 관련되어 자주 언급됩니다.
  • 페이지 컨트롤러 및 프런트 컨트롤러는 MVC 패턴의 컨트롤러 부분에 필요한 구현 전략에 대해 설명합니다.참고 자료MVC(Model-View-Controller) 는 1970년대 후반 Trygve Reenskaug가 Smalltalk 플랫폼용으로 개발한 프레임워크로 출발했습니다[Fowler03]. 방금 읽은 이 버전에서는 다음과 같은 문서를 참조합니다.

    [Burbeck92] Burbeck, Steve. “Application Programming in Smalltalk-80: How to use Model-View-Controller (MVC).”University of Illinois in Urbana-Champaign (UIUC) Smalltalk Archive. 다음 웹 사이트에서 구할 수 있습니다: http://st-www.cs.uiuc.edu/users/smarch/st-docs/mvc.html (영문).

    [Fowler03] Fowler, Martin. Patterns of Enterprise Application Architecture. Addison-Wesley, 2003.

    [Gamma95] Gamma, Helm, Johnson 및 Vlissides. Design Patterns: Elements of Reusable Object-Oriented Software. Addison-Wesley, 1995

 

state pattern

제목 없음

#include <iostream>
#include <string>

using namespace std;

class GameLevel
{
public:
 static GameLevel* CreateInstance() { return 0; }
 virtual void SimpleAttack() = 0;
 virtual void TurnAttack() = 0;
 virtual void FlyingAttack() = 0;
};

class GameLevel0 : public GameLevel
{
public:
 static GameLevel* CreateInstance()
 {
 if( pInstance_ == NULL ) pInstance_ = new GameLevel0;
 return pInstance_;
 }

virtual void SimpleAttack() { cout << "SimpleAttack()" << endl; }
 virtual void TurnAttack() { cout << "Not Allowed" << endl; }
 virtual void FlyingAttack() { cout << "Not Allowed" << endl; }

protected:
 static GameLevel0 * pInstance_;
};
GameLevel0* GameLevel0::pInstance_ = 0;

class GameLevel1 : public GameLevel
{
public:
 static GameLevel* CreateInstance()
 {
 if( pInstance_ == NULL ) pInstance_ = new GameLevel1;
 return pInstance_;
 }

virtual void SimpleAttack() { cout << "SimpleAttack()" << endl; }
 virtual void TurnAttack() { cout << "TurnAttack()" << endl; }
 virtual void FlyingAttack() { cout << "Not Allowed" << endl; }

protected:
 static GameLevel1 * pInstance_;
};
GameLevel1 * GameLevel1::pInstance_ = NULL;

class GameLevel2 : public GameLevel
{
public:
 static GameLevel* CreateInstance()
 {
 if( pInstance_ == NULL ) pInstance_ = new GameLevel2;
 return pInstance_;
 }

virtual void SimpleAttack() { cout << "SimpleAttack()" << endl; }
 virtual void TurnAttack() { cout << "TurnAttack()" << endl; }
 virtual void FlyingAttack() { cout << "FlyingAttack()" << endl; }

protected:
 static GameLevel2 * pInstance_;
};
GameLevel2 * GameLevel2::pInstance_ = NULL;

class GamePlayer
{
public:
 GamePlayer() { pGameLevel_ = GameLevel0::CreateInstance(); }

void Updatelevel( GameLevel * pLevel) { pGameLevel_ = pLevel; }

virtual void SimpleAttack() { pGameLevel_->SimpleAttack(); }
 virtual void TurnAttack() { pGameLevel_->TurnAttack(); }
 virtual void FlyingAttack() { pGameLevel_->FlyingAttack(); }

private:
 GameLevel * pGameLevel_;
};

int main()
{
 GamePlayer user1;

user1.SimpleAttack();
 user1.TurnAttack();
 user1.FlyingAttack();

cout << "---------------------" << endl;

user1.Updatelevel( GameLevel1::CreateInstance() );
 user1.SimpleAttack();
 user1.TurnAttack();
 user1.FlyingAttack();

cout << "---------------------" << endl;

user1.Updatelevel( GameLevel2::CreateInstance() );
 user1.SimpleAttack();
 user1.TurnAttack();
 user1.FlyingAttack();

cout << "---------------------" << endl;
 return 0;
}

구현관련 사항
– State패턴은 상태변화에 따라 행위 수행 변경이 자동으로 이루어 지게 만들기 위한 것. 상태변환을 쉽게 하기 위해 설계된것이 아님.