안드로이드 디자인 패턴
앱을 개발하는 동안 사용 할 수 있는 Java
디자인 패턴에 대해서 소개합니다. 디자인 패턴은 소프트웨어 설계에서 자주 활용되는 구조의 모음이나, 나타나는 다양한 패턴들을 정리한 것입니다. 각 카테고리 별 패턴을 살펴보고 각 패턴이 실질적으로 Android에서 어떻게 적용되는지 살펴보겠습니다.
- 생성
- Builder
- Dependency Injection
- Singleton
- 구조
- Adapter
- Facade
- 행위
- Command
- Observer
- Model View Controller
- Model View ViewModel
1. 생성 패턴
생성 패턴이란 객체 생성에 대해서 다루고 상황에 적절한 객체를 만드는 것입니다. 생성에 관련된 패턴을 사용하면 쉽고 간단하게 객체 생성을 할 수 있습니다.
1) Builder
- 복잡한 인스턴스를 조립하여 만드는 구조
- 생성자에 파라미터가 많은 클래스인 경우 빌더 패턴을 사용하여 가독성을 높일 수 있음
- Android에서는
NotificationCompat.Builder
와 같은 클래스를 사용 할 때Builder
패턴이 나타남
Notification notification =new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.ic_notification)
.setContentIntent(pendingIntent)
.setTicker(message)
.build();
AlertDialog.Builder
에서도Builder
패턴을 사용
new AlertDialog.Builder(this)
.setTitle("Builder Dialog")
.setMessage("Builder Dialog Message")
.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
@Override public void onClick(DialogInterface dialog, int which) {
}
})
.setPositiveButton("OK", new DialogInterface.OnClickListener() {
@Override public void onClick(DialogInterface dialog, int which) {
}
})
.show();
- 위
Builder
들은 단계적으로 진행되며 사용자에게 중요한 부분들을 지정 할 수 있음
2) Dependency Injection
Dependency Injection
은 구성 요소간의 의존 관계가 소스코드 내부가 아닌 외부의 설정파일등을 통해 정의- 필요한 모든 것은 이미 존재하기 때문에 가져다 사용하기만 하면 됨
Dependency Injection
은 새로운 객체를 생성 할 때 필요한 객체를 제공- Android에서는 네트워크 클라이언트, 이미지 로더, SharedPreferences와 같이 앱의 다양한 지점에서 동일한 객체에 접근 해야할 때 사용하면 좋음
Dagger2
는 Android에서 가장 많이 쓰이는 Dependency Injection 프레임워크@Module
클래스 어노테이션을 달고@Provides
메소드 어노테이션를 사용하여 주입하면 됨
@Module
public class AppModule {
@Provides
SharedPreferences provideSharedPreferences(Application app) {
return app.getSharedPreferences("setting", Context.MODE_PRIVATE);
}
}
- 위
Module
은 필요한 객체를 생성하고 초기화함 - 그런 다음
Component
인터페이스를 생성하여 모듈과 주입 할 클래스를 선언 ```java @Component(modules = AppModule.class) interface AppComponent {
}
- `Component`는 종속성이 어디서 오는 것인지(Module)와 어디로 갈 것인지를 연결
- 마지막으로 `@Inject` 어노테이션을 상요하여 필요에 따라 종속성을 요청
```java
@Inject
SharedPreferences sharedPreferences;
Dependency Inject
을 사용하면 Activity에서 SharedPreferences 객체가 어떻게 생겼는지를 Activity가 알 필요없이 로컬 스토리지를 사용 할 수 있음- 자세한 구현 정보는
Dagger2 Users Guide
를 참조
3) Singleton
- 클래스, 생성자가 여러 차례 생성되더라도 실제로 생성되는 객체는 하나
- 최초 생성 이후에 호출된 생성자는 최초의 생성자가 생성한 객체를 리턴
public class SampleSingleton {
private static SampleSingleton instance = null;
private SampleSingleton() {
}
public static SampleSingleton getInstance() {
if (instance == null) {
synchronized(this) {
if (instance == null) {
instance = new SampleSingleton();
}
}
}
return instance;
}
}
- 위 클래스에서 static 메소드
getInstance()
안에 생성자를 숨겨서 객체를 한번만 초기화 하도록 구현 SampleSingleton.getInstance();
를 사용하여 객체에 엑세스- 그러면 앱 전체에서 동일한 클래스의 인스턴스를 사용 할 수 있음
Singleton
은 여러 개체에서 액세스 할 수 있기 때문에 예측하기 어려운 예기치 않은 부작용을 경험할 수 있음
2. 구조 패턴
구조패턴이란 기존에 생성되어 있는 클래스를 새롭게 구현하는 클래스에 맞지 않는 경우에 사용합니다. 작은 클래스의 합성을 통해 더 큰 클래스 구조를 형성하기 위한 패턴입니다.
1) Adapter
- Android에서 대표적으로 Adapter패턴을 보이는 클래스는
RecyclerView.Adapter
RecyclerView.Adapter
클래스는 앱의 비지니스 로직(Model)과 RecyclerView를 연결하는 역활RecyclerView.Adapter
은 데이터 아이템(Model)에 접근하고 해당 데이터를 이용하여 아이템에 View를 그리는 역활을 수행
public class SampleAdapter extends RecyclerView.Adapter<SampleViewHolder> {
private List<Test> tests;
public SampleAdapter() {
tests = new ArrayList();
}
@Override
public SampleViewHolder onCreateViewHolder(ViewGroup parent, int position) {
// Create View Holder
}
@Override
public void onBindViewHolder(SampleViewHolder holder) {
// View Bind
}
@Override
public int getItemCount() {
return tests.size();
}
public void add(Test item) {
tests.add(item);
}
}
RecyclerView
은Test
에 대해서 무엇인지 모름SampleAdapter
가Test
와RecyclerView
을 연결하기 때문에RecyclerView
은Test
에 대해서 알 필요가 없음
2) Facade
Facade
패턴은 다른 인터페이스 세트를 사용하기 쉽게 만드는 하이-레벨 인터페이스를 제공- 라이브러리 바깥쪽의 코드가 라이브러리의 안쪽 코드에 의존하는 일을 감소
- 공통적인 작업에 대해 간편한 메소드들을 제공
- Activity에서 도서 목록이 필요한 경우 저장소, 캐시 및 API등의 내부 동작을 이해하지 않고 하나의 객체를 사용하여 해당 목록을 요청하는 경우
Retrofit
은 Facade 패턴을 구현하는데 도움이 되는 REST API 호출 라이브러리
public interface BooksApi {
@GET("/books")
void listBooks(Callback<List> callback);
}
- 클라이언트는 책의 목록을 얻기위해서는
listBooks()
만 호출하면 됨 Retrofit
을 사용하면Interceptor
및OkHttpClient
를 이용하여 클라이언트가 현재 진행 중인 상황을 알지 못해도 캐싱 동작을 제어 할 수 있음
3. 행위 패턴
- 객체의 행위를 조직, 관리, 조합하는데 사용하는 패턴
- 객체들이 다른 객체와 상호작용하는 방식을 규정
- 각각 다른 객체들과 통신하는 방법과 객체의 책임을 규정하여 복잡한 행위들을 관리 할 수 있도록 함
- 두 객체 간의 관계에서부터 앱의 전체 아키텍처에까지 영향을 미침
1) Command
- 요청을 수행하는 객체가 별도의 수신자를 알지 못해도 요청을 실행하는 방식
- 요청을 별도의 객체로 캡슐화하고 전송
- EventBus는 Android에 대표적인 Command 패턴을 보여주는 라이브러리
- 발신자는 EventBus의
post()
메소드를 이용하여 요청을 발신하고 수신자는@Subscribe
어노테이션을 이용하여 수신을 처리public class MessageEvent { }
- 발신한 메시지를 생성
eventBus.register(this);
@Subscribe()
public void onEvent(MessageEvent event) {
}
- 수신자 할당
eventBus.post(event);
- 발신자에서 메시지 발송
2) Observer
- 객체간의 1:1 의존성을 정의
- 하나의 객체가 상태를 변경하면 모든 종속된 객체에 자동으로 통지되고 업데이트를 수행
- API 호출과 같이 불확실한 시간의 작업을 위해서 사용 할 수 있으며 사용자 입력을 처리하는데 사용 할 수 있음
- RxJava(RxAndroid)를 사용하면 앱 전체에 해당 패컨을 구현 할 수 있음
apiService.getData(someData)
.observeOn(AndroidSchedulers.mainThread())
.subscribe (/* an Observer */);
- 값(이벤트)를 방출 할 Observable 객체를 정의
- Observable들은 한 번 또는 연속적으로 스트림, 값, 이벤트를 방출함
- Subscriber는 이러한 값을 수신하고 도작한 대로 응답
- 예를들어, API 호출을 작성하고 서버에서 응답을 처리할 Subscriber를 지정 할 수 있음
3) Model View Controller
- Model View Controller(MVC)는 현재 여러 플랫폼에서 사용중인 아키텍처 패턴
- UI로부터 비지니스 로직을 분리하여 서로 영향 없이 쉽게 고칠 수 있는 어플리케이션 개발 가능
- Android에서는 MVC로 프로젝트를 설정하는것이 쉬움
- Model : 데이터 클래스, 실제 세계를 ‘모델링’
- View : 시각적 클래스, 사용자에게 표시되는 모든 항목
- Controller : Model과 View 사이의 접착제, View를 업데이트하고 사용자 입력을 받아 Model을 변경
- 가능한 많은 레이아웃과 리소스 로직을 Android XML으로 이동하면 View 레이어를 깔끔하게 유지 할 수 있음
- 하지만 Activity(Controller)에 너무 많은 소스가 집약되기 때문에 좋은 방법이 아님
4) Model View Persenter
- MVC 패턴 기반의 변형 패턴
- MVC 패턴과 다르게 입력을 View에서 처리하며 Presenter에서는 View의 인스턴스를 가지고 1:1 관계를 유지
- View에서 이벤트가 발생하면 Presenter에게 전달해주고 Presenter는 해당 이벤트에 따른 Model을 조작하고 그 결과를 바인딩을 통해 View에게 통보를 하여 View를 업데이트
- 또한 View를 추상화하여 따로 View를 구현하지 않아도 UI 로직에 대한 Test가 가능하며 유지보수가 쉬움
5) Model View ViewModel
- MVC 패턴 기반의 변형 패턴
- ViewModel 객체는 Model과 View사이의 “접착제”이지만 Controller와 다르게 동작
- View에 대한 명령을 표시하고 View를 Model에 바인딩
- Model이 업데이트되면 해당 View는 ViewModel을 통해 자신을 업데이트
- 마찬가지로 사용자가 View와 상호 작용할 때 ViewModel은 Model을 자동으로 업데이트