[PHP OOP] 3. 추상 클래스, 인터페이스, 트레이트 (Abstraction)

정현섭·2021년 6월 23일
2

PHP OOP

목록 보기
3/6

Abstract class

  • abstract class는 구현 된 method와 미 구현된 method가 섞여있는 class를 말한다. (구현된 method가 없을 수도 있다.)
  • 미 구현된 method는 abstract method 라고 한다.
  • 미 구현부가 있으니 당연히 abstract class는 객체를 만들 수 없다. (상속을 하여서 구현되지 않은 부분을 다 구현해야 함.)

Example 1 : abstract class 예시 + 활용

abstract class A 
{
    abstract public function foo();
}

class B extends A 
{
    public function foo() {
        echo 'foo~';
    }
}

$b = new B();
bar($b);

function bar(A $a) {
    $a->foo();
}
  • 위 코드의 결과로 "foo~" 가 출력된다.
  • 주목해야 할 점은 function bar() 의 인자 타입으로 class A 가 들어갔다는 것이다.
  • class A의 자식 class의 instance는 foo() method가 구현이 되어있을 것이기 때문에 저런식으로 많이 쓴다.
  • 아래와 같이 활용할 수 있다.
abstract class A 
{
    abstract public function foo();
}

class B extends A 
{
    public function foo() {
        echo "foo~ B\n";
    }
}

class C extends A
{
	public function foo() {
			echo "foo~ C\n";	
	}
}

$b = new B();
bar($b);

$c = new C();
bar($c);

function bar(A $a) {
    $a->foo();
}
  • 위 코드는 결과로 foo~ Bfoo~ C 가 출력된다.

Interface

  • 구현 안된 public method로만 구성 (attribute도 포함하지 않음.)
  • 무조건 public 이어야함. (protected, private 키워드 쓸 수 없음.)
  • 주로 기능적인 부분을 표현할 때 이용. (이 느낌을 이해해야 Interface를 잘 활용할 수 있음. 참고블로그)
  • Interface를 구현하는 class는 implements 라는 키워드를 사용.
  • Interface를 구현하는 class에서는 Interface에 있는 모든 method를 구현해야함.
  • Interface 끼리 확장(상속) 하고 싶을 경우 extends 키워드를 사용.

Example 1 : Interface 기본 사용

interface A
{
    public function foo();
}

class B implements A
{
    public function foo() {
        echo 'foo~~';
    }
}

B::foo();
  • 위 코드의 결과로 foo~~ 가 출력된다.

  • 근데 별개로 주의해야할게 하나 있다. 현재 foo() method를 B::foo() 로 instance(object)를 생성하지 않고 함수를 불렀는데, foo() method에서 인스턴스 데이터에 접근하지 않기 때문에 에러가 안 난 것이다.

  • foo() method를 아래와 같이 구현할 경우에는 에러가 난다.

interface A
{
    public function foo();
}

class B implements A
{
    public $msg = 'hi!';
    public function foo() {
        echo 'foo~~' . $this->msg;
    }
}

B::foo(); // error!!
  • 에러 메시지 : Uncaught Error: Using $this when not in object context
  • 정리하면, 객체를 만들지 않고 public method에 접근 할 경우 해당 public method에서는 $this 키워드를 사용하지 않아야 한다.

Trait

  • 기본적으로 php는 다중상속을 지원하지 않는다.
  • Trait는 기본 class와 같은데 abstract method도 선언할 수 있다.
  • use 키워드를 통하여 상속받는다. (trait를 trait가 상속받을 수도 있고 class가 상속받을 수도 있음. 둘 다 use 키워드 이용.)

Example 1 : trait의 기본 사용.

trait T1 
{
    private $t1 = 't1~';
    protected function foo() {
        echo $this->t1;
    }
}

trait T2
{
    private $t2 = 't2~';
    protected function bar() {
        echo $this->t2;
    }
}

class A 
{
    use T1, T2;
    public function foobar() {
        $this->foo(); 
        $this->bar();
    }
}

$a = new A();
$a->foobar();
  • 위 코드의 결과로 t1~t2~ 가 출력된다.

Example 2 : abstract method + trait 끼리 상속

trait T1 
{
    private $t1 = 't1~';
    protected function foo() {
        echo $this->t1;
    }
}

trait T2
{
    private $t2 = 't2~';
    protected function bar() {
        echo $this->t2;
    }
}

trait T 
{
    use T1, T2;
    abstract public function foobar();
}

class A 
{
    use T;
    public function foobar() {
        $this->foo(); 
        $this->bar();
    }
}

$a = new A();
$a->foobar();
  • 위 코드 또한 결과로 t1~t2~ 가 출력된다.

Example 3 : Trait 충돌

  • 다중 상속의 문제점은 상속받은 여러 class 안에서 중복되는 name을 사용하는 경우가 생긴다는 것이다.
  • trait 에서는 이 충돌을 어떻게 해결하는지 다음 example로 알아보자.
trait T1 
{
    protected function foo() {
        echo __TRAIT__;
    }
}

trait T2
{
    protected function foo() {
        echo __TRAIT__;
    }
}

class A 
{
    use T1, T2 {
        T1::foo insteadof T2;
    }
    public function foobar() {
        $this->foo(); 
    }
}

$a = new A();
$a->foobar();
  • use 키워드의 끝에 중괄호를 붙여서 여러 trait에 대한 컨트롤이 가능하다. 여기서 insteadof 키워드로 충돌을 제어할 수 있다.
  • 위 코드의 결과로 T1 이 출력된다.
  • T1::foo insteadof T2;T2::foo insteadof T1; 로 바꾸면 T2 가 출력된다.

1개의 댓글

comment-user-thumbnail
2023년 4월 20일

잘 보고 있습니다. 감사합니다.

답글 달기