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

Вурдалак

Продвинутый новичок
hell0w0rd, возвращаемое значение нигде не учитывается :) Нельзя создавать 2 метода, которые отличаются только возвращаемым значением.
 

Lionishy

Новичок
hell0w0rd, сигнатура метода не может включать возвращаемое значение. У Страуструпа есть примеры, когда это приводит к неразрешимым ситуациям.

Решить через базовый интерфейс можно было бы, если бы это были бы два полиморфных интерфейса, для которых предполагалось бы одинаковое поведение. В общем, если бы бабушка была дедушкой. Но ни анализ, ни проект, ни конструирование не дают предпосылок к полиморфизму. Это два разных интерфейса, как Iterator и Countable.

WMix, компилировал gcc 4.7.2 из пакетов Ubuntu.
Код:
#include<iostream>

namespace vendor1 {
  
struct IResolveCat {
    virtual void resolve() =0;
  
    virtual ~IResolveCat()
    {
    }
};

void explain(IResolveCat& resolver)
{
    resolver.resolve();
}

} // namespace vendor1

namespace vendorB {
  
struct IResolveDog {
    virtual void resolve() =0;
      
    virtual ~IResolveDog()
    {
    }
};

void explain(IResolveDog& resolver)
{
    resolver.resolve();
}

}// namespace vendorB

namespace my_namespace {
  
struct IResolveCat: public vendor1::IResolveCat {
    virtual void resolve()
    {
        resolveCat();
    }
  
    virtual ~IResolveCat()
    {
    }
  
    protected:
        virtual void resolveCat() =0;
};

struct IResolveDog: public vendorB::IResolveDog {
    virtual void resolve()
    {
        resolveDog();
    }
  
    virtual ~IResolveDog()
    {
    }
  
    protected:
        virtual void resolveDog() =0;
};
  
  
class Resolver: public virtual IResolveCat, public virtual IResolveDog {
    protected:
        virtual void resolveCat();
        virtual void resolveDog();
};

void Resolver::resolveCat()
{
    std::cout << "I'm a CAT resolved by Resolver" << std::endl;
}
      
void Resolver::resolveDog()
{
    std::cout << "I'm a DOG resolved by Resolver" << std::endl;
}

} //namespace my_namespace

int main(int args, char** argv)
{
    my_namespace::Resolver resolver;
  
    vendor1::explain(resolver);
    vendorB::explain(resolver);
  
    return 0;
}
 

riff

Новичок
WMix, компилировал gcc 4.7.2 из пакетов Ubuntu.
Код:
#include<iostream>

namespace vendor1 {

struct IResolveCat {
    virtual void resolve() =0;

    virtual ~IResolveCat()
    {
    }
};

.....
}
Ну вообще-то, в твоём примере, речь идёт больше о множественном наследии нежели об интерфейсах.
В php множественного наследия нет, но твой пример "легко" переводится на php:
PHP:
<?php

namespace {
    ini_set('display_errors', 1);
    error_reporting(E_ALL | E_STRICT);

    class Resolver
    {
        protected $list = [];

        public function __get($name)
        {
            if (isset($this->list[$name]))
            {
                return $this->list[$name];
            }
            throw new \Exception($name);
        }   
    }
}

namespace vendorA
{
    class IResolveCat
    {
        public function resolve()
        {
            $this->resolveCat();
        }

        protected function resolveCat()
        {
        }
    }

    /*
     * или вместо \Resolver какой-нибудь IResolver, 
     * чтобы не завязываться на конкретный класс
     */
    function explain(\Resolver $resolver)
    {
        //то, что ты в примере C++ прописал тип в параметре
        //и компилятор выдернул нужную структуру
        //здесь получаем её сами
        /** @var IResolveCat $cat */
        $cat = $resolver->{'IResolveCat'}; //или типа $resolve->getByName($name)
        $cat->resolve();
    }

} // namespace vendorA

namespace vendorB
{
    class IResolveDog
    {
        public function resolve()
        {
            $this->resolveDog();
        }

        protected function resolveDog()
        {
        }
    }

    function explain(\Resolver $resolver)
    {
        /** @var IResolveDog $dog */
        $dog = $resolver->{'IResolveDog'};
        $dog->resolve();
    }

} // namespace vendorB

namespace my_namespace
{
    class IResolveCat extends \vendorA\IResolveCat
    {
        protected function resolveCat()
        {
            echo 'Cat';
        }
    }

