PHP: использование ключей массива для идентификации аргументов функции

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

function MyFunction(string $sSayThis, int $nRepeatTimes, bool $bLoud = $false) {...}

теперь у меня есть

function MyFunction(array $args)
   {$sSayThis = $args['sSayThis']); CheckType($sSayThis, 'string');
    $nRepeatTimes = $args['nRepeatTimes']); CheckType($nRepeatTimes, 'int');
    $bLoud = (IsSet($args['bLoud']) ? $args['bLoud'] : false); CheckType($bLoud, 'bool');
    ...
    }

Вызов этой функции вместо

MyFunction('Hello', 3, true);

теперь выглядит

MyFunction(array('sSayThis' => 'Hello', 'nRepeatTimes' => 3, 'bLoud' => true));

Это вряд ли необходимо, когда есть только три аргумента, как в этом примере, но это может быть очень полезно при чтении кода функции с шестью или десятью аргументами! Кроме того, если мне нужно передать значение только для десятого аргумента и использовать значения по умолчанию для всех необязательных аргументов до этого, я могу просто опустить эти другие аргументы из вызова вместо передачи для них серии , ''.

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

Я знаю, что есть IDE, которые дают мне подсказки аргументов, но я использую Notepad++, который этого не делает.

Эта идея обсуждается в аналогичном вопросе, заданном в прошлом году, Аргументы функции PHP - Использовать массив или нет?, но этот вопрос не показывает, как выглядят вызовы функций, что является наиболее важной частью вопроса. Некоторые люди в ответ на этот вопрос сказали, что функция никогда не должна нуждаться в десяти аргументах, а наличие такого количества указывает на плохой дизайн. Я понимаю эту озабоченность, но иногда алгоритму просто нужно много информации.

Что-то не так с этим подходом, или есть лучший способ самостоятельно документировать эти вызовы функций?


person NewSites    schedule 08.08.2018    source источник
comment
Этот вопрос кричит о мнении. Нечего добавить, просто хотел предупредить   -  person Andreas    schedule 08.08.2018


Ответы (2)


ИМХО, разницы в читабельности кода практически нет. Второй подход, однако, добавляет некоторые новые недостатки:

  • Он больше не использует подсказки типов PHP.
  • Ваша IDE больше не может использовать информацию, полученную из кода и аннотаций, для предоставления полезных подсказок или автоматического завершения.

Функции с большим количеством аргументов обычно являются признаком устаревшего кода, который вышел за рамки своего дизайна. Я думаю, что это требует некоторого рефакторинга, например:

class Speaker
{
    /**
     * @var string
     */
    private $sayThis;

    /**
     * @var int
     */
    private $repeatTimes;

    /**
     * @var bool
     */
    private $loud;

    /**
     * @param string $sayThis
     */
    public function __construct(string $sayThis)
    {
        $this->sayThis = $sayThis;
    }

    public function times(int $repeatTimes)
    {
        $this->repeatTimes = $repeatTimes;
        return $this;
    }

    public function loud(bool $loud = false)
    {
        $this->loud = $loud;
        return $this;
    }

    public function say()
    {
        $output = str_repeat($this->sayThis, $this->repeatTimes);
        echo $this->loud
            ? mb_strtoupper($output)
            : $output;
    }
}

(new Speaker('Foo'))
    ->times(4)
    ->loud(true)
    ->say();

Как видите, я также избавился от венгерской нотации.

person Álvaro González    schedule 08.08.2018
comment
Хорошо, это интересно. Похоже, я пытался изобрести объект с аргументом массива, не разбираясь в объектах. Ваш код выше помог мне понять, как начать переход от моей избитой техники массивов к передаче объекта. Я читаю об объектах и ​​классах и буду использовать это вместо этого. Спасибо. - person NewSites; 08.08.2018
comment
А теперь я понял другое. Вы не говорили мне передать объект в качестве аргумента функции вместо массива, но функция стала методом класса. Очень интересно. Совершенно новый способ мышления. - person NewSites; 08.08.2018
comment
@NewSites Вы также можете передать объект (это называется внедрением зависимостей) — я часто делаю это, например. пройти условия поиска. Идея (которую, я думаю, вы прекрасно уловили) состоит в том, чтобы использовать возможности языка и проводить рефакторинг, когда исходный дизайн не масштабируется. - person Álvaro González; 09.08.2018
comment
По умолчанию кажется, что его следует применять при создании свойства, такого как private $loud = false;, а не в методе loud(), который требует вызова этого метода для применения значения по умолчанию. Это правильно? - person NewSites; 09.08.2018
comment
@NewSites Верно. Это всего лишь быстрый прототип, и отсутствие значений по умолчанию — одно из самых вопиющих упущений. - person Álvaro González; 09.08.2018
comment
И делают ли что-нибудь return в методах times() и loud()? Кажется, что эти методы заканчивают свою работу, когда они применяют заданные значения к свойствам. Должны ли эти return быть опущены? - person NewSites; 09.08.2018
comment
Они возвращают текущий экземпляр класса. Это то, что делает возможной цепочку методов. - person Álvaro González; 09.08.2018
comment
Я сделал свой первый класс на основе ваших предложений. Если вы хотите взглянуть, я хотел бы знать, что вы думаете. Я разместил его по адресу stackoverflow.com/questions/51773794/< /а> . Спасибо. - person NewSites; 09.08.2018

Я бы уволил тебя. Просто шучу. А если серьезно, то это свенгали. Вы не должны пытаться изобрести что-то совершенно новое для проблемы, которая уже решена. Вы собьете с толку любого, кто попытается прочитать ваш код, и сами себя сделаете бесполезными в будущем. Это не основано на мнении, потому что есть стандартные способы справиться с этим.

Изучите ООП, особенно интерфейсы. Интерфейс определяет «контракт» того, что объект ожидает получить. [Это входные данные, которые вы постоянно забываете]. Если у вас есть функция, которая принимает 10 аргументов, вам следует сократить свою логику, чтобы функция принимала ОБЪЕКТ с 10 свойствами. Затем создайте объект из других объектов. То, что у вас останется, это 5 или около того [я предполагаю, что свойства каким-то образом связаны, поэтому это не будет 10 объектов], с которыми можно справиться всего за несколько строк кода, что делает его легко читаемым.

person John Dee    schedule 08.08.2018
comment
Вы должны меня уволить, за исключением того, что я работаю бесплатно. Ваш ответ полезен для меня в сочетании с ответом Альваро, и я обращаю внимание на интерфейсы, когда читаю об объектах и ​​классах и использую их для замены моей неуклюжей техники массивов. Спасибо. - person NewSites; 08.08.2018
comment
Я сделал свой первый класс на основе ваших предложений и предложений Альваро, хотя еще не использовал интерфейсы. Если вы хотите взглянуть, я хотел бы знать, что вы думаете. Я разместил его по адресу stackoverflow.com/questions/51773794/< /а> . Спасибо. - person NewSites; 09.08.2018