티스토리 툴바


저작권 안내 (펌 하실 때)
  • 저작권자표시 Yes, 상업적이용 No , 컨텐츠변경 No

UI 관련된 코드를 살펴보다가 리팩토링이 필요한 부분이 있어 점진적으로 리팩을 하고 있는데, 최근에 State 패턴을 적용한 사례가 있어 내용을 공유해 본다.


구현된 결과물은 다음과 같은 UI를 갖고 있다.



검색 탭을 누르면 '전체' 또는 '비디오'와 '블로그'로 분류해서 검색할 수 있다. 검색 탭을 누르면 기본적으로 '전체'를 기준으로 검색을 하며, '비디오'와 '블로그'를 선택하면 각 분류에 해당하는 컨텐츠만 검색한다. 비슷하게 마이로그 탭을 누르면, 기본적으로 즐겨찾기와 전체가 선택된다. 내역을 누르면 동일하게 전체가 기본으로 선택된다. 즐겨찾기와 내역 둘 다 비디오와 블로그를 선택하면 각 분류에 속한 결과만 조회된다.


이러한 기능을 제공하는 UI를 구현하기 위한 코드는 다음과 같은 방식으로 구현되어 있었다. (아래 코드는 이해를 돕기 위해 간단하게 정리한 것이다. 실제로는 더 복잡하게 되어 있다.)


public class SearchUI extends .... {


    private int selectMode; // 0 검색, 1 즐겨찾기, 2 내역

    private int tabMode; // 0 전체, 1 비디오, 2 블로그


    public void onClick(View v) { // 탭이 눌릴 때 이벤트 처리

        switch(v.getId()) {

        case R.id.search:

            selectMode = 0;

            tabMode = 0;

            break;

        case R.id.mylog:

            selectMode = 1;

            tabMode = 0;

            break;

        case R.id.favorite:

            selectMode = 1;

            tabMode = 0;

            break;

        case R.id.history:

            selectMode = 2;

            tabMode = 0;

            break;

        case R.id.all:

            tabMode = 0;

            break;

        case R.id.video:

            tabMode = 0;

            break;

        case R.id.blog:

            tabMode = 0;

            break;

        }

    }


    // selectMode와 tabModel에 따라 알맞은 분기

    private void process() {

        if (selectMode == 0) {

            if (tabMode == 0) {

                processSearchAll();

            } else if (tabMode == 1) {

                processSearchVideo();

            } else if (tabMode == 2) {

                processSearchBlog();

            }

        } else if (selectMode == 1) {

            if (tabMode == 0) {

                processGetAllFavoriteContent();

            } else if (tabMode == 1) {

                processGetFavorite();

            } else if (tabMode == 2) {

                processGetFavoriteBlog();

            }

        } else if (selectMode == 2) {

            ... // 비슷한 코드

        }

    }

}


만약 이 상태에서 탭이라도 하나 덩 생긴다면,,,,,,  Oh NO!!! 생각도하기 싫어진다. 그래서 새로운 요구사항이 생기기 전에 위 코드의 설계를 변경하기로 했다. 설계를 변경하기에 앞서 코드의 의미를 먼저 파악해 보았다. 파악한 결과는 아래와 같다.


* 검색을 누르면

======> 검색 상태가 되고, 전체 검색 기능을 실행한다.

* 마이그로나 즐겨찾기를 누르면

======> 즐겨찾기 상태가 되고, 전체 즐겨찾기 조회 기능을 실행한다.

* 내역을 누르면

======> 내역 상태가 되고, 전체 내역 조회 기능을 실행한다.


* 검색 상태에서

======> 전체를 누르면, 전체 검색 기능을 실행한다.

======> 비디오를 누르면, 비디오 검색 기능을 실행한다.

======> 블로그를 누르면, 블로그 검색 기능을 실행한다.

* 즐겨찾기 상태에서