    class IResolveDog extends \vendorB\IResolveDog
    {
        protected function resolveDog()
        {
            echo 'Dog';
        }
    }

    class Resolver extends \Resolver
    {
        public function __construct()
        {
            $this->list['IResolveCat'] = new IResolveCat;
            $this->list['IResolveDog'] = new IResolveDog;
        }
    }
} //namespace my_namespace

namespace {
    $resolver = new \my_namespace\Resolver;
    \vendorA\explain($resolver);
    \vendorB\explain($resolver);
}
 
Последнее редактирование:

WMix

герр M:)ller
Партнер клуба
PHP:
<?php

class Dog{
    public function resolve( IResolveDog $resolver ){
        $resolver->resolveDog();
    }
}

class Cat{
    public function resolve( IResolveCat $resolver ){
        $resolver->resolveCat();
    }
}

interface IResolveDog{
    public function resolveDog();
}
interface IResolveCat{
    public function resolveCat();
}


class Resolver implements IResolveDog, IResolveCat{
    public function resolveCat(){
        echo "I'm a CAT resolved by Resolver";
    }
     
    public function resolveDog(){
        echo "I'm a DOG resolved by Resolver";
    }
}

$cat = new Cat;
$dog = new Dog;
$cat->resolve( new Resolver );
$dog->resolve( new Resolver );
 
  • Like
Реакции: riff

Lionishy

Новичок
riff, в PHP примере есть существенное отличие от примера на C++: функции vendorA::explain и vendorB::explain принимают базовый \Resolver, который может быть им не известен.

множественное наследование
В С++ нет интерфейсов, но есть "чисто виртуальные методы", которые не предоставляют реализации. Фактически же структура с набором чисто виртуальных методов -- это в точности интерфейс.
Однако модель наследования в С++ хитрая, которая и достаточно гибкая и не слишком накладная. В частности, все "чисто виртуальные методы" всегда переопределяются. А вот виртуальные методы с базовой реализацией наследуются вместе с реализацией, то есть внутри производного класса создаются разные базовые сущности. На этом и основан пример. Это что-то хрестоматийное, бродит по всем книгам примеров (cook-book).

WMix, ваш пример не эквивалентен предложенному на C++. Он делает нечто похожее, но методы в интерфейсах IResolveDog и IResolveCat имеют разную сигнатуру. А проблема именно в том, что сигнатура одинаковая.
 
Последнее редактирование:

riff

Новичок
riff, в PHP примере есть существенное отличие от примера на C++: функции vendorA::explain и vendorB::explain принимают базовый \Resolver, который может быть им не известен.
Базовый класс, можно заменить на понятный им общий интерфейс, или вообще убрать тип, оставив просто "$resolve", всё таки мы не ограничены типитизацией параметров.
 

Lionishy

Новичок
riff, это было бы возможно, если бы речь шла об одном-двух программистах. Если это разные группы, то так просто не пройдёт. А если это один-два программиста, то уж точно можно было бы просто поправить имена методов. Интерфейс -- это, всё же, контракт. Отказ от контракта -- это более сильное преступление, чем шов при конструировании.

Это, конечно, моё личное мнение. Но я лучше бы заставил программистов писать каждый раз явный вызов адаптера (вроде явного вызова привидения типов), чем позволил бы отказаться от контракта.
 
Последнее редактирование:

riff

Новичок
В С++ нет интерфейсов, но есть "чисто виртуальные методы", которые не предоставляют реализации. Фактически же структура с набором чисто виртуальных методов -- это в точности интерфейс....
Ну извини, уж как смог близко по смыслу перевёл твой пример. То, что "структура в C++ -- это в точности интерфейс" -- это ваш(сишников) плюс, а за рамки того что есть в php, перейти не могу, класс он и остаётся классом :)
 

Lionishy

Новичок
WMix,
у тебя тоже самое
Нет, совершенно другое.

Сотри тип, это как "отстегни ремень безопасности".

Посмотрите ещё раз самый первый пост. Это условие. Его нарушать никак нельзя. Свои типы добавить можно, а вот изменить рисунок поставщиков никак.

Может это поможет Вам понять разницу.
 
Последнее редактирование:

Lionishy

