PHP Trait이 인터페이스를 구현할 수없는 이유는 무엇입니까?


82

PHP Trait (PHP 5.4)가 인터페이스를 구현할 수없는 이유가 궁금합니다.

user1460043의 답변에서 업데이트 => ... 특정 인터페이스를 구현하는 데 사용하는 클래스가 필요하지 않습니다.

나는 사람들이이 경우에 있다고 생각할 수 있기 때문에, 분명이 될 수 있음을 이해 Class A사용 Trait T를 구현하는이 interface I(가)보다, Class A이행되어야한다 interface Iundirectly을 (이 사실 때문에없는 Class A특성 방법의 이름을 바꿀 수있다).

제 경우에는 특성을 사용하는 클래스가 구현하는 인터페이스에서 메서드를 호출하는 특성이 있습니다.

특성은 사실 인터페이스의 일부 메소드의 구현입니다. 그래서 저는 제 특성을 사용하려는 모든 클래스가 인터페이스를 구현해야하는 코드를 "디자인"하고 싶습니다. 그러면 Trait이 인터페이스에 의해 정의 된 클래스 메서드를 사용하고 클래스에 존재하는지 확인할 수 있습니다.



13
그게 요점이 아닙니다. 특성과 인터페이스의 차이를 압니다.
Leto

1
기술적 인 이유가 있을지 모르지만 왜 원하십니까? 트레이 트를 인스턴스화 할 수 없으므로 인터페이스를 구현한다고해서 타입 힌팅 이점이 제공되지 않습니다. 당신이 말했듯이, 트레이 트를 사용하는 클래스가 인터페이스를 구현하도록 강제하기를 원한다면 (추상)베이스 클래스가 더 적합한 지 궁금 할 것입니다.

당신 말이 맞아요. 어디에서나 추상 클래스를 사용할 수 있지만, 저는 제 코드를 Trait으로 업데이트하고 있고, 간단한 상속 문제를 피할 수 있습니다. 그래서 제가 trait을 사용하고 있습니다. 따라서 그 경우에는 가능하지만 다른 경우에는 그렇지 않습니다.
Leto 2013

2
또는 더 간단한 용어로 말하면 PHP에서 Traits 유형이 아닌 이유는 무엇입니까?
nnevala 2013

답변:


97

정말 짧은 버전은 할 수 없기 때문에 더 간단합니다. 그것은 특성이 작동하는 방식이 아닙니다.

use SomeTrait;PHP로 작성할 때 (효과적으로) 컴파일러에게 Trait의 코드를 복사하여 사용중인 클래스에 붙여 넣도록 지시합니다.

use SomeTrait;는 클래스 내부에 있기 때문에 implements SomeInterface클래스 외부에 있어야하므로 클래스에 추가 할 수 없습니다 .

"PHP에 특성 유형이없는 이유는 무엇입니까?"

인스턴스화 할 수 없기 때문입니다. 트레이 트는 코드에서 참조 할 수있는 객체 나 유형 이 아니라 실제로 언어 구조 (컴파일러에게 트레이 트 코드를이 클래스에 복사하여 붙여 넣도록 지시)에 불과합니다 .

그래서 저는 제 특성을 사용하려는 모든 클래스가 인터페이스를 구현해야하는 코드를 "디자인"하고 싶습니다.

추상 클래스를 사용 use하여 특성에 적용한 다음 클래스를 확장 할 수 있습니다.

interface SomeInterface{
    public function someInterfaceFunction();
}

trait SomeTrait {
    function sayHello(){
        echo "Hello my secret is ".static::$secret;
    }
}

abstract class AbstractClass implements SomeInterface{
    use SomeTrait;
}

class TestClass extends AbstractClass {
    static public  $secret = 12345;

    //function someInterfaceFunction(){
        //Trying to instantiate this class without this function uncommented will throw an error
        //Fatal error: Class TestClass contains 1 abstract method and must therefore be 
        //declared abstract or implement the remaining methods (SomeInterface::doSomething)
    //}
}

$test = new TestClass();

$test->sayHello();

그러나-Trait을 사용하는 모든 클래스가 특정 메서드를 갖도록 강요해야하는 경우, 처음에 추상 클래스 여야했던 트레이 트를 사용하고있을 수 있습니다.

또는 논리가 잘못되었다는 것입니다. 인터페이스를 구현하는 클래스에는 특정 기능이 있어야하며, 특정 기능이있는 경우 인터페이스를 구현하는 것으로 선언해야하는 것이 아닙니다.