======> 전체를 누르면, 전체 즐겨찾기 조회 기능을 실행한다.

======> 비디오를 누르면, 비디오 즐겨찾기 조회 기능을 실행한다.

======> 블로그를 누르면, 블로그 즐겨찾기 조회 기능을 실행한다.

* 내역 상태에서

======> 전체를 누르면, 조회한 전체 내역 조회 기능을 실행한다.

======> 비디오를 누르면, 조회한 비디오 내역 조회 기능을 실행한다.

======> 블로그를 누르면, 조회한 블로그 내역 조회 기능을 실행한다.


위에서 공통된 부분을 추려보면 다음과 같은 형식을 따른다.


* 특정한 상태에서 

======> 전체를 누르면, 전체 관련 기능 실행한다.

======> 비디오를 누르면, 비디오 관련 기능 실행한다.

======> 블로그를 누르면, 블로그 관련 기능 실행한다.

* 특정한 상태가 되면 전체 관련 기능 실행한다.


뭔가 상태에 변화가 있고, 동일한 기능 요청에 대해서 상태에 따라 다르게 동작해야 한다. 빙고! 상태(State) 패턴이다. 이제 위 코드를 상태 패턴을 이용해서 변경해 보자. 상태 패턴에 대한 설명은 필자가 예전에 정리한 패턴 스터디 발표 자료를 참고하기 바란다.


상태의 종류


상태는 크게 다음의 세 종류가 있다.

  • 검색 상태
  • 즐겨찾기 상태
  • 내역 상태
따라서, 다음과 같이 타입을 구성할 수 있을 것이다.


상태의 기능 정의


상태에서 제공할 기능은 다음의 3개다.

  • 전체 관련 기능
  • 비디오 관련 기능
  • 블로그 관련 기능
따라서, State 인터페이스는 다음과 같의 3개의 기능을 제공하도록 정의할 수 있다.

public interface State {
    void all();
    void video();
    void blog();

    void enter();
}

State의 3개의 하위 타입들은 각각 위 메서드를 알맞게 오버라이딩 할 것이다. 잠깐, 위에서 enter() 메서드는 상태가 바뀔 때 해당 상태로의 진입 처리를 하기 위한 메서드이다. 예를 들어, SearchState의 enter() 메서드는 화면상에 검색어를 입력하는 화면을 보여줄 수 있을 것이다.

상태를 정리했으니 다음으로 할 작업은 상태를 사용하도록 코드를 수정하는 것이다. 바뀐 코드는 아래와 같다.

public class SearchUI extends .... {


    private State currentState;


    public void onClick(View v) { // 탭이 눌릴 때 이벤트 처리

        switch(v.getId()) {

        case R.id.search:

            state = new SearchState();

            state.enter();

            break;

        case R.id.mylog:

        case R.id.favorite:

            state = new FavoriteState();

            state.enter();

            break;

        case R.id.history:

            state = new HistoryState();

            state.enter();

            break;

        case R.id.all:

            state.all();

            break;

        case R.id.video:

            state.video();

            break;

        case R.id.blog:

            state.blog();

            break;

        }

    }

}


앞의 코드와 비교해보자. 훨씬 간단해진 것을 알 수 있다. 하지만, 위 코드는 상태 변이와 기능 요청코드-State 객체를 생성하는 코드와 State의 all() 등의 메서드를 실행하는 코드-가 섞여 있다. 이는 다음과 같이 두 개의 리스너로 구분하는 것으로 정리할 수 있다.


public class SearchUI extends .... {


    private State currentState;


