37. abstract - 1. 소개
상속과 구현이라는 두 가지 기능을 하나의 클래스로 합칠 수 있을까?
= abstract
37. abstract - 2. 형식
부모 클래스 앞에 abstract가 붙어 있고
그 안에 있는 메소드 b 앞에도 abstract가 붙어 있고
이 메소드를 정의할 것으로 예상되는 코드의 정의는 없고
메소드의 형태만이 존재한다면
이 클래스를 상속받는 자식 클래스들은
반드시 b라는 메소드를 구현해야 한다.
새로 코딩
앞에 abstract가 적힌 메소드는
자식이 반드시 구현하도록 강제했기 때문에
자기 자신은 구현한 것이 없어야 한다.
37. abstract - 3. 사례(1) 템플릿 메소드 패턴
디자인 패턴이란?
우리가 오랫동안 SW를 만들다 보면
그 SW를 만드는 과정에서 부딪히는 문제 중에서
주기성을 갖고 자주 등장하는 문제들이 있다.
그러한 문제들을 형식화/패턴화시켜서 정리해 놓은 코딩기법이
디자인 패턴이다.
디자인 패턴의 각각의 기법들은 마치 건축의 공법과 비슷해서
각각의 기법별로 이름을 부여해놨다.
그래서 다른 사람과 의사소통할 때
"이번에는 템플릿 패턴을 쓸 거야."
"이번에는 전략 디자인 패턴을 쓸 거야."
라는 식으로 얘기를 함으로써 의사소통이 쉬워진다.
디자인 패턴 중에서 abstract를 사용하는 템플릿 메소드 패턴을 살펴볼 것이다.
이 php 파일에는 htmlPage라는 클래스가 정의되어 있고
그 클래스를 인스턴스화해서 render라는 메소드를 호출하면
이렇게 생긴 페이지가 된다.
두 줄을 주석처리하고 아래 두 줄을 실행시키면
이런 페이지가 뜬다.
이것을 페이지 보기를 하면 아래와 같이
일반적인 텍스트를 보여주는 그런 클래스이다.
HtmlPage와 Textpage 모두
똑같은 정보를 똑같은 형식으로 보여주는 클래스이다.
차이가 있다면 위는 Html 형식,
아래는 텍스트 형식이라는 것 뿐이다.
즉 정보의 구조 등은 같은데
그것이 표현되는 형식이 다를 경우에
템플릿 메소드 디자인 패턴이라는 것을 이용해서
더 정제된/규격화된 클래스를 만들 수 있다.
TextPage는 이렇게 생겼다.
그리고 이것을 호출할 때는 아래와 같은 코드를 통해서 한다.
즉 TextPage를 인스턴스화하고
render라는 메소드를 호출한다.
render라는 메소드는 TextPage라는 클래스 안에 없다.
그러면 php는 AbstractPageTemplate를 찾아갈 것이다.
AbstractPageTemplate 안에는 render라는 메소드가 정의되어 있는데,
그 메소드에서는 Template라는 메소드를 호출하고 있다.
Template이라는 메소드는 위 그림의 회색으로 표시한 부분인데, 여기에는
AbstractPageTemplate 아래에 있는
header 메소드를 호출하고
그 결과를 $result라는 변수에 담고
article을 호출하고
그 결과를 $result라는 변수에 담고
footer를 호출하고
그 결과를 $result라는 변수에 담는다는 순서가 정의되어 있다.
그 결과($sresult)를 리턴하고
render가 template의 결과를 다시 리턴하면
여기에는 우리가 원하는 정보가 결합되어 있는 텍스트가 echo에 의해서 출력된다.
가장 중요한 얘기가 지금부터 나온다.
이 template 함수에서는
header, article, footer라는 세 개의 메소드를 호출하고 있다.
그리고 그 메소드들은 아래에 있는데,
각각의 메소드는 abstract이다.
그래서 AbstractPageTemplate 클래스도 당연히 abstract이다.
그렇기 때문에 이 AbstractPageTemplate을 상속받는
TextPage 클래스에게는
반드시 header, article, footer를 구현해야 하는 책임이 있다.
그래서 TextPage라는 클래스는
header, article, footer의 정보를 각각 리턴하고 있다.
즉 중요한 것은,
Template라는 메소드에는 우리가 출력하고자 하는 정보의 구조가 들어있다.
header, article, footer가 호출되어야 한다는 것과
그 순서가 정의되어 있다.
그리고 header가 무엇이고, article이 무엇이고, footer이 무엇인지는
여기에는 추상화되어있다. (비어있다.)
그리고 그것을 구현하는 것은
자식 클래스가 이렇게 구체적으로 구현하는 것이다.
똑같이 HtmlPage라는 클래스를
render라는 메소드로 호출하면
HtmlPage라는 클래스에는 render라는 메소드가 없기 때문에
AbstractPageTemplate 클래스에 있는 render가 샐행되고,
똑같이 template이라는 메소드에 있는 코드들이 실행된다.
하지만 이 각각의 메소드는 AbstractPageTemplate에는 정의돼있지 않기 때문에
HtmlPage라는 클래스가 정의하고 있다.
header, article, footer를 html의 형식에 맞게 정의하고 있다.
그럼 자연스럽게 이 코드를 실행했을 때 결과는
TextPage와 구조는 같지만
형식이 Text가 아닌 html이 된다.
즉 AbstractPageTemplate 클래스와
TextPage 클래스와 HtmlPage 클래스의 관계는
AbstractPageTemplate이
어떤 구조와 어떤 순서로 메소드가 호출될 것인지를 정의하고 있고
TextPage와 HtmlPage는
각각의 header, article, footer가 어떤 모습이어야 하는지
어떤 정보를 출력할 것인지 결정하고 있다.
그래서 AbstractPageTemplate의
template 메소드는 final을 쓰고 있다.
∵ 자식이 저것을 상속해서 그 순서를 함부로 바꾸는 것을 규제하기 위해서
하지만 header, article, footer는 부모가 결정할 수 없기 때문에
abstract로 강제한 것이다.
그러면서 render는 final도 붙어있지 않고
abstract도 붙어있지 않기 때문에
만약 자식 클래스를 만들 때,
이런 방식($this->template)만으로는 렌더링하기가 불가능할 경우에는
※ HtmlPage 클래스의 내용이다.
이것처럼 render라는 메소드를 overrride해서
render를 호출하기 전에
html태그와 /html 태그를 추가해놓을 수 있다.
즉 이 예제에는
일반적인 상속의 예로서의 render와
상속을 못하게 하는 예로서의 final과
상속을 반드시 하도록 강제하는 abstract가
모두 포함된 예제이다.
37. abstract - 4. 템플릿 메소드 패턴 구현
주석과 같은 텍스트 페이지를 출력하는 클래스를 만들어보자.
\n은 줄바꿈 기호이다.
주황색 텍스트들이 굉장히 길고,
각각의 행마다 데이터를 가져오는 방법이 서로 완전히 다르다고 상상해보자.
그럴 경우 우리가 이 데이터를 render라는 메소드에
그냥 이렇게 쓸어담아 놓으면 굉장히 복잡해서 골치가 아플 것이다.
그러면 여기 있는 각각의 주황색 데이터를 생성하는 것을
메소드로 추출하는 기법을 사용해서 할 수 있다.
코딩을 할 때 코드가 많지 않더라도
메소드로 추출하는 것이 가독성이나 관리 면에서 훨씬 좋다.
어떠한 이유에 의해서 이 정보들을 php가 아니라 html로 출력해야 된다면
어떻게 하면 좋을까?
이렇게 만들고 보니
html 페이지와 text 페이지는
정보 구조가 동일하기 때문에
어떤 부분은 같고 어떤 부분은 다르다.
render 메소드 부분은 완전히 같다.
코드의 중복이 발생했으므로
HtmlPage와 TextPage의 공통의 조상을 만들겠다.
우리가 render라는 메소드에서
HtmlPage와 TextPage 각각의 클래스가 갖고 있는
header, article, footer를 수정하려고 했더니
걔네들이 private이다.
private는 그 클래스 안에서만 사용할 수 있으므로
부모가 사용할 수 없다.
그래서 private를 다 protected로 바꿨다.
이 중에서 html 페이지를 통해서 만들어지는 코드는
하늘색으로 선택한 부분이다.
그런데 html 태그가 빠져있다.
render에는 html 태그를 넣을 수 없다.
HtmlPage 클래스의 커서 부분에 넣는 것도 방법이긴 한데
Page 클래스의 render 메소드를 override해보겠다.
그러고 나서 보니
html 태그를 제외한 부분은
Page 클래스의 render 부분과 완전히 같다.
그래서 메소드를 추출하는 기법을 이용해서
이 부분을 공통으로 빼겠다.
이렇게 해놓으니
Page를 상속받고 있는
TextPage와 HtmlPage는
어떠한 순서로 어떠한 정보를 출력할 것인가에 대한 정보는
template이라는 메소드 안에 들어있고
각각의 정보의 구체적인 내용들은
자식 클래스의 header, article, footer를 통해서 표현된다.
나중에 다른 사람이 Page 클래스를 상속받아서
다른 구체적인 Page를 만들려고 할 때,
header, article, footer를 반드시 구현하도록 강제할 필요가 있다.
그러려면 인터페이스를 쓰거나
클래스를 abstract로 만들어야 한다.
클래스를 abstract로 만들어 보겠다.
그리고 Page 클래스를 상속받아서 클래스를 만드는 사람이
template이라는 메소드를 overrride해서
template 메소드의 동작 방법을 달리하면
형식이 무너질 수 있다.
그래서 template을 override하지 못하도록 final을 붙여준다.
Page 클래스의 이름을 AbstractPageTemplate라고 바꿔주어도 좋다. (취향)
우리가 본 Template Method 패턴이 여기 있다.
'PHP > 생활코딩' 카테고리의 다른 글
6/1(3) 생활코딩 - PHP 기본수업 - Interface (0) | 2020.06.01 |
---|---|
6/1(2) 생활코딩 - PHP 기본수업 - 상속 (0) | 2020.06.01 |
6/1 생활코딩 - PHP 기본 수업 - 컴포저 (0) | 2020.06.01 |
5/31(5) 생활코딩 (0) | 2020.05.31 |
5/31(3) 생활코딩 (0) | 2020.05.31 |
댓글