[php] 특성과 인터페이스

나는 최근 PHP에 대해 연구하려고 노력해 왔고, 나는 특성에 매달리는 것을 발견했다. 수평 코드 재사용의 개념을 이해하고 반드시 추상 클래스에서 상속 받기를 원하지 않습니다. 내가 이해하지 못하는 것은 특성과 인터페이스를 사용하는 것의 중요한 차이점은 무엇입니까?

적절한 블로그 게시물이나 기사 중 하나를 사용할 때를 설명하는 기사를 검색하려고 시도했지만 지금까지 찾은 예제는 동일한 것으로 보입니다.



답변

인터페이스는 구현 클래스 구현 해야하는 일련의 메소드를 정의 합니다 .

특성이 정의되면 use메소드의 구현도 함께 나타납니다 Interface.

가장 큰 차이점입니다.

PHP RFC수평 재사용에서 :

특성은 PHP와 같은 단일 상속 언어에서 코드를 재사용하는 메커니즘입니다. 특성은 개발자가 서로 다른 클래스 계층 구조에있는 여러 독립 클래스에서 메소드 세트를 자유롭게 재사용 할 수있게함으로써 단일 상속의 일부 한계를 줄이기위한 것입니다.


답변

공공 서비스 발표 :

나는 특성이 거의 항상 코드 냄새이며 구성을 위해 피해야한다고 기록에 대해 진술하고 싶습니다. 단일 상속이 반 패턴이되고 여러 상속이이 문제를 복잡하게한다는 점에서 단일 상속이 자주 남용된다고 생각합니다. 대부분의 경우 상속보다 구성을 선호하여 훨씬 더 나은 서비스를 제공 할 수 있습니다 (단일 또는 다중). 특성과 인터페이스와의 관계에 여전히 관심이 있다면 …


이 말로 시작하자 :

객체 지향 프로그래밍 (OOP)은 파악하기 어려운 패러다임이 될 수 있습니다. 클래스를 사용한다고해서 코드가 객체 지향 (OO)이라는 의미는 아닙니다.

OO 코드를 작성하려면 OOP가 실제로 객체의 기능에 관한 것임을 이해해야합니다. 실제로 하는 것 대신 할 수있는 것의 관점에서 클래스에 대해 생각 해야 합니다. 이것은 약간의 코드를 “무언가로 만드는”것에 중점을 둔 전통적인 절차 적 프로그래밍과는 완전히 대조적이다.

OOP 코드가 계획 및 설계에 관한 것이라면 인터페이스는 청사진이며 객체는 완전히 구성된 집입니다. 한편, 특성은 단순히 청사진 (인터페이스)으로 구성된 집을 짓는 데 도움이되는 방법입니다.

인터페이스

그렇다면 왜 인터페이스를 사용해야합니까? 간단히 말해서 인터페이스는 코드의 취성을 떨어 뜨립니다. 이 진술이 의심 스러우면 인터페이스에 대해 작성되지 않은 레거시 코드를 유지하도록 강요받은 사람에게 문의하십시오.

인터페이스는 프로그래머와 코드 간의 계약입니다. 인터페이스는 “내 규칙에 따라 플레이하는 한 원하는대로 구현할 수 있으며 다른 코드는 중단하지 않겠다고 약속합니다.”라고 말합니다.

예를 들어, 실제 시나리오 (자동차 또는 위젯 없음)를 고려하십시오.

웹 애플리케이션이 서버로드를 줄이기 위해 캐싱 시스템을 구현하려고합니다.

APC를 사용하여 요청 응답을 캐시하는 클래스를 작성하여 시작하십시오.

class ApcCacher
{
  public function fetch($key) {
    return apc_fetch($key);
  }
  public function store($key, $data) {
    return apc_store($key, $data);
  }
  public function delete($key) {
    return apc_delete($key);
  }
}

그런 다음 HTTP 응답 오브젝트에서 실제 응답을 생성하기 위해 모든 작업을 수행하기 전에 캐시 적중을 점검하십시오.

class Controller
{
  protected $req;
  protected $resp;
  protected $cacher;

  public function __construct(Request $req, Response $resp, ApcCacher $cacher=NULL) {
    $this->req    = $req;
    $this->resp   = $resp;
    $this->cacher = $cacher;

    $this->buildResponse();
  }

