Skip to content

Instantly share code, notes, and snippets.

@gotterdemarung
Last active August 29, 2015 14:05
Show Gist options
  • Save gotterdemarung/65368255391c605ca279 to your computer and use it in GitHub Desktop.
Save gotterdemarung/65368255391c605ca279 to your computer and use it in GitHub Desktop.
Local Context

Point

Бизнес логика компоненты должна во время работы использовать исключительно данные локального контекста:

  • Входящие аргументы
  • Внутренние свойства компоненты

Не являющимися локальными считаются данные, полученные:

  • Из глобальных переменных
  • Из суперглобальных массивов
  • Из классов (статика)
  • Из функций php
  • Из изменяемого внутреннего контекста

Всё это необходимо для соблюдения повторяемости: равенство y = f(x1, ..., xN) соблюдается всегда (сегодня, завтра, через год, на любой системе)

Explanation

Рассмотрим простой пример:

/**
 * 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}!";
}

Нарушение через функции PHP

/**
 * 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}!";
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment