Реализация классом двух интерфейсов с пересекающимся набором методов

Domovoj

Guest
Реализация классом двух интерфейсов с пересекающимся набором методов

Столкнулся со следующей проблеммой в PHP 5.0.2:

PHP:
interface A {
// делаем A
public function a();
}

interface B {
// делаем A, нужно для того-то
public function a();
// делаем B
public function b();
}

class C implements A, B {
public function a() {};
public function b() {};
}
Это не работает, пишет, что нельзя наследовать B::a(), так как таккая функция уже была наследованна от A
?!

С каких пор интерфейсы наследуются? Это баг PHP или я чего-то не понимаю?

Я думал, что интерфейсы лишь определяют имена методов, которые класс, реализующий интерфейсы, должен определить. Почему класс не может реализовывать два разных интерфейса с пересекащимися методами?

Изначально пытался сделать следующее:
PHP:
interface A {
// делаем A
public function a();
}

interface B extends A {
// делаем A, нужно для того-то
public function a();
// делаем B
public function b();
}

class C implements B {
public function a() {};
public function b() {};
}
Это не работало (таким образом, наследуя интерфейс, нельзя "переопределить" уже указанный метод).

Попробовал способ, описанный в начале. Не работало.

Попробовал даже так:
PHP:
interface A {
// делаем A
public function a();
}

abstract class B implements A {
// делаем A, нужно для того-то
abstract public function a();
// делаем B
abstract public function b();
}

class C extends B {
public function a() {};
public function b() {};
}
Опять таже ошибка про наследование абстрактной функции, уже унаследованной от интерфейса!


Вопрос:
Есть ли объективные причины для описанного выше поведения PHP? Я имею в виду не особенности реализации PHP, а ООП в целом. Насколько я помню, в Jave такого ограничения вроде бы нет.

Пришлось делать так:
PHP:
interface A {
// делаем A
public function a();
}

abstract class B implements A {
// делаем A, нужно для того-то
public function a(){};
// делаем B
abstract public function b();
}

class C extends B {
public function a() {};
public function b() {};
}
 

tony2001

TeaM PHPClub
PHP:
interface A { 
public function a(); 
} 

abstract class B implements A { 
public function a(){} // НЕ abstract, т.к. у интерфейсов все методы и так абстрактные
// делаем B 
abstract public function b(); 
} 

class C extends B {
public function a() {}
public function b() {} 
}
?

>Я думал, что интерфейсы лишь определяют имена методов, которые класс,
>реализующий интерфейсы, должен определить.
не имена, а сигнатуры.

PHP:
interface A { 
public function a($var); 
} 
class B implements A { 
public function a() {} // сигнатура не совпадает - кол-во требуемых параметров разное
}
>Насколько я помню, в Jave такого ограничения вроде бы нет.
"насколько я помню" или "нет" ?
 

Domovoj

Guest
>> Я думал, что интерфейсы лишь определяют имена методов, которые класс,
>> реализующий интерфейсы, должен определить.
>
> не имена, а сигнатуры.

Да, согласен. Сигнатуры. Только в указанных выше примерах сигнатуры индентичны. И никакого конфликта между интерфейсами нет.


PHP:
interface A { 
public function a($var); 
} 
class B implements A { 
public function a() {} // сигнатура не совпадает - кол-во требуемых параметров разное
}
Это про мой пример, или просто объяснение? Если про мой, то там нет $var.


>> Насколько я помню, в Jave такого ограничения вроде бы нет.
>
> "насколько я помню" или "нет" ?

"насколько я помню" + "вроде бы"

Вопрос в том, почему так сделано? Это недоработка (особенность, баг) PHP? Или есть какие либо причины не давать реализовывать классом несколько интерфейсов с пересекающимися методами (и одинаковыми сигнатурами :) этих методов)?
 

tony2001

TeaM PHPClub
>Это про мой пример, или просто объяснение?
пояснение.

>Это недоработка (особенность, баг) PHP?
для начала хотелось бы выяснить как с этим обстоит в других языках.
я не спец в теории ООП, но мне кажется вполне логичным, что два одинаковых абстрактных метода наследоваться/имплементироваться одновременно не могут.

ты мой вариант попробовал?
чем он не подходит?
 

Domovoj

Guest
>> Это недоработка (особенность, баг) PHP?
> для начала хотелось бы выяснить как с этим обстоит в других языках.
> я не спец в теории ООП, но мне кажется вполне логичным, что два одинаковых абстрактных метода
> наследоваться/имплементироваться одновременно не могут.

Логично - это что нельзя наследовать несколько классов, если их методы пересекаются (иначе неясно, какой из методов наследовать).

На счёт абстрактных классов - более-менее логично. Класс представляет собой некий объект, пусть даже абстрактный. И его метод может по смыслу не совпасть с методом другого объекта.

А вот с интерфейсами - не понятно. Интерфейс просто определяет, как с экземплярами можно работать. Грубо говоря какие методы и с какими аргументами можно вызывать. И если есть два интерфейса, которые говорят - "это можно будет отобразить методом display()", то почему класс Shape не может реализовывать несколько интерфейсов, которые говорят, что их можно отобразить методом "display()"?

Хотя, наверно изначально разработчики исходили из того, что если есть два разных интерфейса с одинаковыми методами, то назначение этих методов могут отличаться в этих интерфейсах.

Возможно, дополнительный вклад внесло то, что retrun может вернуть любой тип. И, получается, что формально сигнатуры :) то могут и различаться у этих интерфейсов.


> ты мой вариант попробовал?

Да, я в конце указал его в моём первом сообщении.


> чем он не подходит?

Не красиво как-то когда класс абстрактный, все методы абстрактные, а один - нет и пустой к тому же.
 

