Выборочное наследование методов родительского класса в PHP

Tamat

Новичок
Добрый день!

Есть базовый класс с некоторым набором методов.
От него наследуется несколько других классов.
Хочется сделать так, чтобы в дочерних классах можно было удалять некоторые родительские методы.
Как это лучше реализовать?

Пример:
PHP:
class Base {

    public function Test1()
    {
        //some code
    }
    
    public function Test2()
    {
        //some code
    }
    
    public function Test3()
    {
        //some code
    }
}

class Client1 extends Base {
    
}

class Client2 extends Base {
    
}

$client1 = new Client1();
print_r(get_class_methods($client1)); //Array ( [0] => Test1 [1] => Test2 )

$client2 = new Client1();
print_r(get_class_methods($client1)); //Array ( [0] => Test2 [1] => Test3 )
Сейчас лишние методы переопределены в дочерних классах пустыми функциями.
Но, во-первых, их названия все равно есть в get_class_methods, а, во-вторых, таких пустых методов иногда требуется очень много.
Можно ли решить задачу более изящно?
 

fixxxer

К.О.
Партнер клуба
Это говорит об архитектурной ошибке.
Наверняка базовый класс перегружен лишними ответственностями и необходима его декомпозиция на отдельные классы.
Скорее всего, на самом деле там нужно не наследование, а выделение интерфейсов и делегирование с передачей зависимостей через конструкторы.

Конкретнее, конечно, можно сказать только на примере из реальной жизни, а не на test1 test2.
 

Tamat

Новичок
Спасибо за ответ!

выделение интерфейсов и делегирование с передачей зависимостей через конструкторы
Как примерно должен выглядеть такой код?

Реальный пример:
Есть система продажи товаров (база данных + API для обращения к ней)
Через API работает как сам сайт компании, так и сайты/программы партнеров.
В API есть много методов (график работы магазинов, ассортимент, наличие товаров, история транзакций и покупок, бронирование товаров, удаление из брони, покупка, методы для отображения статистики).
При этом для каких-то действий есть несколько методов (метод просмотра остатков в конкретном магазине и расширенный метод по всем магазинам + на складе | простая статистика и расширенная)
И хочется сделать так:
У первого партнера есть только методы просмотра остатков на складе.
У второго есть все методы + базовая статистика
У третьего нет метода бронирования товаров, только покупка. Но есть расширенная статистика
и т. д.

Т. е. создаем для каждого партнера класс только с его методами.
А потом для списка набранных методов (get_class_methods) генерируем документацию.
 

AnrDaemon

Продвинутый новичок
А не плевать ли, у кого что есть? Опишите базовый интерфейс, который должен быть у всех, и от него отталкивайтесь.
А дальше инъекция расширений.
 

fixxxer

К.О.
Партнер клуба
Хотя бы один общий метод есть?

Если нет, то непонятно, зачем вообще наследование. Если у классов нет совсем ничего общего, то у них не может быть общего базового класса по определению.
 

Tamat

Новичок
Хотя бы один общий метод есть?
У всех есть авторизация)

Если у классов нет совсем ничего общего
Методы все одни и те же. Просто их комбинации у каждого из партнеров разные

зачем вообще наследование
Да, наследование тут выбрано ошибочно. Просто хотелось бы перестроить систему с минимальным переписыванием.

Так-то можно при авторизации получать для каждого пользователя список доступных методов и строить для этого списка документацию. Если вызываемого метода нет в списке разрешенных, то выводить ошибку.
 

fixxxer

К.О.
Партнер клуба
Я не очень понимаю, как с этим работать. Допустим, есть базовый класс Partner, и некоторый метод некоторого класса:

PHP:
class SomeService
{
    public function doSomeMagic(Partner $partner)
    {
        // и что тут писать, если я не знаю ничего о методах?
    }
}
 

WMix

герр M:)ller
Партнер клуба
PHP:
class ApiWrapper{

  private $api;
  private $allowed = [];

  private function isAllowed($name){
    return in_array($name, $this->allowed);
  }

  public function __call($name, $arguments){
    if(!$this->isAllowed($name))
      throw new MethodNotAllowed('¯\_(ツ)_/¯');
    return call_user_func_array([$this->api, $name], $arguments);
  }
}
 

Tamat