편집하다

실제로 Traits 내부에 추상 함수를 정의하여 클래스가 메서드를 구현하도록 할 수 있습니다. 예 :

trait LoggerTrait {

    public function debug($message, array $context = array()) {
        $this->log('debug', $message, $context);
    }

    abstract public function log($level, $message, array $context = array());
}

그러나 이것은 여전히 ​​특성에서 인터페이스를 구현하는 것을 허용하지 않으며, 클래스가 이행해야하는 계약을 정의 할 때 특성보다 인터페이스가 훨씬 낫기 때문에 여전히 나쁜 디자인처럼 냄새가납니다.


2
어떻게 배치하라고 제안하겠습니까? 저는 Human 클래스가 있습니다.이 클래스는 Job을 기반으로하는 하위 클래스로 추상화되었지만 이러한 작업 중 대부분은 공유 코드로 가장 잘 구현되는 기능을 공유합니다 (예 : 비서와 프로그래머 모두 type메서드 가 필요함) ). 이것이 특성없이 어떻게 구현 될 수 있는지 생각할 수 있습니까?
scragar

@scragar 당신은 programmers.stackexchange.com 에서 물어봐야 하지만 짧은 버전은 'WorkingHuman'클래스가되기 위해 여러 'Jobs'와 함께 'Human'을 합성한다는 것입니다.
Danack

1
하나 더. 인터페이스가 일부 인식 계약을 정의하고 해당 계약이 대부분의 구현에 공통적 인 경우. 그러나 이러한 구현에는 고유 한 유형 3이 있습니다. ContainerAwareInterface를 사용한 명령과 같은 것입니다. 그러나 Comand에는 고유 한 사용 영역이 있습니다. 따라서 Container Awareness가 필요할 때마다 반복해야하지만 Trait을 사용하면 특정 Interface에 대한 자체 계약을 정의 할 수 없습니다. 핵심 개발자는 Go-Type 인터페이스 (예 : Structural Typing)를 고려해야할까요?
lazycommit 2015 년

3
실제로 제 동료와 저는 인터페이스를 구현하는 여러 클래스에서 필요하지만 다른 조상에서 온 코드를 공유하고 싶을 때만 특성을 사용하기 때문에 정말 이상합니다. 또한 컴파일러가이 클래스에 의해 구현 된 인터페이스가 아닌 클래스 내부의 코드를 변경할 수있는 이유에 대한 합리적인 설명이 없습니다. ... 그것은 단순히 "누락 된"기능입니다 ... "당신은 할 수 없기 때문에"이것이 가장 잘 설명합니다
Summer-Sky

5
나는 PHP 핵심 개발자가 특성이 본격적인 유형으로 간주되는 Scala를 약간 연구해야한다고 생각합니다. PHP가 점차적으로 타이핑 시스템을 개선하고 싶지만 기존의 잘 작동하는 구현을 고려하지 않는 것이
안타깝습니다

28

있다 RFC는 : 인터페이스 특색가 언어에 추가 할 다음과 같은 제안 :

trait SearchItem implements SearchItemInterface
{
    ...
}

인터페이스에 필요한 메서드는 트레이 트에 의해 구현되거나 추상으로 선언 될 수 있으며,이 경우 트레이 트를 사용하는 클래스가이를 구현할 것으로 예상됩니다.

이 기능은 현재 해당 언어에서 지원되지 않지만 검토 중입니다 (RFC의 현재 상태 : 논의 중 ).


확인되면 사람들은 일반 클래스의 더 많은 기능이 특성으로 구현되기를 원할 것이라고 생각합니다. 그들 사이에 차이가 없을 때까지 우리는 우려와 책임을 제대로 나누지 않는 일종의 프랑켄슈타인 특성을 갖게 될 것입니다. 베스트 답변이 강조 하듯이 특성은 과거 복사 편의성으로보아야합니다. 수업의 경계를 너무 많이 넘지 않아야합니다. 우리는 클래스가 인터페이스를 구현하기를 원합니다. 구현이 직접 코드에서 나왔 든 트레이 트를 사용하든간에 요. 특성에 인터페이스가 혼란과 오해의 소지가 될 수 구현할 수
Kamafeather

트레이 트의 훌륭한 응용 프로그램 중 하나는 붙여 넣기가 쉬운 인터페이스의 기본 구현을 제공하는 것입니다. 특성이 인터페이스를 충족하는지 확인하려면 컴파일러의 도움을받는 것이 좋을 것입니다
The Mighty Chris