svetasmirnova

маленький монстрик
tony2001
>для начала хотелось бы выяснить как с этим обстоит в других языках.
Только что попробовала на Java: первоначальный пример Domovoj работает.
>я не спец в теории ООП, но мне кажется вполне логичным, что два одинаковых абстрактных метода >наследоваться/имплементироваться одновременно не могут.
Я тоже :)
Но, по-моему, Domovoj прав. Не в смысле, что баг, а вообще.
Взять хотя бы пример с display()
Такое ведь тоже не проходит:
PHP:
interface A {
    public function a();
}

interface B implements A {
    public function b();
}

interface C implements A  {
    public function c();
}

class D implements C, B {
public function a() {}
public function b() {}
}
Нелогично: это же интерфейсы, а не классы.

-~{}~ 12.01.05 17:34:

OOps!
Так-то работает. tony2001 прав: надо перепроектировать.
PHP:
interface A {
    public function a();
}

interface B extends A {
    public function b();
}

interface C extends A  {
    public function c();
}

class D implements C, B {
public function a() {}
public function b() {}
public function c() {}
}
 

Blindman

Новичок
> Нелогично: это же интерфейсы, а не классы.

Не знаю, как в других языках, но в PHP интерфейс - это фактически абстрактный класс (нюансы, конечно, есть)
Например:
PHP:
interface A
{
    public function a();
}

interface B extends A
{
    public function b();
}

interface C extends A
{
    public function c();
}


class D implements B,C
{
    public function a() {}
    public function b() {}
    public function c() {}
}
-~{}~ 13.01.05 00:47:

поздно ...
 

Domovoj

Guest
Originally posted by svetasmirnova
OOps!
Так-то работает. tony2001 прав: надо перепроектировать.
PHP:
interface A {
    public function a();
}

interface B extends A {
    public function b();
}

interface C extends A  {
    public function c();
}

class D implements C, B {
public function a() {}
public function b() {}
public function c() {}
}
[/B]
Я думал об этом.

А как быть, если B::a() подразумевает более обширный список действий.

Например:
PHP:
interface A {
    // делаем a
    public function a();
}

interface B extends A {
    // делаем a, а ещё обновляем b
    public function a();
}

class _A implements A {...};
class _B implements B {...};

class C {
    public function c1(A $var) {
       // подразумевается, что будет делаться только a
       $var->a();
   };
    public function c2(B $var) {
       // подразумевается, что будет делаться только a и обновляться b
       $var->a();
   };
}

$a = new _A();
$b = new _B();
$c = new C();

/*
логично, что можно сделать только:
$c->c1($a);
$c->c1($b);
$c->c2($b);
а следующее не пройдёт:
$c->c2($a);
*/
Пример надуманный, и не знаю, случится ли такое в жизни.

Тем не менее, здесь явно видно, что B::a() - это расширенная A::a(). Если же заменить наследованием, то это не очевидно.

-~{}~ 13.01.05 14:13:

Originally posted by Blindman
> Нелогично: это же интерфейсы, а не классы.

Не знаю, как в других языках, но в PHP интерфейс - это фактически абстрактный класс (нюансы, конечно, есть)
Эээ, а какие нюансы есть? Если судить по описанному мной поведению, то, кроме названия и способа записи, больше никакой разницы нет (там даже в ошибке сказано про абстрактный метод, хотя это - метод интерфейса):
- заменяем abstract class на interface
- не пишем abstract перед функциями
- implements вместо extends
А другие отличия есть в реализации интерфейсов в PHP?
 

svetasmirnova

маленький монстрик
>Тем не менее, здесь явно видно, что B::a() - это расширенная A::a(). Если же заменить наследованием
А в чём проблема? Поведение логичное.
>Эээ, а какие нюансы есть?
PHP:
interface C extends A, E  {
    public function c();
}
-~{}~ 13.01.05 14:44:

реплика на пост tony2001 :
я протестировала :)
 

Vasya

Guest
Вот тут http://phpclub.ru/talk/showthread.php?postid=371754#post371754 меня тоже удивило это ограничение на перекрытие одноименных методов в интерфейсах. Причем перекрытие метода объявленного в классе и интерфейсе проходит нормально... В общем, создается впечатление, что это не концептуальное, а техническое ограничение. То есть решили, что "не фиг заморачиваться" :)
 

tony2001

TeaM PHPClub
svetasmirnova
>Только что попробовала на Java: первоначальный пример Domovoj работает.
ок, я набросаю патч (ничего сложного там не должно быть) и попробую его протолкнуть.
посмотрим на реакцию..
 

svetasmirnova

маленький монстрик
В чём-то настоящее поведение интерфейсов логично, так как force лучше продумывать код. Но с другой стороны возможны конфликты при использовании библиотек сторонних разработчиков: поиграв немножко с интерфейсами нашла таки жизненный пример.
PHP:
interface A {
    public function a();
}

interface AA {
    public function a();
}

interface B extends A {
    public function b();
}

interface C extends AA  {
    public function c();
}

interface D extends B, C { //throws error!
    public function d();
}

class D extends Other_Needed implements B, C { //throws error!
    public function a() {}
    public function b() {}
    public function c() {}
}
Естественно, это можно переписать по-другому, но если представить, что interface A и interface B из одного пакета, а interface AA и interface C из другого, всё хуже.
 

ihanic

Guest
Это не работает, пишет, что нельзя наследовать B::a(), так как таккая функция уже была наследованна от A
Если следовать логике OOP:
если инерфейсы содержат пересекающиеся функции, то проблема с сигнатурами можно решить использованием пространств имен.
PHP:
class C implements A, B {
public function A::a() {};
public function B::a() {};
public function b() {};
}
только в php так нельзя.
 
Сверху