Разные интерфейсы, одинаковые методы

Lionishy

Новичок
Предположим есть два интерфейса, которые определяют каждый по методу с одинаковой сигнатурой:
PHP:
interface IResolveDog {
    /**
    * @param Name : a name of a dog
    * @return Dog
    */
    public function resolve(Name $name);
};

interface IResolveCat {
    /**
    * @param Name : a name of a cat
    * @return Cat
    */
    public function resolve(Name $name);
};
И в одном классе требуется дать возможность разрешать и кошек, и собак, и ещё каких-нибудь тварей.
Что делать?
Есть ли в PHP пространство имён для методов, как в C++?
 

AmdY

Пью пиво
Команда форума
котопёс

У тебя признак плохого интерфейса, вынеси resolve в отдельный. В php через интерфейсы можно делать множественное наследование implements IResolve, ICat {}
 

HraKK

Мудак
Команда форума
AmdY,Проблему определил правильно, а вот решение не правильное. Лучше сотри.
 

Lionishy

Новичок
Это разные методы. Один возвращает кошек, другой собак.
Это не полиморфизм. Это вопрос реализации.
 
Последнее редактирование:

Lionishy

Новичок
P.S. Интерфейсы из чужих пакетов -- нельзя поменять код =(

Хотя у меня была похожая идея: вынести всё общее в базовый класс и вбросить туда абстрактный resolve, а затем унаследовать от него два разных класса.

PHP:
abstract class Resolver {
    abstract public function resolve(Name $name);
};

class CatResolver extends Resolver implements IResolveCat {
  public function resolve(Name $name);
};

class DogResolver extends Resolver implements IResolveDog {
    public function resolve(Name $name);
};
Почему это плохо?

i) Сущность Resolver не используется. Используется reinterpret_cast в IResolveDog или IResolveCat. Это запутывает.

ii) Код расползается по разным файлам и становится сложнее поддерживать, так как DogResolver и CatResolver для построения Cat и Dog должны иметь данные от Resolver, то есть они с ним тесно связаны, но расположены в разных алгебрах, что плохо.

P.S. И, конечно, это не решает проблему представления одной сущность в различных аспектах. Например, шкаф можно поставить на весы и тогда resolve вернёт вес, а если в хромотограф -- то цвет.
 
Последнее редактирование:

riff

Новичок
И в одном классе требуется дать возможность разрешать и кошек, и собак, и ещё каких-нибудь тварей.
По-моему, ты придумал себе несуществующую проблему:
PHP:
interface IResolveDog {
    /**
    * @param Name : a name of a dog
    * @return Dog
    */
    public function resolve($name);
};

interface IResolveCat {
    /**
    * @param Name : a name of a cat
    * @return Cat
    */
    public function resolve($name);
};

class Animals implements IResolveDog, IResolveCat {
    public $list = [];
    public function resolve($name)
    {
        return $this->list[$name];
    }
}
class Dog {
    public function getDogName() {
        return 'aaaaaa';
    }
}
class Cat {
    public function getCatName() {
        return 'bbbbbbb';
    }
}

$animals = new Animals;
$animals->list['cat'] = new Cat;
$animals->list['dog'] = new Dog;

function print_dog(IResolveDog $animals) {
    echo $animals->resolve('dog')->getDogName();
}
function print_cat(IResolveCat $animals) {
    echo $animals->resolve('cat')->getCatName();
}
print_cat($animals);
В чём проблема?
 
Последнее редактирование:

Lionishy

Новичок
riff, проблема в том, что Cat и Dog это не полиморфные сущности, разные аспекты одного объекта. Между resolve и get -- пропасть. Скажем так: я должен "сконструировать" кошку по её имени. Я не могу по одним правилам конструировать и кошек, и собак. Предположим банальную вещь: кошки и собаки хранятся на разных серверах баз данных, в зависимости о того, какой конкретно сейчас пользуют интерфейс, я должен выбрать нужное соединение.

Или в примере со шкафом (ясно, что это не компилируется, но в С++ оно было бы решалось именно так):

PHP:
interface IResolveWeight {
    /**
    * @return Weight
    */
    public function resolve();
};

interface IResolveColor {
    /**
    * @return Color
    */
    public function resolve();
};

class Wardrobe implements IResolveWeight, IResolveColor {
    /**
    * @param Weight
    * @param Color
    */
    public function __construct(Weight $weight, Color $color)
    {
        $this->weight = $weight;
        $this->color  = $color;
    }
    /**
    * {@inheritdoc}
    */
    public function /*IReolveWeight::*/resolve()
    {
        return $this->weight;
    }
  