  public function buildResponse() {
    if (NULL !== $this->cacher && $response = $this->cacher->fetch($this->req->uri()) {
      $this->resp = $response;
    } else {
      // Build the response manually
    }
  }

  public function getResponse() {
    return $this->resp;
  }
}

이 방법은 효과적입니다. 그러나 몇 주 후에 APC 대신 파일 기반 캐시 시스템을 사용하기로 결정했습니다. 이제 ApcCacher클래스의 기능을 표현하는 인터페이스가 아닌 클래스 의 기능을 사용하도록 컨트롤러를 프로그래밍 했으므로 컨트롤러 코드를 변경해야합니다 ApcCacher. 위 대신에 당신이 Controller클래스 CacherInterface대신에 콘크리트 대신에 의존하게 만들었다 고 가정 해 봅시다 ApcCacher.

// Your controller's constructor using the interface as a dependency
public function __construct(Request $req, Response $resp, CacherInterface $cacher=NULL)

이와 함께 인터페이스를 다음과 같이 정의하십시오.

interface CacherInterface
{
  public function fetch($key);
  public function store($key, $data);
  public function delete($key);
}

그리고 당신은 당신 ApcCacher과 당신의 새로운 FileCacher클래스를 구현 하고 인터페이스에 필요한 기능을 사용하도록 클래스를 CacherInterface프로그래밍합니다 Controller.

이 예제는 인터페이스 프로그래밍이 변경 사항으로 인해 다른 코드가 손상 될 염려없이 클래스의 내부 구현을 변경하는 방법을 보여줍니다.

특성

반면에 특성은 단순히 코드를 재사용하는 방법입니다. 인터페이스는 특성에 대한 상호 배타적 인 대안으로 생각해서는 안됩니다. 실제로 인터페이스에 필요한 기능을 충족시키는 특성을 만드는 것이 이상적인 사용 사례 입니다.

여러 클래스가 동일한 기능을 공유 할 때 (같은 인터페이스에 의해 지시 될 수 있음) 특성 만 사용해야합니다. 특성을 사용하여 단일 클래스에 기능을 제공하는 것은 의미가 없습니다. 이는 클래스의 기능을 난독 화하고 더 나은 디자인은 특성의 기능을 관련 클래스로 옮길 것입니다.

다음 특성 구현을 고려하십시오.

interface Person
{
    public function greet();
    public function eat($food);
}

trait EatingTrait
{
    public function eat($food)
    {
        $this->putInMouth($food);
    }

    private function putInMouth($food)
    {
        // Digest delicious food
    }
}

class NicePerson implements Person
{
    use EatingTrait;

    public function greet()
    {
        echo 'Good day, good sir!';
    }
}

class MeanPerson implements Person
{
    use EatingTrait;

    public function greet()
    {
        echo 'Your mother was a hamster!';
    }
}

더 구체적인 예 : 인터페이스 토론에서 귀하 FileCacher와 귀하 ApcCacher의 인터페이스 항목이 동일한 방법을 사용하여 캐시 항목이 오래되어 삭제되어야하는지 여부를 결정 한다고 상상해보십시오 (실제로는 그렇지 않지만 실제로는 마찬가지입니다). 특성을 작성하고 두 클래스 모두 공통 인터페이스 요구 사항에이를 사용할 수 있습니다.

주의해야 할 마지막 말 : 특성으로 배 밖으로 나가지 않도록주의하십시오. 독특한 클래스 구현으로 충분할 때 특성이 열악한 디자인의 버팀목으로 사용되는 경우가 종종 있습니다. 최상의 코드 디자인을 위해서는 특성을 인터페이스 요구 사항을 충족하도록 제한해야합니다.


답변

A trait는 본질적으로 PHP의의 구현이며 mixin,의 추가를 통해 모든 클래스에 추가 할 수있는 확장 메소드 세트입니다 trait. 그런 다음 메소드는 상속을 사용하지 않고 해당 클래스 구현의 일부가 됩니다.

보내는 사람 PHP 매뉴얼 (강조 광산) :

특성은 PHP와 같은 단일 상속 언어에서 코드를 재사용 하는 메커니즘입니다 . … 그것은 전통적인 상속에 추가 된 것이며 행동의 수평 적 구성을 가능하게합니다. 즉, 상속을 요구하지 않고 클래스 멤버를 적용하는 것입니다.

예를 들면 :

trait myTrait {
    function foo() { return "Foo!"; }
    function bar() { return "Bar!"; }
}

위의 특성을 정의하면 이제 다음을 수행 할 수 있습니다.

class MyClass extends SomeBaseClass {
    use myTrait; // Inclusion of the trait myTrait
}

내가 클래스의 인스턴스를 만들 때이 시점에서 MyClass, 그것은라는 두 가지 방법이 있습니다 foo()bar()에서 온 – myTrait. 그리고- trait정의 된 메소드에는 이미 Interface정의 된 메소드가 할 수없는 메소드 본문이 있습니다.

또한 PHP는 다른 많은 언어와 마찬가지로 단일 상속 모델을 사용하므로 클래스는 여러 인터페이스가 아닌 여러 인터페이스에서 파생 될 수 있습니다. 그러나 PHP 클래스 에는 여러 기본 클래스가 포함 된 것처럼 프로그래머가 재사용 가능한 부분을 포함 할 있는 여러 trait포함이 포함될 있습니다 .

몇 가지 참고할 사항 :

                      -----------------------------------------------
                      |   Interface   |  Base Class   |    Trait    |
                      ===============================================
> 1 per class         |      Yes      |       No      |     Yes     |
---------------------------------------------------------------------
Define Method Body    |      No       |       Yes     |     Yes     |
---------------------------------------------------------------------
Polymorphism          |      Yes      |       Yes     |     No      |
---------------------------------------------------------------------

다형성 :

여기서, 이전의 실시 예에서, MyClass 연장 SomeBaseClass , MyClass 인스턴스 SomeBaseClass. 즉,와 같은 배열 SomeBaseClass[] bases은의 인스턴스를 포함 할 수 있습니다 MyClass. 마찬가지로 MyClass확장 된 IBaseInterface경우의 배열 IBaseInterface[] bases에는의 인스턴스가 포함될 수 있습니다 MyClass. a와 함께 사용할 수있는 다형성 구문은 없습니다. trait왜냐하면 a trait는 본질적으로 프로그래머의 편의를 위해이를 사용하는 각 클래스에 복사되는 코드 이기 때문 입니다.

상위:

매뉴얼에 설명 된대로 :

기본 클래스에서 상속 된 멤버는 Trait에 의해 삽입 된 멤버로 대체됩니다. 우선 순위는 현재 클래스의 멤버가 Trait 메소드를 대체하며,이 메소드는 상속 된 메소드를 대체합니다.

따라서 다음 시나리오를 고려하십시오.

class BaseClass {
    function SomeMethod() { /* Do stuff here */ }
}

interface IBase {
    function SomeMethod();
}

trait myTrait {
    function SomeMethod() { /* Do different stuff here */ }
}

class MyClass extends BaseClass implements IBase {
    use myTrait;

    function SomeMethod() { /* Do a third thing */ }
}

위의 MyClass 인스턴스를 만들 때 다음이 발생합니다.

  1. Interface IBase호출하려면 매개 변수없는 함수가 필요합니다 SomeMethod().
  2. 기본 클래스 BaseClass는이 메소드의 구현을 제공하여 필요를 충족시킵니다.
  3. trait myTrait라는 매개 변수가 기능을 제공 SomeMethod()뿐만 아니라, 우선합니다 오버 BaseClass-version을
  4. class MyClass자체 버전 제공 SomeMethod()우선합니다 오버 trait-version을.

결론

  1. Interface잠시 방법 본체의 기본 구현을 제공 할 수 없습니다 trait캔.
  2. InterfaceA는 다형성 , 상속 구조 – A는 동안 trait아닙니다.
  3. 여러 Interface들 같은 클래스에서 사용하고있는 여러 그렇게 할 수 trait의.

답변

traits여러 클래스의 메소드로 사용할 수있는 메소드가 포함 된 클래스를 작성 하는 것이 유용 하다고 생각 합니다.

예를 들면 다음과 같습니다.

trait ToolKit
{
    public $errors = array();

