SELF STUDY/Design Pattern

[디자인패턴] 템플릿 메소드 패턴 | Template Method Pattern | 안드로이드 예제

호이호이호잇 2021. 12. 30. 09:33
728x90
반응형

 

지난 게시글에서 퍼사드 패턴을 출근 준비로 표현했던 것 처럼..!

이번 템플릿 메소드 패턴도 동일하게 정리를 해봐야겠다.!

 

출근 준비에 대한 것을 큰 카테고리? 로 보면

알람 -> 씻기 -> 옷입기 -> 이동수단 으로 볼 수 있다.

 

A와 B의 준비 단계를 보자!

A : 알람 -> 씻기 -> 옷입기 (원피스) -> 이동수단 (자동차)

B : 알람 -> 씻기 -> 옷입기 (파란티) -> 이동수단 (버스)

이다.!

여기서  A,B가 다르지 않고 동일한 과정은 빨간색으로, 다른 과정은 파란색으로 표시를 해보았다.

 

이를 코드로 표현을 해보자.!

 

"A" 의 준비 과정

class ReadyToWorkA {
    public void start(TextView tv) {
        alarm(tv);
        wash(tv);
        clothes(tv);
        vehicle(tv);
    }

    public void alarm(TextView tv) {
        tv.setText( (String) tv.getText() + "\nalarm!!" );
    }

    public void wash(TextView tv) {
        tv.setText( (String) tv.getText() + "\nwash!!" );
    }

    public void clothes(TextView tv) {
        tv.setText( (String) tv.getText() + "\nclothes - red!!" );
    }

    public void vehicle(TextView tv) {
        tv.setText( (String) tv.getText() + "\nvehicle - car!!" );
    }
}

 

"B" 의 준비과정

class ReadyToWorkB {
    public void start(TextView tv) {
        alarm(tv);
        wash(tv);
        clothes(tv);
        vehicle(tv);
    }

    public void alarm(TextView tv) {
        tv.setText( (String) tv.getText() + "\nalarm!!" );
    }

    public void wash(TextView tv) {
        tv.setText( (String) tv.getText() + "\nwash!!" );
    }

    public void clothes(TextView tv) {
        tv.setText( (String) tv.getText() + "\nclothes - blue!!" );
    }

    public void vehicle(TextView tv) {
        tv.setText( (String) tv.getText() + "\nvehicle - bus!!" );
    }
}

 

만약의 "C"의 준비 과정이 추가된다면 어떻게 해야할까?

class ReadyToWorkC {
    public void start(TextView tv) {
        alarm(tv);
        wash(tv);
        clothes(tv);
        vehicle(tv);
    }

    public void alarm(TextView tv) {
        tv.setText( (String) tv.getText() + "\nalarm!!" );
    }

    public void wash(TextView tv) {
        tv.setText( (String) tv.getText() + "\nwash!!" );
    }

    public void clothes(TextView tv) {
        tv.setText( (String) tv.getText() + "\nclothes - hawaiian!!" );
    }

    public void vehicle(TextView tv) {
        tv.setText( (String) tv.getText() + "\nvehicle - train!!" );
    }
}

그런데.. D,E.....Z까지의 유형이 추가된다면 

하나하나 추가해 주기엔 중복된 메소드 (start, alarm, wash) 의 경우에는 계속 동일한 메소드를 추가하는 것은 불필요한 작업처럼 느껴진다.

 

이럴떄 사용 할 수 있는 디자인패턴이 템플릿 메소드 패턴 이다.

 


템플릿 메소드 패턴이란?

메소드에서 알고리즘의 골격을 정의해서 알고리즘의 구조는 유지하면서 특정 단계를 재정의 할 수 있게 하는 패턴.

알고리즘의 각 단계를 정의하고, 그 중 한 개 이상의 단계가 서브 클래스에 의해 제공 될 수 있는 패턴.


위의 예시로 다시 쉽게 설명하자면,  

알람 -> 씻기 -> 옷입기 -> 이동수단 에서

각 단계의 순서를 나타내는 start 함수를 포함한 중복되는 단계를 골격으로 정의하고,

