2016-06-07 5 views
2

Почему класс-подсказка класса должна быть на 100% одинаковой с интерфейсом?
Я имею в виду, почему он не может принять реализацию класса в качестве подсказки типа?Тип намека на интерфейс

Для того, чтобы понять,

<?php 
interface MyInterface 
{ 
    public function doMethod(SomeInterface $a) 
    { 
    } 
} 

class MyClass implements MyInterface 
{ 
    public function doMethod(ClassThatImplementsSomeInterface $a) 
    { 
    } 
} 

Ошибка: Fatal error: Declaration of MyClass::doMethod() must be compatible with that of MyInterface::doMethod()

Поскольку тип класса намекая реализует SomeInterface, я ожидаю, что это не нарушает контракт.
Зачем мне это нужно? Из-за преимуществ гибкости интерфейса.
То же самое касается абстрактного класса.
Если я переписал код, чтобы метод «делать» не имел подсказки типа, я знаю, что он «исправит» его.
Но, как-то я думаю, я должен определить контракт, что тип-намек $a должен реализовать SomeInterface.
И, почему бы мне просто не использовать тот же тип-намек, который является SomeInterface?
Это потому, что есть методы, которых нет в SomeInterface, которые мне нужны.
Итак, в чем смысл этого ограничения?

воспроизводимое codepad: http://codepad.org/2PLd8AmV

+1

Я добавил некоторую информацию, хороший вопрос! – DanFromGermany

+0

В вашем интерфейсе говорится: «Я воспользуюсь одним из этих благодарностей», и вы сказали: «Да, я использую один из них вместо этого» ... Я не вижу, где вопрос – Dale

ответ

2

Ответ просто, что вы нужно, чтобы иметь возможность полагаться на объект, что интерфейс требует.

Реальный пример:

интерфейс требует APPLE.

Теперь вы пытаетесь реализовать класс, который говорит I require a GREEN APPLE (это все еще яблоко!).

Кто-то теперь пытается реализовать ваш ИНТЕРФЕЙС и поместить его в класс для зеленого яблока. Он пытается поставить RED APPLE, который совместим с APPLE, но не с GREEN APPLE.

=> взрыва, контракт сломан!

Coding пример:

interface MyInterface 
{ 
    public function doMethod(SomeInterface $a); 
} 

class MyClass implements MyInterface 
{ 
    public function doMethod(ClassThatImplementsSomeInterface $a) { } 
} 

class DifferentClass implements SomeInterface { } 

$ding = new MyClass(); 
$ding->doMethod(new DifferentClass); 

Это не будет работать, потому что DifferentClass не ClassThatImplementsSomeInterface!

+0

Собственно, в моем случае речь идет о прослушивании событий. В этом проблема: [http://codepad.org/ZKYUYcid](http://codepad.org/ZKYUYcid) ... Пожалуйста, посмотрите. Я смущен, следует ли мне удалить тип-намек в метод 'handle' в классе Listener или должен ли я переписать подсказку типа в классе' ChangeUserName' из 'UserHasLoggedIn' в' Event'. –

+0

Или, должен ли я закодировать новый интерфейс под названием «ChangeUserNameInterface», который расширяет интерфейс «Listener» и позволяет реализовать его класс «ChangeUserName»? Но, я думаю, это слишком дорого, потому что так много прослушивателей событий, что означает, что будет так много интерфейса прослушивателя событий. Теперь я запутался. –

+0

Вы можете комбинировать интерфейсы и другие классы с использованием абстрактных классов, но я думаю, что интерфейсы лучше всего (поиск «свободного кулинарного php», чтобы увидеть объяснение!). – DanFromGermany

1

Тип намекая другой класс меняет договор. В вашем интерфейсе говорится: «все, что меня использует, должно использовать определенный тип в этом методе». В вашей реальной реализации говорится, что метод действительно должен использовать другой тип.

До тех пор, пока у меня есть объект, реализующий «MyInterface», я знаю, что существует метод do. Я также знаю, что этот метод принимает один аргумент, который должен быть SomeInterface. Из-за интерфейса мне действительно не нужно знать и не заботиться о каких-либо специфических особенностях объекта (какая конкретная реализация этого интерфейса).

Однако, если объект, который у меня есть, имеет тип MyObject, что это не так! Мне действительно нужен объект ClassThatImplementsSomeInterface! Если мой объект не относится к этому типу, то моя программа сработает. Возможно, случайным образом, если у меня есть фабричные методы создания объектов. Я больше не могу доверять тому, что объекты MyInterface имеют последовательную реализацию и должны проверить, чтобы аргумент, который я хочу передать, правильный.

Проблема, с которой вы сталкиваетесь, - это хороший запах, который не соответствует вашим классам и интерфейсам. MyObject не может быть экземпляром SomeInterface. Или SomeInterface нужно фактически взять ClassThatImplementsSomeInterface вместо этого.

Добавление

Основываясь на вашем примере кода удалить аргумент из функции ручки. Имейте аргументы __construct для конкретных типов объектов Listener, которые принимают конкретную реализацию Event.

interface Event 
{ 
    public function getName(); 
} 

interface Listener 
{ 
    public function handle(); 
} 

class UserHasLoggedIn implements Event 
{ 
    public function __construct($name, $id) 
    { 
     $this->name = $name; 
     $this->id = $id; 
    } 

    public function getName() 
    { 
     return 'UserHasLoggedIn'; 
    } 
} 


class ChangeUserName implements Listener 
{ 
    private $event; 

    public function __construct(UserHasLoggedIn $event) 
    { 
     $this->event = $event; 
    } 

    public function handle() 
    { 

    } 
} 
+0

Это конкретная реализация это: [http://codepad.org/ZKYUYcid](http://codepad.org/ZKYUYcid) .. Пожалуйста, взгляните на это. Каково ваше предложение решить мою проблему? –

+1

Я добавил к моему ответу с предложением. Хотя, не зная больше о том, что вы пытаетесь сделать, есть ограниченный совет, который я могу предоставить. – Schleis

+0

Тем не менее, интерфейс не определяет никаких контрактов для события. Что, если параметр __construct не является событием или событием? Я хотел бы установить контракт –

Смежные вопросы