Новичок
// и что тут писать, если я не знаю ничего о методах?
У меня как-то так получилось:
PHP:
class Partner
{
    public function getSomeData1() {
        echo 'SomeData1';
    }
   
    public function getSomeData2()
    {
        echo 'SomeData2';
    }
   
    public function getSomeData3()
    {
        echo 'SomeData2';
    }
}

class SomeService
{
    private $allowed_methods = array('getSomeData1', 'getSomeData2');
   
    public function Request(Partner $partner, $method)
    {
        if ( in_array($method, $this->allowed_methods) ) {
            $partner->$method();
        } else {
            echo('Access error');
        }
    }
   
    //Cписок разрешенных методов c документацией
    public function Documentation()
    {
        $answer = array();
        foreach ($this->allowed_methods as $method) {
            $answer[$method] = 'Описание метода';
        }
        print_r($answer);
    }
}

$partner = new Partner();

$some_service = new SomeService();
$some_service->Request($partner, 'getSomeData1'); //SomeData1
$some_service->Request($partner, 'getSomeData3'); //Access error
$some_service->Documentation(); //Array ( [getSomeData1] => Описание метода [getSomeData2] => Описание метода )
 

Вурдалак

Продвинутый новичок
Реальный пример:
Есть система продажи товаров (база данных + API для обращения к ней)
Через API работает как сам сайт компании, так и сайты/программы партнеров.
В API есть много методов (график работы магазинов, ассортимент, наличие товаров, история транзакций и покупок, бронирование товаров, удаление из брони, покупка, методы для отображения статистики).
При этом для каких-то действий есть несколько методов (метод просмотра остатков в конкретном магазине и расширенный метод по всем магазинам + на складе | простая статистика и расширенная)
И хочется сделать так:
У первого партнера есть только методы просмотра остатков на складе.
У второго есть все методы + базовая статистика
У третьего нет метода бронирования товаров, только покупка. Но есть расширенная статистика
и т. д.

Т. е. создаем для каждого партнера класс только с его методами.
Тебе нужно изучить понятие bounded context, чтобы понять, что тут нужен не один класс.
Если на словах у партнёра что-то есть (статистика, возможность бронировать, ...), то это совсем не значит, что есть класс Partner с таким методом.
 

fixxxer

К.О.
Партнер клуба
@WMix, ты зачем людям плохое советуешь? Сам ведь понимаешь же, какой ад будет :)
 

WMix

герр M:)ller
Партнер клуба
вы усложняете, вызов метода это простая магия с обычным "execute" ( в твоем варианте не лучше $partner->$method(); ), как будто в этот момент вас интересует какая команда (в смысле от какого класса) там была заинжектина (она просто тип интерфейса executable), это решается на другом уровне, я просто упростил до минимума.
Я не очень понимаю, как с этим работать. Допустим, есть базовый класс Partner, и некоторый метод некоторого класса:
до этого не дойдет, вьюшка должна предоставлять только возможное апи (хочешь option для rest). для этого у тебя есть список разрешенных фич.

на самом деле очень стандартная задача, когда пользователю покупать можно, а аккаунт смотреть нельзя
 
Последнее редактирование:

WMix

герр M:)ller
Партнер клуба
@Вурдалак, просто вывалить линк слишком простая позиция, попробуй описать от начала до конца всю цепочку, и обязательно натолкнешься на место где придется писать либо new $class, либо $function(), либо рефлексия, либо набор switch в ручном режиме. но твое "завышенное представлений о собственных способностях, в то время как действительно высококвалифицированные люди, наоборот, склонны занижать оценку своих способностей и страдать недостаточной уверенностью в своих силах, считая других более компетентными" мешает тебе это сделать.
 

Вурдалак

Продвинутый новичок
Автор описывает неудачный пример моделирования, когда модель «Партнёр» обрастает поведением через наследование, но тут приходишь ты и рассказываешь про контроллеры и мэппинг API-запросов на них. Ты не видишь проблемы дальше своего носа, но уже кричишь громче всех о решении.
 

WMix

герр M:)ller
Партнер клуба
ему не интересны сейчас bounded context, но я с большим удовольствием еще раз прочел, примеряя на это описанную проблему. у него же все готово от удаленного провайдера (он называет это api), осталось разделить по правам доступа. он просто не знает как эту проблему описать.
 
Сверху