Новичок
riff, извиняю. Здесь дело каждого, как поступить. Если неожиданно совпадут методы из интерфейсов каких-нибудь корпоративных производителей, то с возможностями "убрать type-hint" или "переименовать метод" можно будет распрощаться. Да и вариант с композицией интерфейсов мне ближе. COM напоминает.
 

WMix

герр M:)ller
Партнер клуба
Lionishy, пока не понятно что ты хочешь!
то что у тебя выполняется в main, выполняется и у меня остальное это внутренняя реализация, и нет смысла ее повторять. добавь еще пару вызовов в main чтоб показать отличие твоего кода от моего!
 

Lionishy

Новичок
WMix,
то что у тебя выполняется в main, выполняется и у меня остальное это внутренняя реализация
Нет. То, что у меня в main не выполняется в вашем коде. Explain требует два различных интерфейса, которые содержат одинаковый метод. У Вас методы разные.

К Вам с Марса и Венеры отправили два пакета кода:

PHP:
namespace Mars {
interface IResolver {
    /**
    */
    public functon resolve();
};

/**
* @param IResolver
*/
function doResolve(IResolver $resolver)
{
    $resolver->resolve()
}

} //namespace Mars

namespace Venus {
interface IResolver {
    /**
    */
    public functon resolve();
};

/**
* @param IResolver
*/
function doResolve(IResolver $resolver)
{
    $resolver->resolve()
}

} //namespace Venus
Есть необходимость одну и туже сущность передавать и в \Mars\doResolve, и в \Venus\doResolve.
Марсианский и Венерианский коды менять не получится, связь сегодня плохая, солнечная буря.

Как это сделать?
 

WMix

герр M:)ller
Партнер клуба
Lionishy, вот тебя кидает то...
еще раз для прапорщиков, покажи как ты собираешься использовать "два различных интерфейса, которые содержат одинаковый метод" просто напиши ЕЩЕ 2 вызова в main
в томже C++ коде
 

Lionishy

Новичок
WMix, уже написаны две функции, которые используют одну и ту же сущность в двух различных аспектах, при этом внутри функций вызывается метод с одной и той же сигнатурой.
Как ещё вызовы Вы хотите, чтобы я написал?
 

WMix

герр M:)ller
Партнер клуба
еще раз, это внутренняя реализация, покажи отдельно и то и другое иначе все верят в то что реализация твоя довольно дибильная, нет смысла так закручивать.
интересно только то что реально используется, и откуда будет ответ! и это готово!
PHP:
$cat = new Cat;
$dog = new Dog;
$cat->resolve( new Resolver );
$dog->resolve( new Resolver );
 

Lionishy

Новичок
WMix, я Вас совсем перестаю понимать.

Сформулируйте, что такое "внутренняя реализация" и почему она не является частью проекта.

На всякий случай.
Я бы назвал внутренней реализацией всё то, что не представлено в примере, а также реализацию функций vendor1::explain и vendor2::explain. А вот открытые интерфейсы, я бы никак не отнёс ко "внутреней реализации", кому бы эти открытые интерфейсы не принадлежали.
 
Последнее редактирование:

WMix

герр M:)ller
Партнер клуба
конструкция на команду $cat->resolve( new Resolver ); запускает resolveCat() в классе Resolver. это задача.
как она реализовано, остается за скобками. добавь вторую задачу но только снаружи в main что еще может твоя программа чего не может моя
 

Lionishy

Новичок
WMix, откуда взялась задача про "вызвать resolveCat()"? Какую-нибудь мою цитату можете привести? Может быть я плохо изъясняюсь?

Задача состояла в том, чтобы на одной сущности можно было вызвать методы с идентичной сигнатурой, но из разных интерфейсов. Вы просили "компилируемый пример с функцией main", я привёл. Естественно, что в нём нет никакого содержания существенно. Это каркас. И он делает то, что написано курсивом.

Более широкий вариант: есть два пакета, которые требуют два разных контракта, каждый из которых определяет по методу с идентичными сигнатурами, нужно одну сущность (одну реализацию, одну алгебру) заставить работать синхронизировано в разных аспектах.

Первое -- очевидное решение второго.


Если Вы чего-то не понимаете, скажите, что конкретно из написанного мной не ясно. Слово какое или словосочетание. Я поясню. Когда Вы будете абсолютно уверены, что понимаете, попробуйте сделать.
 
Последнее редактирование:
Сверху