Бизнес логика компоненты должна во время работы использовать исключительно данные локального контекста:
- Входящие аргументы
- Внутренние свойства компоненты
Не являющимися локальными считаются данные, полученные:
- Из глобальных переменных
- Из суперглобальных массивов
- Из классов (статика)
- Из функций
php
- Из изменяемого внутреннего контекста
Всё это необходимо для соблюдения повторяемости: равенство y = f(x1, ..., xN)
соблюдается всегда (сегодня, завтра, через год, на любой системе)
Рассмотрим простой пример:
/**
* Returns greeting message for user
* /
function getGreetMessage($username) {
return "Welcome, {$username}!";
}
Указанная функция работает с локальным контекстом данных и следовательно имеет чётко детерминированное поведение:
- Передав в неё
Foo
я получуWelcome, Foo!
- Передав в неё
Bar
я получуWelcome, Bar!
Имея подобную информацию я пишу тесты, где путём ассертов проверяю возврат ожидаемых значений. Код детерминирован, тестами обложен, надёжен.
Через некоторое время была поставлена задача - отображать разный текст приветствия для русского и английского языков. Ниже представлены примеры реализации.
/**
* Returns greeting message for user
* /
function getGreetMessage($username) {
global $currentLang;
return ($currentLang === 'ru' ? 'Добро пожаловать' : 'Welcome') . ", {$username}!";
}
Подразумевается, что есть глобальная переменная $currentLang
.
Чем плохо:
- Возникает глобальная переменная, причём нет возможности быстро отследить - где она используется
- Результат выполнения функции зависит не только от входящих данных и для тестирования нужно знать об дополнительных данных и инициализировать их. При этом передача в функцию происходит неявно (мы данные не передаём, но они оказываются внутри функции)
/**
* Returns greeting message for user
* /
function getGreetMessage($username) {
return ($_SESSION['lang'] === 'ru' ? 'Добро пожаловать' : 'Welcome') . ", {$username}!";
}
/**
* Returns greeting message for user
* /
function getGreetMessage($username) {
return (Locale::getCurrent() === 'ru' ? 'Добро пожаловать' : 'Welcome') . ", {$username}!";
}
/**
* Returns greeting message for user
* /
function getGreetMessage($username) {
return (getenv('LANG') === 'ru' ? 'Добро пожаловать' : 'Welcome') . ", {$username}!";
}
Более простой пример нарушение контекста - функция time()
, используемая повсеместно
Здесь - отдельный, пример, не связанный с языком:
/**
* Returns greeting message for user
* /
function getGreetMessage($username) {
static $count;
return ($count++ > 1 ? 'Welcome back': 'Hello') . ", {$username}!";
}
Ситуация плачевна - результат вызова функции не зависит от окружающего кода, а от количества вызовов.
Аналогично и с классами:
class Decoder
{
private $value;
public function set($value)
{
$this->value = $value;
}
public function get()
{
return $this->value;
}
public function jsonDecode()
{
$this->value = json_decode($this->value, true);
}
public function base64_decode()
{
$this->value = base64_decode($this->value);
}
}
/**
* Returns greeting message for user
* /
function getGreetMessage($username, $lang) {
return ($lang === 'ru' ? 'Добро пожаловать' : 'Welcome') . ", {$username}!";
}