    private OnClickListener changeStateLitener = new OnClickListener() {

        void onClick(View v) { // 탭이 눌릴 때 이벤트 처리

            switch(v.getId()) {

                case R.id.search:

                    state = new SearchState();

                    break;

                case R.id.mylog:

                case R.id.favorite:

                    state = new FavoriteState();

                    break;

                case R.id.history:

                    state = new HistoryState();

                    break;

                default:

                    throw new ..... // 익셉션

        }

        state.enter();

    }


    private OnClickListener requestFunctionLitener = new OnClickListener() {

        void onClick(View v) { // 탭이 눌릴 때 이벤트 처리

            switch(v.getId()) {

            case R.id.all:

                state.all();

                break;

            case R.id.video:

                state.video();

                break;

            case R.id.blog:

                state.blog();

                break;

        }

    }

}


State 패턴을 적용한 결과 다음과 같은 일을 덜 고생하면서 할 수 있게 되었다.

  • 새로운 상태 추가 : if-else 블록의 경우 새로운 상태 추가와 함께 비슷한 if-else 블록이 이곳 저곳에 추가되어 코드가 더 복잡해진다. 하지만, State 패턴을 적용하면, State 객체를 사용하는 코드 입장에서 즉, 위 코드에서는 SearchUI 입장에서 새로운 State 구현 클래스와 해당 State로 전이하는 코드만 추가해 주면 된다.

  • 새로운 기능 요청 : if-else 블록의 경우 새로운 기능 요청이 추가되면 비슷한 if-else 블록이 이곳 저곳에 추가되어 코드가 더 복잡해진다. 하지만, State 패턴을 적용하면 각각의 State 구현체들이 해당 기능을 구현하므로 코드의 복잡도가 전혀 증가되지 않는다. SearchUI 입장에서 새로운 기능을 실행하는 코드만 추가해 주면 된다.


저작자 표시 비영리 변경 금지
Posted by 최범균 madvirus

댓글을 달아 주세요

  1. maruldy 2012/11/09 16:15  댓글주소  수정/삭제  댓글쓰기

    안녕하세요.
    Android 어플 개발 도중에, State 패턴을 적용해 보려다 궁금한 점이 있어서 댓글을 남깁니다..
    평소 최범균님 블로그에서 공부를 하려다가 댓글 남기는건 처음인데요..
    바쁘지 않으신 시간에 한번만 봐주시면 감사하겠습니다.. ^^;

    현재 작성중인 MaruActivity 클래스를 살펴보니, 여러 상태를 비교적으로 명확하게 가지게 되어서,
    기존에 if 문 들로 구분하던 부분을 각각의 State 로 분리 했습니다.
    그 결과 MaruAcitvity 는 SubState 라는 interface 를 구현한 AState, BState, CState, DState 를 갖게 되었습니다.

    또한 MaruActivity 에서 if 문으로 비교된 코드들이 상당히 간결화 되고, 화면 객체를 컨트롤 하는 Method 들 위주로 남아있게 되었습니다.

    문제는, 각각의 SubState 들로 옮겨진 코드들의 결과값을 MaruActivity 의 화면에 표현해 주기 위해서 각각의 SubState 들이 MaruActivity 의 직접적인 Reference 를 가지게 되었습니다.

    MaruActivity 와 SubState 가 각각 상호간에 참조를 하게 된겁니다.

    지금 이에 대한 처방으로는 MaruActivity 에서는 객체가 없어질 때, 각 SubState 로 부터 MaruActivity 의 Reference 를 해제해주는 메소드를 호출해주고 있습니다.

    하지만, 그동안 책에서 봤었던 State 패턴에서는 중간에 다른 클래스를 두어서 MaruActivity 와 SubState 들 간의 coupling 을 낮추는 방식을 많이 추천하고 있는데요.. 지금 이대로 가는게 좋을지, 아니면 중간에 다른 클래스를 두어서 결합도를 더 낮추는게 맞을지를 어떤 기준으로 판단해야 할지 몰라서 여쭙니다..

    • madvirus 2012/11/12 00:18  댓글주소  수정/삭제

      SubState가 MaruActivity에 요구하는 것이 무엇인지 확인하고, 그걸 추상화한 인터페이스로 분리하면 어떨까요? 대충 아래와 같이요.

      public class MaruActivity extends ... implements SomeIF {

      SubState ss = new ConcreteSubState(this);
      }

      public class ConcreteSubState implements SubState {
      private SomeIF someIF;

      public ConcreteSubState(SomeIF someIF) {
      this.someIF = someIF;
      }

      public void doAnything() {
      ...
      someIF.some();
      ...
      }
      }

    • maruldy 2012/11/12 10:22  댓글주소  수정/삭제

      답변 감사합니다.

      답변해주신 부분 참고해 보겠습니다. ^^
      좋은 한 주 되세요~~ :)

페이스북 친구들과 댓글을 공유하고 싶다면 아래를 이용해주세요.

OOP 소개 자료 입니다.


2012년 11월 10일 자료: 객체, 캡슐화, 추상화

OOP 소개 20121110_2page.pdf



2012년 10월 13일 자료: 객체, 캡슐화, 추상화

OOP 소개 20121013_2page.pdf



2012년 9월 22일 자료: 객체, 캡슐화, 추상화

OOP 소개 20120922.pdf


저작자 표시 비영리 변경 금지
Posted by 최범균 madvirus

댓글을 달아 주세요