중복되지 않은 단계 는 서브 클래스에 의해 재정의 될 수 있게 하는 것이다.

 

일단 내가 만든 예제에서 중복되는 부분은 아래와 같다.

 

public void start(TextView tv) {
    alarm(tv);
    wash(tv);
    clothes(tv);
    vehicle(tv);
}

public void alarm(TextView tv) {
    tv.setText( (String) tv.getText() + "\nalarm!!" );
}

public void wash(TextView tv) {
    tv.setText( (String) tv.getText() + "\nwash!!" );
}

중복되지 않은 부분이다.

"A"

public void clothes(TextView tv) {
    tv.setText( (String) tv.getText() + "\nclothes - red!!" );
}

public void vehicle(TextView tv) {
    tv.setText( (String) tv.getText() + "\nvehicle - car!!" );
}

 

"B"

public void clothes(TextView tv) {
    tv.setText( (String) tv.getText() + "\nclothes - blue!!" );
}

public void vehicle(TextView tv) {
    tv.setText( (String) tv.getText() + "\nvehicle - bus!!" );
}

"C"

public void clothes(TextView tv) {
    tv.setText( (String) tv.getText() + "\nclothes - hawaiian!!" );
}

public void vehicle(TextView tv) {
    tv.setText( (String) tv.getText() + "\nvehicle - train!!" );
}

 

위 과정을 템플릿 메소드로 표현하기 위해서는 final 과 abstract 두 가지 용어의 뜻을 알아야한다.

내가 정의한 두 용어의 뜻은..!

- final : 오버라이드를 못하도록 정의하는 것.

- abstract : "추상" 으로 재 정의가 꼭 필요한 것.

 

이제 위 과정을

ReadyToWork abstract class로 구현을 해보자!

 

먼저 각 단계의 순서를 알려주면서 class의 골격 역할을 해주는 부분이다.

위 예제에서 준비하는 순서는 A,B...Z까지 동일하기 때문에

순서는 재정의가 되면 절대 안되는 부분으로 final 로 표현을 해야한다.!

public final void start(TextView tv) {
    alarm(tv);
    wash(tv);
    clothes(tv);
    vehicle(tv);
}

 

alarm 과 wash의 경우 A,B,C가 동일해 A~C까지는 재정의 할 필요는 없지만,

달라질 경우 재정의가 가능하도록 할 수 있도록 구현해준다.

public void alarm(TextView tv) {
    tv.setText( (String) tv.getText() + "\nalarm!!" );
}

public void wash(TextView tv) {
    tv.setText( (String) tv.getText() + "\nwash!!" );
}

 

clothes와 vehicle 의 경우 다른 경우가 대부분이기 때문에, 무조건 재정의 하도록 구현해준다.

abstract void clothes(TextView tv);

abstract void vehicle(TextView tv);

여기서 !!

위처럼 아무 내용도 없는 코드를 후크 라고 한다.


후크란?

추상클래스에서 선언되는 메소드

기본적인 내용만 있거나 아무 코드도 없는 메소드

서브클래스에서 필요에 따라 재정의해 사용하는 메소드

로 정의할 수 있다.

 

자바의 스윗프레임을 예로 들 수 있다.


 

전체적인 소스를 보면, 아래와 같이 표현 할 수 있다.

abstract class ReadyToWork {
    public final void start(TextView tv) {
        alarm(tv);
        wash(tv);
        clothes(tv);
        vehicle(tv);
    }

    public void alarm(TextView tv) {
        tv.setText( (String) tv.getText() + "\nalarm!!" );
    }

    public void wash(TextView tv) {
        tv.setText( (String) tv.getText() + "\nwash!!" );
    }

    abstract void clothes(TextView tv);

    abstract void vehicle(TextView tv);
}

 

위에 템플릿을 상속받아 D의 경우를 만들어 보았다.

D 의 경우 준비 순서는 

no alarm -> 씻기 -> 옷 입기 (오렌지 옷) -> 이동 수단 (택시) 이다.

class ReadyToWorkD extends ReadyToWork {
    @Override
    public void alarm(TextView tv) {
        tv.setText( (String) tv.getText() + "\nno alarm !!");
    }

