트레이트 – 16.추상 트레이트 메소드

private 가시성 지원

트레이트는 트레이트에서 제시하는 요구명세(requirement)를 조합 클래스(exhibiting class)에서 강제로 구현하게 만드는 추상 메소드를 지원합니다. public, protected 및 private 메소드가 지원됩니다. PHP 8 이전에는 public 및 protected 추상 메소드만 지원되었습니다.

클래스의 추상 메소드는 상속을 통해 하위 클래스에서 구현하여야 하기 때문에, 상속될 수 없는 private 가시성으로 선언될 수 없습니다. 그럼에도 불구하고 PHP 8부터 트레이트 추상 메소드는 private 가시성으로 선언될 수 있습니다. 이것을 이해하려면 앞에서 설명 드린 복사·붙여넣기 메커니즘(copy’n’paste mechanism) 개념을 이해하시면 됩니다. 트래이트는 상하 상속 관계가 아니라 수평적 재사용으로 트레이트를 클래스 내로 조합(insert)하는 것입니다.

트레이트 Hello와 클래스 HelloWorld는 컴파일할 때에 의미 상 아래와 같이 재배치 되며, 이러한 배치에 따라 클래스 HelloWorld 메소드는 트레이트 Hello의 private 메소드에 접근할 수 있습니다. 따라서 추상 트레이트 private 메소드를 조합 클래스에서 구현하는데 전혀 문제가 없습니다.

PHP 8의 변경 사항

그러나 PHP 8에서는 추상 트레이트 메소드 관련해서 좀 많이(?) 수정되었습니다. 이는 RFC 문서 abstract trait method validation의 제안을 PHP에서 받아들여서 PHP 8에 반영한 것입니다. 그 내용으로는 추상 메소드에 private를 지원한 것과 구상 메소드와 속성 호환성 규칙의 검증 기준 중에 초기 값, 즉 메소드 시그니처에 대한 호환성 검증을 추가한 것입니다.

반면에 PHP 8 이전 버전과의 호환성을 유지하기 위하여 일반적인 상속 규칙과 다르게 가시성 수준(visibility level)을 감소할 수 있도록 허용하였습니다. 추상 트레이트 protected 메소드에 대하여 가시성 수준을 private로 감소시키거나, public로 증가시켜 구현할 수 있습니다.

호환성 검증 추가

PHP 8 이전 버전에는 추상 트레이트 메소드를 구현하는 조합 클래스의 메소드 시그니처가 아래의 예와 같이 잘못된 경우에도 이를 제대로 검증하지 못하고 실행되어, 이전 버전에서는 아무런 문제가 없었던 소스가 PHP 8에서는 치명적인 오류를 쏟아 낼 수 있습니다.

위의 예와 같이 이전에 추상 트레이트 메소드를 선언하고 조합 클래스에서 잘못된 시그니처을 사용하여 구현하였던 코드는 PHP 8에서는 실행되지 않습니다. 이러한 코드는 조합 클래스에서 메소드 시그니처를 수정하거나 트레이트에서 추상 메소드를 제거하여 해결하여야 합니다.

PHP 8 이전 버전에서 추상 트레이트 메소드 구현

일반적인 상속 규칙(usual inheritance rule)과 다르게 추상 트레이트 private 메소드는 조합 클래스에서 반드시 바로 구현하며, protected, public 메소드는 하위 클래스에서 구현할 수 있습니다.

그러나 PHP 8 이전 버전에서는, 추상 트레이트 메소드를 private로 지정할 수 없었기 때문에 private로 지정해야 하는 경우에도 protected로 지정하였습니다.

이러한 문제로 조합 클래스에서 private로 지정하여 추상 메소드를 구현하였습니다. 구상 메소드의 경우와 마찬가지로 추상 메소드의 가시성 수준을 감소시킬 수 있습니다. protected로 지정한 추상 메소드를 조합 클래스에서 private 메소드로 구현할 수 있습니다.

PHP 8에서 추상 메소드에 대한 호환성 검증을 추가하면서 일반적인 상속 규칙과 같이 가시성 수준을 증가시킬 수 있게만 하면 바람직하겠지만, 그렇게 되면 PHP 8 이전 버전에서 위와 같이 트레이트 메소드를 protected로 제작한 많은 프로그램을 모두 수정해야 하는 문제가 발생합니다. 결국 PHP 8 이전 버전과의 하위 호환성을 위해 일반적인 상속 규칙과 다르게 가시성 수준을 감소시킬 수 있도록 하였습니다.

조합 클래스에서 시그니처를 추상 메소드와 다르게 코딩한 것은 코딩한 제작자의 책임도 있다고 보겠지만, 가시성 문제는 순전히 PHP가 private 추상 메소드 기능을 제공하지 않아서 발생한 문제이니만큼 PHP에 책임(?)이 있다고 봐야지요.

PHP 8 이전 버전에서는 추상 private 메소드가 금지되었기 때문에 어쩔 수 없었지만, PHP 8과 호환되어야 하는 코드를 작성할 때는 정보 은닉이 필요할 때는 명확하게 추상 private 메소드를 사용하는 것이 좋습니다.

다중 트레이트에서 동일하게 선언된 추상 메소드 선언

동일한 이름의 메소드를 가진 두 개의 트레이트가 조합되었을 때 발생하는 충돌을 방지하기 위해, insteadof 또는 as 연산자로 충돌되는 메소드를 배제시키거나 별칭을 부여하여 해결하였습니다. 그러나 두 개의 트레이트에 동일한 이름의 추상 메소드가 선언되더라도, 실제적인 구현은 조합 클래스에서 정의되므로 충돌 되지 않습니다. 따라서 아래와 같이 메소드 충돌 방지를 위한 조치가 필요치 않습니다.

추상 트레이트 메소드 구현

추상 트레이트 private 메소드는 조합 클래스에서 반드시 바로 구현하여야 합니다. 조합 클래스를 상속 받은 하위 클래스에서 구현하게 되면, 하위 클래스에서 트레이트 private 메소드를 접근 할 수 없게 되며, 치명적인 오류(Fatal error)가 발생됩니다.

그러나 추상 트레이트 protected 메소드는 조합 클래스를 상속 받은 하위 클래스에서 구현할 수 있습니다.

답글 남기기