  1. 홍구 2012/10/04 21:28  댓글주소  수정/삭제  댓글쓰기

    감사히 봅늬다

페이스북 친구들과 댓글을 공유하고 싶다면 아래를 이용해주세요.

필자는 요즘 안드로이드 코드를 만져보고 있다. 안드로이드를 공부하면서, 그리고 기존에 만들어 놓은 코드들을 보면서 그리고 책에 예제로 나와 있는 코드를 보면서 뭔가 정리하고 싶은 게 생겼는데, 그 부분은 바로 Handler의 사용과 관련된 부분이다. 특히 비동기로 서버에서 데이터를 읽어와 UI에 결과를 반영해주는 코드는 정말 최악인데, 이 때의 Handler 처리 과정을 정리해보면 다음과 같은 방식으로 이루어진다.




뭔가 복잡하다. 위 그림은 관련된 코드를 그림 하나로 표현한 것이어서 그나마 덜 복잡해 보이고는거고, 실제 코드에서는 handleMessage() 안에 상단히 많은 case 문이 위치하고, handler 코드와 someMethod() 사이에 많은 다른 코드들이 위치하게 된다. 그래서 버튼을 눌렀을 때 기능이 어떻게 동작하는지 확인하려면 코드를 이리 저리 탐색하면서 돌아다녀야 하고, 이는 코드 분석을 힘들게 만들어준다.


위 코드의 문제점들을 나열해보면 다음과 같은 것들이 있다.