이 제안 특성을 사용하는 클래스가 자동으로 특성 인터페이스를 구현하도록 만들지 않습니다 (특성 메서드의 이름을 바꾸거나 바꿀 수 있으므로 작동하지 않습니다). 이 통과하는 것은 어떻게 든 물을 보유하지 않아야 더 공격적으로 미래 RFC를 통과 할 것이라는 미끄러운 경사 인수
마이티 크리스

10

[...] 내 특성을 사용하려는 모든 클래스가 인터페이스를 구현해야하는 코드에서 "디자인"합니다. 그러면 Trait이 인터페이스에 의해 정의 된 클래스 메서드를 사용하고 클래스에 존재하는지 확인할 수 있습니다.

이것은 매우 합리적으로 들리며 귀하의 디자인에 문제가 있다고 말하지 않습니다. 이 아이디어를 염두에두고 특성이 제안되었습니다. 여기에서 두 번째 요점을 참조하십시오.

  • 트레이 트는 동작을 구현하는 일련의 메서드를 제공 합니다.
  • 트레이 트 에는 제공된 동작에 대한 매개 변수 역할을하는 메소드 세트가 필요 합니다.
  • [...]

Schärli et al, Traits : Composable Units of Behaviour, ECOOP'2003, LNCS 2743, pp. 248–274, Springer Verlag, 2003, 페이지 2

따라서 특성이 인터페이스를 "구현"하는 것이 아니라 필요 로한다고 말하는 것이 더 적절할 것입니다.

PHP에서이 "특성 (소비자 클래스가 인터페이스를 구현해야 함)을 필요로한다"는 것이 불가능한 이유는 모르겠지만 현재는 누락 된 것 같습니다.

@Danack이 그의 답변 에서 언급했듯이, 트레이 트에서 추상 함수를 사용 하여 트레이 트 를 사용하는 클래스에서 "요구"할 수 있습니다. 불행히도 개인 기능으로 는 이것을 할 수 없습니다 .


1

@Danack의 응답에 동의하지만 조금 보완하겠습니다.

정말 짧은 버전은 할 수 없기 때문에 더 간단합니다. 그것은 특성이 작동하는 방식이 아닙니다.

나는 당신이 요청한 것이 필요하고 언어 실패보다 디자인 문제로 더 명백한 경우를 거의 생각할 수 없습니다. 다음과 같은 인터페이스가 있다고 상상해보십시오.

interface Weaponize
{
    public function hasAmmunition();
    public function pullTrigger();
    public function fire();
    public function recharge();
}

인터페이스에 정의 된 함수 중 하나를 구현 하는 특성 이 생성 되었지만 프로세스에서 인터페이스에 의해 정의 된 다른 함수도 사용 하는 특성 이 생성되었습니다. 해당 기능 을 사용하는 클래스가 인터페이스를 구현하지 않으면 모든 기능이 방아쇠

trait Triggerable
{
    public function pullTrigger()
    {
        if ($this->hasAmmunition()) {
            $this->fire();
        }
    }
}

class Warrior
{
    use Triggerable;
}

쉬운 해결책은 단순히 특성 을 사용하는 클래스가 이러한 기능을 구현하도록하는 것입니다.

trait Triggerable
{
    public abstract function hasAmmunition();
    public abstract function fire();

    public function pullTrigger()
    {
        if ($this->hasAmmunition()) {
            $this->fire();
        }
    }
}

따라서 특성인터페이스 에 완전히 의존하는 것이 아니라 해당 기능 중 하나를 구현하기위한 제안입니다. 특성을 사용할 때 클래스는 추상 메서드의 구현을 요구하기 때문입니다.

최종 디자인

interface Weaponize
{
    public function hasAmmunition();
    public function pullTrigger();
    public function fire();
    public function recharge();
}

trait Triggerable
{
    public abstract function hasAmmunition();
    public abstract function fire();

    public function pullTrigger()
    {
        if ($this->hasAmmunition()) {
            $this->fire();
        }
    }
}


class Warrior implements Weaponize
{
    use Triggerable;

    public function hasAmmunition()
    {
        // TODO: Implement hasAmmunition() method.
    }

    public function fire()
    {
        // TODO: Implement fire() method.
    }

    public function recharge()
    {
        // TODO: Implement recharge() method.
    }
}

제 영어 실례합니다

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.