    @Override
    void clothes(TextView tv) {
        tv.setText( (String) tv.getText() + "\nclothes - orange !!");
    }

    @Override
    void vehicle(TextView tv) {
        tv.setText( (String) tv.getText() + "\nvehicle - taxi !!");
    }
}

이 D의 순서를 보기 위해서는

ReadyToWorkD readyToWorkD = new ReadyToWorkD();
readyToWorkD.start(textView);

로 해주면 된다.

 

동작 화면은 아래와 같다.

- 초기 상태 

 

- 버튼을 누르면 D 의 준비 과정이 보인다.!

 

전체적인 소스는 아래와 같다.!

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;

public class TemplateMethodActivity extends Activity implements View.OnClickListener {

    TextView textView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_template_method);

        init();
    }

    private void init(){
        findViewById(R.id.buttonToReady).setOnClickListener(this);
        textView = findViewById(R.id.textView);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.buttonToReady:
                ReadyToWorkD readyToWorkD = new ReadyToWorkD();
                readyToWorkD.start(textView);
                break;
        }
    }
}
class ReadyToWorkA {
    public void start(TextView tv) {
        alarm(tv);
        wash(tv);
        clothes(tv);
        vehicle(tv);
    }

    public void alarm(TextView tv) {
        tv.setText( (String) tv.getText() + "\nalarm!!" );
    }

    public void wash(TextView tv) {
        tv.setText( (String) tv.getText() + "\nwash!!" );
    }

    public void clothes(TextView tv) {
        tv.setText( (String) tv.getText() + "\nclothes - red!!" );
    }

    public void vehicle(TextView tv) {
        tv.setText( (String) tv.getText() + "\nvehicle - car!!" );
    }
}

class ReadyToWorkB {
    public void start(TextView tv) {
        alarm(tv);
        wash(tv);
        clothes(tv);
        vehicle(tv);
    }

    public void alarm(TextView tv) {
        tv.setText( (String) tv.getText() + "\nalarm!!" );
    }

    public void wash(TextView tv) {
        tv.setText( (String) tv.getText() + "\nwash!!" );
    }

    public void clothes(TextView tv) {
        tv.setText( (String) tv.getText() + "\nclothes - blue!!" );
    }

    public void vehicle(TextView tv) {
        tv.setText( (String) tv.getText() + "\nvehicle - bus!!" );
    }
}

class ReadyToWorkC {
    public void start(TextView tv) {
        alarm(tv);
        wash(tv);
        clothes(tv);
        vehicle(tv);
    }

    public void alarm(TextView tv) {
        tv.setText( (String) tv.getText() + "\nalarm!!" );
    }

    public void wash(TextView tv) {
        tv.setText( (String) tv.getText() + "\nwash!!" );
    }

    public void clothes(TextView tv) {
        tv.setText( (String) tv.getText() + "\nclothes - hawaiian!!" );
    }

    public void vehicle(TextView tv) {
        tv.setText( (String) tv.getText() + "\nvehicle - train!!" );
    }
}

abstract class ReadyToWork {
    public final void start(TextView tv) {
        alarm(tv);
        wash(tv);
        clothes(tv);
        vehicle(tv);
    }

    public void alarm(TextView tv) {
        tv.setText( (String) tv.getText() + "\nalarm!!" );
    }

    public void wash(TextView tv) {
        tv.setText( (String) tv.getText() + "\nwash!!" );
    }

    abstract void clothes(TextView tv);

    abstract void vehicle(TextView tv);
}

class ReadyToWorkD extends ReadyToWork {
    @Override
    public void alarm(TextView tv) {
        tv.setText( (String) tv.getText() + "\nno alarm !!");
    }

    @Override
    void clothes(TextView tv) {
        tv.setText( (String) tv.getText() + "\nclothes - orange !!");
    }

    @Override
    void vehicle(TextView tv) {
        tv.setText( (String) tv.getText() + "\nvehicle - taxi !!");
    }
}

 

끄읐!

 

 

 

 

 

 

728x90
반응형