  • 처리 결과를 Message를 이용해서 Handler에 전달할 때, 각 메시지를 구분하기 위해 정수값(message.what)을 사용한다. 
  • 비동기 방식 동작 코드에 Handler가 전달되어 불필요한 의존이 발생한다.
  • 무엇보다도 응집도가 떨어져 분석을 어렵게 만든다.

Handler는 결과에 따라 다른 행동을 하기 위해서 int 값을 사용한다. 아래 코드는 전형적인 Handler의 handleMessage() 메서드의 구현 모습이다.


Handler handler = new Handler() {

    public void handlerMessage(Message msg) {

        switch(msg.what) {

            case 0: // <--- 0 의 의미는?

                // msg.obj 사용

                // UI 처리 코드

                break;

            case 1: // <-- 새로운 조건 추가될 때 마다 case 비교 구문 추가

                // UI 처리 코드

            ...

            case SOME_CONSTANT: // <-- 새로운 조건 추가될 때 마다 상수 추가하는 번거로움

            ....

        }

    }

}


위 case에서 0과 1은 무엇을 의미할까? 의미를 부여하기 위해 상수를 사용할 수도 있다. 하지만, 그런다고 문제가 끝나는 것은 아니다. 새로운 조건이 추가되면 그 때 마다 case가 하나 추가되고 상수이름을 생각해서 추가해 주어야 한다. 실수도 서로 다른 상수가 같은 값을 갖게 되면 디버깅 하느라 (헛)수고 좀 해야 한다.


또 다른 문제는 비동기 처리를 위해 Handler 객체가 이곳 저곳으로 전달된다는 것이다. 예를 들어, HTTP 요청을 비동기로 보내고 그 결과를 받기 위해 HTTP 요청을 처리하는 객체에 Handler를 전달하고 (앞서 그림에서 과정1), 응답이 도착하면 Handler에 메시지를 보내는 방법으로 응답 결과를 UI에 반영한다. (앞서 그림에서 과정4와 과정5) HTTP 요청을 처리하는 객체는 사실 Handler와는 전혀 상관이 없는 객체이다. 그런데, 비동기로 들어오는 응답을 UI에 반영하기 위해 부득이 Handler 객체에 대한 의존이 발생하는 것이다.


Handler 객체가 날아다니기 시작할 때의 또 다른 문제점은 실제 handler.sendMessage()를 수행하는 Handler가 어떤 Handler인지 찾아다녀야 한다는 점이다. 예를 들어, HTTP 요청 처리 객체를 여러 액티비티에서 호출한다고 해 보자. 이 때, HTTP 요청 처리 객체 내부적으로 사용되는 handler가 어느 액티비티 객체의 멤버인지 확인하려면 코드를 탐색해야 하는 (헛)수고를 하게 된다.


낮아지는 응집도(cohesion)!


결국 앞에서 Handler의 사용으로 인해 발생하는 문제점은 코드의 응집도를 확 낮춰준다는 것이다. 이 말은 관련된 코드가 이곳 저곳으로 퍼진다는 것을 뜻하는데, 예를 들어, 뭔가 버튼 클릭시 서버에서 데이터를 읽어와 UI에 반영하는 코드는 다음과 같이 퍼지게 된다.

  • 버튼 클릭시 호출되는 메서드: HTTP 요청 처리 객체에 handler를 주고 비동기로 처리를 요청한다.
  • HTTP 요청 객체: 응답 처리 결과를 handler를 통해 전달한다.
  • handler의 handlerMessage() 메서드: case 문으로 메시지를 확인하고, 응답 처리 결과를 이용해서 UI를 조작한다.

뭔가 하나의 처리 흐름을 이해하기 위해 관련된 코드들이 위와 같이 세 곳에 퍼지게 된다. 이는 코드를 산만하게 이곳 저곳에 배치시켜서 가독성을 바닥으로 확 떨어뜨리고 유지보수 하기 어렵게 만들어준다. 게다가 Handler가 각각의 메시지를 구분할 수 있도록 나름대로 상수나 코드 값을 불필요하게 정의해 주어야 한다. (이런 거 은근 머리 아프다.)


커맨드와 콜백을 활용한 해결


앞에서 비동기 처리 상황에서 Handler를 사용하기 위해 발생했던 응집도 저하 문제를 어떻게 해결하면 좋을까 곰곰히 생각하다가, 다음의 두 가지를 생각해봤다.

  • 콜백
  • 커맨드

두 가지가 앞의 문제들을 어떻게 없애주는지 살펴보자.


1단계: 콜백 전달해서 호출해 달라고 요청하기


Handler가 이곳 저곳으로 전달되는 이유는 비동기로 처리한 결과를 UI로 받아야 하기 때문이다. 하지만, Handler가 다른 객체에 파라미터로 전달되는 순간부터 한 기능과 관련된 코드가 이곳 저곳으로 퍼지게 되고 이는 심각한 응집도 저하를 불러 일으킨다. 이를 방지하기 위한 한 가지 방법이 콜백을 사용하는 것이다. 우선, 다음과 같이 간단한 콜백 인터페이스를 만든다.


public interface AsyncCallBack<T> {