    public function error($msg)
    {
        $this->errors[] = $msg;
        return false;
    }
}

이 특성 을 사용 하는 모든 클래스에서이 “오류”메소드를 사용하고 사용할 수 있습니다 .

class Something
{
    use Toolkit;

    public function do_something($zipcode)
    {
        if (preg_match('/^[0-9]{5}$/', $zipcode) !== 1)
            return $this->error('Invalid zipcode.');

        // do something here
    }
}

사용 interfaces하면 메소드 서명 만 선언 할 수 있지만 함수 코드는 선언 할 수 없습니다. 또한 인터페이스를 사용하려면을 사용하여 계층 구조를 따라야합니다 implements. 이것은 특성의 경우가 아닙니다.

완전히 다릅니다!


답변

위의 초보자는 대답이 어려울 수 있습니다. 이해하는 가장 쉬운 방법입니다.

특성

trait SayWorld {
    public function sayHello() {
        echo 'World!';
    }
}

따라서 sayHello전체 함수를 다시 만들지 않고 다른 클래스에서 함수를 사용하려면 특성을 사용할 수 있습니다.

class MyClass{
  use SayWorld;

}

$o = new MyClass();
$o->sayHello();

멋지다!

특성뿐만 아니라 특성 (function, variables, const ..)에서 무엇이든 사용할 수 있습니다. 또한 여러 특성을 사용할 수 있습니다.use SayWorld,AnotherTraits;

상호 작용

  interface SayWorld {
     public function sayHello();
  }

  class MyClass implements SayWorld {
     public function sayHello() {
        echo 'World!';
     }
}

인터페이스와 특성이 다른 방법입니다. 구현 된 클래스의 인터페이스에서 모든 것을 다시 만들어야합니다. 인터페이스에 구현이 없습니다. interface는 함수와 const 만 가질 수 있고 변수는 가질 수 없습니다.

이게 도움이 되길 바란다!


답변

특성을 설명하기 위해 자주 사용되는 은유는 특성과 구현의 인터페이스입니다.

이것은 대부분의 상황에서 그것에 대해 생각하는 좋은 방법이지만, 둘 사이에는 많은 미묘한 차이가 있습니다.

처음에는 instanceof운영자가 특성에 대해 작업하지 않으므로 (즉, 특성이 실제 개체가 아닙니다) 클래스에 특정 특성이 있는지 확인하거나 관련이없는 두 클래스가 특성을 공유하는지 확인할 수 없습니다 ). 이것이 수평 코드 재사용을위한 구성이라는 의미입니다.

있습니다 당신이 특성을 클래스에서 사용하는 모든 목록을 얻을 수있게된다 이제 PHP 함수가 있지만, 특성 상속 수단은 당신이 안정적으로 재귀 검사를 할 어떤 점에서 클래스가 특정 형질을 가지고 있는지 확인해야합니다 (예를있다 PHP doco 페이지의 코드). 그러나 그렇습니다. instanceof만큼 간단하고 깨끗하지는 않지만 IMHO는 PHP를 더 좋게 만드는 기능입니다.

또한 추상 클래스는 여전히 클래스이므로 다중 상속 관련 코드 재사용 문제를 해결하지 못합니다. 하나의 클래스 (실제 또는 추상) 만 확장 할 수 있지만 여러 인터페이스를 구현할 수 있습니다.

특성과 인터페이스가 의사 다중 상속을 만들기 위해 함께 사용하는 것이 좋습니다. 예 :

class SlidingDoor extends Door implements IKeyed
{
    use KeyedTrait;
    [...] // Generally not a lot else goes here since it's all in the trait  
}

이렇게하면 instanceof를 사용하여 특정 Door 객체가 Keyed인지 아닌지를 확인할 수 있으며, 일관된 메소드 세트 등을 얻을 수 있으며 모든 코드는 KeyedTrait를 사용하는 모든 클래스에서 한 곳에 있습니다.


답변

특성 은 단순히 코드 재사용을 위한 입니다.

인터페이스프로그래머의 재량 에 따라 사용할 수있는 클래스 에서 정의 할 함수 의 서명 만 제공합니다 . 따라서 클래스 그룹에 대한 프로토 타입 을 제공합니다 .

참조
-http://www.php.net/manual/en/language.oop5.traits.php