PHP에서 불법 : OOP 설계 이유가 있습니까?


16

아래 인터페이스 상속은 PHP에서 불법이지만 실제 생활에서는 상당히 유용하다고 생각합니다. PHP가 저를 보호하는 아래의 디자인에 실제로 반 패턴이나 문서화 된 문제가 있습니까?

<?php

/**
 * Marker interface
 */
interface IConfig {}

/**
 * An api sdk tool
 */
interface IApi
{
    public __construct(IConfig $cfg);
}

/**
 * Api configuration specific to http
 */
interface IHttpConfig extends IConfig
{
    public getSomeNiceHttpSpecificFeature();
}

/**
 * Illegal, but would be really nice to have.
 * Is this not allowed by design?
 */
interface IHttpApi extends IApi
{
    /**
     * This constructor must have -exactly- the same
     * signature as IApi, even though its first argument
     * is a subtype of the parent interface's required
     * constructor parameter.
     */
    public __construct(IHttpConfig $cfg);

}

답변:


22

해당 메소드가 1 초 동안 무시 __construct하고 호출 해 봅시다 frobnicate. 이제 객체가 있다고 가정 api구현 IHttpApi하고, 객체가 config구현 IHttpConfig. 분명히이 코드는 인터페이스에 적합합니다.

$api->frobnicate($config)

그러나의 업 캐스팅 우리를 가정하자 apiIApi에 전달 예를 들어, function frobnicateTwice(IApi $api). 이제 해당 함수 frobnicate에서을 IApi호출하고을 처리하기 때문에 구현 하지만 구현 하지 않는 $api->frobnicate(new SpecificConfig(...))위치 와 같은 호출을 수행 할 수 있습니다 . 어느 누구도 타입이 좋지 않은 것을 수행하지 않았지만 예상되는 위치를 얻었 습니다 .SpecificConfigIConfigIHttpConfigIHttpApi::frobnicateSpecificConfigIHttpConfig

이건 좋지 않아 우리는 업 캐스팅을 금지하고 싶지 않고, 서브 타이핑을 원하며, 인터페이스를 구현하는 여러 클래스를 분명히 원합니다. 따라서 유일하게 합리적인 옵션은 매개 변수에 대해 더 구체적인 유형이 필요한 하위 유형 방법을 금지하는 것 입니다. ( 보다 일반적인 유형 을 반환 하려는 경우에도 비슷한 문제가 발생합니다 .)

공식적으로, 당신은 다형성, 분산을 둘러싼 고전적인 함정에 들어갔습니다 . 유형의 모든 항목을 T하위 유형으로 바꿀 수있는 것은 아닙니다 U. 반대로, 모든 유형의 발생이 아님T 수퍼 타입 으로 대체 될 수있는S . 신중한 고려 (또는 더 나은 유형의 이론 적용)가 필요합니다.

되돌아 가기 __construct: AFAIK를 사용하면 인터페이스를 정확하게 인스턴스화 할 수 없으며 구체적인 구현 자만이 무의미한 제한처럼 보일 수 있습니다 (인터페이스를 통해 호출되지는 않습니다). 그러나이 경우 왜 __construct인터페이스에 포함시켜야 합니까? 어쨌든, 특별한 경우에는 거의 쓸모가 없습니다 __construct.


19

예, 이것은 Liskov 대체 원칙 (LSP) 에서 직접 따릅니다 . 메서드를 재정의하면 반환 형식이 더 구체적이 될 수 있지만 인수 형식은 동일하게 유지되거나 더 일반적이 될 수 있습니다.

이 이외의 방법에서는 더 분명 __construct합니다. 치다:

class Vehicle {}
class Car extends Vehicle {}
class Motorcycle extends Vehicle {}

class Driver {
    public drive(Vehicle $v) { ... }
}
class CarDriver extends Driver {
    public drive(Car $c) { ... }
}

A는 CarDriverDriver소위, CarDriver인스턴스가 수행 할 수 있어야합니다 아무것도 • 그래도 Driver깡통을. Motorcycles단지 포함 하기 때문에 driving을 포함 합니다 Vehicle. 그러나의 인수 유형은 driveA가 있다고 CarDriver만 구동 할 수 있습니다 Car: 모순을 -들 CarDriver 의 적절한 서브 클래스 Driver.

반대가 더 의미가 있습니다.

class CarDriver {
    public drive(Car $c) { ... }
}
class MultiTalentedDriver extends CarDriver {
    public drive(Vehicle $v) { ... }
}

A는 CarDriver만 구동 할 수 Car들. A MultiTalentedDriverCars 를 구동 할 수 있습니다 .Car 정당한이다 Vehicle. 따라서 MultiTalentedDriver의 적절한 하위 클래스입니다 CarDriver.

귀하의 예에서는 IApi로 구성 할 수 있습니다 IConfig. 경우 IHttpApi의 하위 유형입니다 IApi, 우리는 구성 할 수 있어야합니다 IHttpApi어떤 사용하여 IConfig인스턴스를 -하지만 그것은 단지 받아 들인다 IHttpConfig. 이것은 모순입니다.


모든 운전자가 자동차와 오토바이를 모두 운전할 수있는 것은 아닙니다.
sakisk

3
@faif :이 특정 추상화에서, 그들은 할 수있을뿐만 아니라 반드시 그래야합니다. 보시다시피, can은 Driverany를 구동 할 수 있고 Vehicle, 모두 CarMotorcycleextends Vehicle이므로 모든 Driver것이 모두를 처리 할 수 ​​있어야합니다.
Alex
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.