    /**
    * {@inheritdoc}
    */
    public function /*IResolveColor::*/resolve()
    {
        return $this->color;
    }
  
    /**
    * @var Weight
    */
    private $weight;
  
    /**
    * @var Color
    */
    private $color;
};

class Weigher {
    /**
    * @param IResolveWeight
    * @return Weight
    */
    public function doWeight(IResolveWeight $obj)
    {
        return $obj->resolve();
    }
};

/* a lot of code here */

$myWardrobe = new Wardrobe($weight,$color);

/* a lot of code goes here */

$weigher = new Weigher();
echo "My wardrobe is ".$weigher->doWeight($myWardrobe)." lbs heavy! \n";

$chromatograph = new Chromatograph();
echo "My wardrobe is ".$chromatograph->doChromatography($myWardrobe)."! \n";
Складывается ощущение, что в PHP имена методов интерфейсов оказываются "глобальными символами", что напрягает и рвёт ООП. =(
 

Lionishy

Новичок
Вурдалак, шкаф может возвращать вес какой-то "общий". В условных единицах, принятых для класса "Весы". Например в массах Солнца. А весы могут сформатировать вес в удобных единицах: фунтах, граммах, ещё чего-нибудь.

При этом, процедура определения веса для шкафа "дорогая" и она вызывается, только когда вес "требуется". То есть, вес не синхронизирован постоянно со внутренним состоянием шкафа.
 

Lionishy

Новичок
На других форумах мне ещё предложили:

Proxy:
PHP:
class Wardrobe {
    /**
      * @return IResolveWeight
      */
    public function getIResolveWeight()
    {
          return $this->weightResolver;
    }
 
    /**
      * @return IResolveColor
      */
    public function getIResolveColor()
    {
          return $this->colorResolver;
    }
 
    /**
    * @var WardrobeWeightResolver
    */
    private $weightResolver;
 
    /**
    * @var WardrobeWeightResolver
    */
    private $colorResolver;
};
Плохой вариант: сильная связь между классами Wardrobe, WardrobeWeightResolver, WardrobeColorResolver; не ясно, как внедрять зависимости -- ведь все зависят от всех. Кроме того, придётся сделать открытыми детали внутренностей Wardrobe, чтобы ими могли оперировать WardrobeWeightResolver и WardrobeColorResolver, что даёт лишнюю волю клиентам Wardrobe.


Другой вариант C-style: пусть все называют методы с префиксом интерфейсов: IResolveCat_resolve(); IResolveDog_resolver(). Тоже как-то гнило, но, думаю, работает понятнее. Беда в том, что какими-то группами я могу командовать, а какими-то -- нет. Я не могу приказать это всем программистам PHP во всём мире. Можно наткнуться на такие грабли с совершенно неизвестным поставщиком.
 

Lionishy

Новичок
riff, неоднозначности, несомненно, нет, так как методы из разных пространств имён.
Само замечание устарело. В версии 5.3.9 уже можно совместить два интерфейса с одинаковыми названиями методов. Но имя осталось, по-моему, глобальным: одно имя -- один метод.
Документация отстаёт от реальности. Потому я и на форуме, а не в документации.

Пока что выигрывает вариант из декораторов-прокси. Хотя это очень скверно =(
 

accido

Новичок
........
PHP:
abstract class Resolver {
    abstract public function resolve(Name $name);
};

class CatResolver extends Resolver implements IResolveCat {
  public function resolve(Name $name);
};

class DogResolver extends Resolver implements IResolveDog {
    public function resolve(Name $name);
};
...
а, сделать абстрактную фабрику для CatResolver, DogResolver - не подходит?
 

Lionishy

Новичок
accido, абстрактная фабрика и тому подобные вещи нацелены на полиморфизм, дифференциацию реализаций. А здесь нет полиморфизма. Интерфейсы совершенно разные.

Можно открыть окно, а можно открыть новый корпус. И хотя оба интерфейса содержат метод "открыть" они вообще никак не связаны, однако может появится объект, который должен и окно открывать, и новое здание.
 

Lionishy

Новичок
В Java, как оказалась, такой же промах =(
А вот в С# можно разрешать пространство имён для методов интерфейсов.
 

hell0w0rd

Продвинутый новичок
Lionishy, это в C# промах. Интерфейс позволяет методу "ожидать" определенного поведения от передаваемого ему объекта. Если в классе, реализуя интерфейс, можно что-то в этом интерфейсе изменить - то это еще какой промах.
Промах в php - отсутствие типизации возвращаемых значений. Но думается мне скоро это исправится, уже есть NFR
 
Сверху