    public void call(T result);

}


이제 뭔가 비동기로 데이터를 읽어오는 코드가 위 콜백을 통해서 결과를 전달하도록 변경해 보자. 예를 들면, 아래와 같이 코드가 만들어질 것이다.


public class AsyncJob {

    public void loadSomeData(final AsyncCallBack<SomeData> callback) {

        new Thread() {

            // 데이터를 외부에서 로딩

           SomeData data = ....;

           callback.call(data);

        }.start();

    }

}


버튼이 클릭되었을 때 위 기능을 사용해야 한다면, 이제 Handler를 전달하는 대신 아래와 같이 콜백 객체를 전달하도록 코드가 바뀐다.


public void onClick(View v) {

    asyncJob.loadSomeData(

        new AsyncCallBack<SomeData> () {

            public void call(SomeData result) {

                // 여기서 뭔가 handler에 결과 전달함

                Message msg = handler.obtainMessage();

                msg.what = 0;

                msg.obj = result;

                handler.sendMessage(msg); // TODO: 아직도 데이터 로딩 후 UI 조작 코드가 분리됨 

            }

        }

    );

}


handler = new Handler() {

    public void handleMessage(Message msg) {

        switch(msg.what) {

            case 0:

                SomeData someData = (SomeData) msg.obj;

                // UI 조작

        }

    }

}


위와 같이 바꾸면, 이제 비동기 작업을 수행하는 코드에 Handler 객체를 전달할 필요가 없어진다. 위 코드의 경우 AsyncJob.loadSomeData() 메서드는 비동기로 데이터를 읽어오면 파라미터로 전달된 콜백 메서드를 호출한다. 즉, 콜백 메서드에서 Handler에 보낼 메시지를 생성하고 전달하게 되는 것이다.


하지만, 아직도 데이터를 읽어와서 UI를 변경한다는 하나의 작업과 관련된 코드가 onClick() 메서드와 Handler의 handleMessage() 메서드에 분리되어 있다. 이는 커맨드를 이용해서 해결할 수 있다.


2단계: 커맨드 패턴을 활용해서 Handler 처리 코드에서 what 제거하기


두 번째 과정은 커맨드를 사용하는 것이다. 우선 다음과 같은 간단한 커맨드 인터페이스를 정의한다.


public interface Command {

    void execute();

}


그리고, 다음과 같이 위 Command 만을 전문적으로 처리하는 CommandHandler 클래스를 정의한다.


public class CommandHandler {

    private Handler handler = new Handler() {

        public void handleMessage(Message msg) {

            if (msg.obj instanceof Command) {

                ((Command) msg.obj).execute();

            }

        }

    }

    public void send(Command command) {

        Message message = handler.obtainMessage();

        message.obj = command;

        handler.sendMessage(message);

    }

}


CommandHandler의 handler는 switch 문이 없다. 단지 Message를 통해서 전달받은 Command 객체의 execute() 메서드를 호출하는 것만 한다. CommandHandler의 send() 메서드는 Message에 Command 객체를 담아 handler에 전송한다.


이제 Handler 대신 CommandHandler를 사용하도록 코드를 변경해 보자. 


// 어떤 작업

final SomeData data = getSomeData();


// Command 전송

commandHandler.send(new Command() {

    public void execute() {

        // UI 변경

        changeUI(data);

    }

});


Handler를 사용하는 코드와 비교해 보자. 위와 같이 함으로써 다음과 같은 변화가 발생했다.

