language-oop5-abstract

클래스 추상화

PHP에는 추상 클래스, 추상 메서드, 추상 프로퍼티가 있어요. 추상으로 정의된 클래스는 인스턴스를 만들 수 없고, 추상 메서드나 추상 프로퍼티를 하나라도 포함하는 클래스는 반드시 추상이어야 해요.

추상 메서드는 시그니처와 그것이 public인지 protected인지만 선언할 뿐, 구현은 정의할 수 없어요. 추상 프로퍼티는 get이나 set 동작에 대한 요구사항을 선언할 수 있고, 두 동작 중 하나에 대해서만 구현을 제공할 수 있어요.

추상 클래스를 상속할 때는 부모 클래스 선언에서 추상으로 표시된 모든 메서드를 자식 클래스가 정의해야 해요. 그 과정에서 평소의 상속 규칙과 시그니처 호환성 규칙을 그대로 따르게 돼요.

PHP 8.4부터 추상 클래스는 public 또는 protected 추상 프로퍼티를 선언할 수 있어요. protected 추상 프로퍼티는 protected 또는 public 범위에서 읽고 쓸 수 있는 프로퍼티로 충족될 수 있어요.

추상 프로퍼티는 일반 프로퍼티로 채울 수도 있고, 요구된 동작에 맞춰 훅(hook)이 정의된 프로퍼티로 채울 수도 있어요.

예제 #1 추상 메서드 예제

<?php

abstract class AbstractClass
{
    // Force extending class to define this method
    abstract protected function getValue();
    abstract protected function prefixValue($prefix);

    // Common method
    public function printOut()
    {
        print $this->getValue() . "\n";
    }
}

class ConcreteClass1 extends AbstractClass
{
    protected function getValue()
    {
        return "ConcreteClass1";
    }

    public function prefixValue($prefix)
    {
        return "{$prefix}ConcreteClass1";
    }
}

class ConcreteClass2 extends AbstractClass
{
    public function getValue()
    {
        return "ConcreteClass2";
    }

    public function prefixValue($prefix)
    {
        return "{$prefix}ConcreteClass2";
    }
}

$class1 = new ConcreteClass1();
$class1->printOut();
echo $class1->prefixValue('FOO_'), "\n";

$class2 = new ConcreteClass2();
$class2->printOut();
echo $class2->prefixValue('FOO_'), "\n";

?>

위 예제는 다음과 같이 출력돼요:

ConcreteClass1
FOO_ConcreteClass1
ConcreteClass2
FOO_ConcreteClass2

예제 #2 추상 메서드 예제

추상 메서드는 필수 인자만 정의하면 되고, 자식 클래스는 부모 시그니처에 없는 선택적 매개변수를 추가로 정의할 수 있어요.

<?php

abstract class AbstractClass
{
    // An abstract method only needs to define the required arguments
    abstract protected function prefixName($name);
}

class ConcreteClass extends AbstractClass
{
    // A child class may define optional parameters which are not present in the parent's signature
    public function prefixName($name, $separator = ".")
    {
        if ($name == "Pacman") {
            $prefix = "Mr";
        } elseif ($name == "Pacwoman") {
            $prefix = "Mrs";
        } else {
            $prefix = "";
        }

        return "{$prefix}{$separator} {$name}";
    }
}

$class = new ConcreteClass();
echo $class->prefixName("Pacman"), "\n";
echo $class->prefixName("Pacwoman"), "\n";

?>

위 예제는 다음과 같이 출력돼요:

Mr. Pacman
Mrs. Pacwoman

예제 #3 추상 프로퍼티 예제

<?php

abstract class A
{
    // Extending classes must have a publicly-gettable property
    abstract public string $readable {
        get;
    }

    // Extending classes must have a protected- or public-writeable property
    abstract protected string $writeable {
        set;
    }

    // Extending classes must have a protected or public symmetric property
    abstract protected string $both {
        get;
        set;
    }
}

class C extends A
{
    // This satisfies the requirement and also makes it settable, which is valid
    public string $readable;

    // This would NOT satisfy the requirement, as it is not publicly readable
    protected string $readable;

    // This satisfies the requirement exactly, so is sufficient.
    // It may only be written to, and only from protected scope
    protected string $writeable {
        set => $value;
    }

    // This expands the visibility from protected to public, which is fine
    public string $both;
}

?>

예제 #4 훅이 있는 추상 프로퍼티 예제

<?php

abstract class A
{
    // This provides a default (but overridable) set implementation,
    // and requires child classes to provide a get implementation
    abstract public string $foo {
        get;

        set {
            $this->foo = $value;
        }
    }
}

?>

추상 클래스의 추상 프로퍼티는 어떤 훅이든 구현을 제공할 수 있지만, get이나 set 중 하나 이상은 선언만 하고 정의하지 않은 채 남겨두어야 해요(바로 위 예제처럼).