ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Design Pattern] 템플릿 메소드 패턴 (Template Method Pattern)
    공부/디자인 패턴 2021. 6. 6. 18:13

    템플릿 메소드 패턴이란 여러 클래스에서 공통적으로 호출, 사용하는 메소드들을 상위 클래스에서 정의하고 이 상위 클래스를 상속 받은 하위 클래스에서 세부 동작을 구현하는 패턴을 말합니다. 이는 객체 지향 언어로 개발할 때, 해당 패턴을 알지 못해도 많이 사용하고 접하는 패턴입니다.

    템플릿 메소드 패턴을 사용하면 중복 코드를 제거할 수 있고 상속을 받은 하위 클래스의 역할이 줄어 로직 관리가 편합니다. 하지만 추상 메소드가 많아진다면 클래스의 관리가 복잡해 진다는 단점이 있습니다.

    클래스 다이어그램

    예제

    예를 들어, 아래와 같이 공항에 도착해 티켓을 발권받는 과정을 코딩한다면 아래와 같습니다.

    public class IncheonAirport {
        public void check() {
            checkPassport();
            checkBaggage();
            checkTicket();
            checkFlight();
            checkAirportLocation();
        }
        public void checkPassport() {
            System.out.println("여권 검사");
        }
        public void checkBaggage() {
            System.out.println("수하물 검사");
        }
        public void checkTicket() {
            System.out.println("항공권 검사");
        }
        public void checkFlight() {
            System.out.println("비행기 검사");
        }
        public void checkAirportLocation() {
            System.out.println("인천 공항 출발인지 검사");
        }
    }
    
    public class GimpoAirport {
        public void check() {
            checkPassport();
            checkBaggage();
            checkTicket();
            checkFlight();
            checkAirportLocation();
        }
        public void checkPassport() {
            System.out.println("여권 검사");
        }
        public void checkBaggage() {
            System.out.println("수하물 검사");
        }
        public void checkTicket() {
            System.out.println("항공권 검사");
        }
        public void checkFlight() {
            System.out.println("비행기 검사");
        }
        public void checkAirportLocation() {
            System.out.println("김포 공항 출발인지 검사");
        }
    }

    위 예제를 보면 마지막 어느 공항에서 출발하는지만 다르고 과정은 동일해 중복코드가 발생한 것을 볼 수 있습니다. 템플릿 메소드 패턴을 사용하면 중복 코드를 제거할 수 있습니다.

    public abstract class Airport {
        public void check() {
            checkPassport();
            checkBaggage();
            checkTicket();
            checkFlight();
            checkAirportLocation();
        }
    
        //    공통 메소드
        public void checkPassport() {
            System.out.println("여권 검사");
        }
    
        public void checkBaggage() {
            System.out.println("수하물 검사");
        }
    
        public void checkTicket() {
            System.out.println("항공권 검사");
        }
    
        public void checkFlight() {
            System.out.println("비행기 검사");
        }
    
        //    추상 메소드
        public abstract void checkAirportLocation();
    }
    
    public class IncheonAirport extends Airport {
        public void checkAirportLocation() {
            System.out.println("인천 공항 출발인지 검사");
        }
    }
    
    public class GimpoAirport extends Airport {
        public void checkAirportLocation() {
            System.out.println("김포 공항 출발인지 검사");
        }
    }

    추상 클래스에 공통적으로 사용하는 메소드를 정의함으로써 중복 코드를 제거하고 이를 상속받은 하위 클래스의 역할을 줄이는 효과를 줄 수 있습니다. 템플릿 메소드 패턴에서 추상 클래스와 인터페이스의 결합으로 진행하면 (추상 골격 구현, skeletal implementation) 더욱 좋은 시너지를 발휘합니다.

    public interface Flight {
        void checkBaggage();
    }
    
    public interface Ticket {
        void checkPassport();
        void checkTicket();
        void checkFlight();
        void checkAirportLocation();
    }
    
    public abstract class AbstractAirport implements Flight, Ticket{
        public void check() {
            checkPassport();
            checkBaggage();
            checkTicket();
            checkFlight();
            checkAirportLocation();
        }
    
        //    공통 메소드
        public void checkPassport() {
            System.out.println("여권 검사");
        }
    
        public void checkBaggage() {
            System.out.println("수하물 검사");
        }
    
        public void checkTicket() {
            System.out.println("항공권 검사");
        }
    
        public void checkFlight() {
            System.out.println("비행기 검사");
        }
    
        //    추상 메소드
        public abstract void checkAirportLocation();
    }
    
    public class GimpoAbstractAirport extends AbstractAirport {
        public void checkAirportLocation() {
            System.out.println("김포 공항 출발인지 검사");
        }
    }
    
    public class IncheonAirport extends AbstractAirport {
        public void checkAirportLocation() {
            System.out.println("인천 공항 출발인지 검사");
        }
    }

    파이썬

    파이썬에서도 아래와 같이 abc 모듈을 사용해 구현할 수 있습니다.

    from abc import ABCMeta, abstractmethod
    
    class AirportMixin(metaclass=ABCMeta):
        def check(self):
            self.check_baggage()
            self.check_passport()
            self.check_ticket()
            self.check_flight()
            self.check_airport_location()
    
        def check_baggage(self):
            print("수하물 검사")
    
        def check_passport(self):
            print('여권 검사')
    
        def check_ticket(self):
            print('티켓 검사')
    
        def check_flight(self):
            print('항공편 검사')
    
        @abstractmethod
        def check_airport_location(self):
            pass
    
    class IncheonAirport(AirportMixin):
        def check_airport_location(self):
            print('인천공항에서 출발')
    
    class GimpoAirport(AirportMixin):
        def check_airport_location(self):
            print('김포공항에서 출발')

    이를 위 자바의 추상 골격 구현과 동일하게 표현하면 다음과 같습니다.

    from abc import ABCMeta, abstractmethod
    
    class FlightMixin(metaclass=ABCMeta):
        def check_baggage(self):
            print("수하물 검사")
    
    class TicketMixin(metaclass=ABCMeta):
        def check_passport(self):
            print('여권 검사')
    
        def check_ticket(self):
            print('티켓 검사')
    
        def check_flight(self):
            print('항공편 검사')
    
        @abstractmethod
        def check_airport_location(self):
            pass
    
    class AirportMixin(FlightMixin, TicketMixin):
        def check(self):
            self.check_baggage()
            self.check_passport()
            self.check_ticket()
            self.check_flight()
            self.check_airport_location()
    
    class IncheonAirport(AirportMixin):
        def check_airport_location(self):
            print('인천공항에서 출발')
    
    class GimpoAirport(AirportMixin):
        def check_airport_location(self):
            print('김포공항에서 출발')
    

    파이썬에서는 다중 상속을 지원하고 있어, 아래와 같이 구현할 수도 있지만 디버깅이 어려워지므로 지양하는 것이 좋습니다.

    from abc import ABCMeta, abstractmethod
    
    class FlightMixin(metaclass=ABCMeta):
        def check_baggage(self):
            print("수하물 검사")
    
    class TicketMixin(metaclass=ABCMeta):
        def check_passport(self):
            print('여권 검사')
    
        def check_ticket(self):
            print('티켓 검사')
    
        def check_flight(self):
            print('항공편 검사')
    
        @abstractmethod
        def check_airport_location(self):
            pass
    
    class AirportMixin:
        def check(self):
            self.check_baggage()
            self.check_passport()
            self.check_ticket()
            self.check_flight()
            self.check_airport_location()
    
    class IncheonAirport(FlightMixin, TicketMixin, AirportMixin):
        def check_airport_location(self):
            print('인천공항에서 출발')

    댓글