  • Handler.handleMessage() 메서드의 switch 문에서 사용할 상수 값 불필요
  • 작업 결과물을 UI에 반영하는 코드가 한 곳으로 모임
    • Handler를 사용하는 경우, Handler에 메시지를 보내는 코드와 Handler의 handleMessage() 메서드에서 분리되서 들어감
즉, 개발자는 Handler가 메시지를 구분하기 위해서 사용할 상수값(앞서 봤던 0, 1, SOME_CONSTANT 등)을 고민해서 만들 필요가 없고, 그 상수값을 맞출 필요가 없다. 그냥 알맞은 Command 구현 객체만 만들어서 전달해주면 된다.

그리고 응집도가 높아졌다. Handler를 사용하게 되면 데이터를 읽어오는 코드와 읽어온 데이터를 사용하는 코드가 분리되어 코드의 응집도가 떨어지고 이로 인해 코드 분석이나 변경이 어렵게 되는데, 위의 경우는 한 곳에 몰려 있어서 좀 더 빠르게 코드를 분석하고 이해할 수 있게 된다.

두 가지를 합친 결과 코드

두 가지를 합치면 앞서의 onClick() 메서드가 다음과 같이 바뀐다.

public void onClick(View v) {

    asyncJob.loadSomeData( // 1. 데이터 로딩 실행

        new AsyncCallBack<SomeData> () { 

            public void call(final SomeData result) { // 2. 로딩된 데이터 수신

                commandHandler.send(

                    new Command() {

                        public void execute() {

                            changeUI(result); // 3. 수신한 데이터를 이용 UI 변경

                        }

                    }

                );

            }

        }

    );

}


최초에 봤던 코드와 비교해 보자. 앞서 Handler를 전달하는 방식의 코드에서는 데이터가 처리되는 과정을 살펴보려면 이벤트 처리 코드(onClick과 같은), 비동기로 데이터 읽어오는 코드, 그리고 Handler 코드를 살펴봐야 했다. 또한, 그 과정에서 Handler가 각각의 메시지를 식별할 수 있도록 상수도 정의해 주어야 했다.

이랬던 것들이, 콜백과 커맨드를 사용함으로써 위 코드와 같이 응집도를 확 높일 수 있게 되었다. 관련 코드가 한 곳에 모여 있기 때문에 이제 데이터의 처리 과정을 확인하기 위해 이곳 저곳 흩어져 있는 코드를 살펴볼 필요가 없어 졌고, 메시지 구분 식별값 등 불필요한 고민을 하지 않아도 된다. 물론, 임의 객체 사용으로 인해 코드가 약간 복잡해 보이지만, 코드의 응집도가 낮아져서 코드 분석을 어렵게 하는 비용과 비교해보면 약간의 복잡한 코드가 비용이 (훨씬~~~) 작다.

자바에 클로저만 있었어도 더 간결한 코드를 얻을 수 있겠지만 지금은 이 정도만으로도 만족할 만한 코드 결과물을 얻을 수 있게 되었다.


관련자료



저작자 표시 비영리 변경 금지
Posted by 최범균 madvirus

댓글을 달아 주세요

  1. 하늘섬 2012/09/14 09:44  댓글주소  수정/삭제  댓글쓰기

    이런한 경우에능 안드로이드 프래임웍에서 제공하는 AsyncTask를 사용하시면 매우 편리합니다.

    • madvirus 2012/09/14 10:13  댓글주소  수정/삭제

      AsyncTask가 편리하긴 하죠. 첫 번째 그림에서는 Thread를 사용했지만 그 부분이 AsyncTask로 바뀌면 됩니다.

  2. bluepoet 2013/03/19 19:54  댓글주소  수정/삭제  댓글쓰기

    이번 스타리그 앱의 쓰레드작업 부분을 이걸로 적용해봐야겠네요.

    무엇보다 코드 가독성과 응집도가 높아진게 맘에 듭니다.

    좋은글 잘 읽었습니다^^

  3. 응집도 2013/04/23 12:28  댓글주소  수정/삭제  댓글쓰기

    결합도를 낮추고 응집도를 높히는 작업을 하신듯 하네요~^^

페이스북 친구들과 댓글을 공유하고 싶다면 아래를 이용해주세요.