Как сделать код красивым и удобночитаемым. Процедуры, функции и классы в PHP.

Опубликовано 2017.02.06

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

Поэтому всегда важно уделять внимание оформлению кода, комментировать сложные участки кода.

Вначале немного теории. Один и тот же код можно писать разными способами. Код может быть процедурным, функциональным и объектно-ориентированным.

Процедурный подход

Процедурный подход самый простой. Под ним подразумевается скрипт, в котором сплошняком написан команды и вызваны элементарные php функции.

Например:

  1. $a = 12;
  2. $b = 10;
  3. $c = 4;
  4. $d = $a % $b;
  5. $e = $c + $d;
  6. echo round($e);

Такой подход уместен, если у вас очень маленький код или он выполняет строго одну задачу(например генерирует картинку), как говорится "ни шаг вправо, ни шаг влево".

Функциональный подход

В функциональном подходе ваш код разделён на части и каждая из них помещена в свою функцию. Это позволит избежать дублирования кода, придаст коду лаконичность и его будет проще читать.

Так-же у каждой функции будет своя собственная область видимости переменных. То есть переменные, "живущие внутри неё" никогда не просочатся наружу и переменные, используемые вне функции так-же не попадут в саму функцию.

Определяя функцию вы сами можете указать какие переменные должны попасть в функцию и что она должна вернуть, то есть вы строго контролируете этот процесс.

Исключения составляют суперглобальные переменные, такие как $_SERVER, $_REQUEST, $_GET, $_POST - они доступны всегда и везде.

Перепишем процедуру на функции:
  1. function getRemainder($a, $b) {
  2. $result = $a % $b;
  3. return $result;
  4. }
  5. function getSum($a, $b) {
  6. $result = $a + $b;
  7. return $result;
  8. }
  9. $a = 12;
  10. $b = 10;
  11. $c = 4;
  12. $d = getRemainder($a, $b);
  13. $e = getSum($c, $d);
  14. echo round($e);

Теперь вычисление остатка от деления $a на $b и нахождение суммы от $c и $d завёрнуто в функции getRemainder и getSum.

Функции могут быть определены как до их вызова, так и после. Наверняка вы обратили внимание, что в функции getSum "используются переменные" $a и $b, хотя складываем мы $c и $d. Это как раз и есть область видимости переменных внутри функции и они не имеют ничего общего с числовыми переменными $a и $b, определёнными вне функции. Мы могли назвать их как угодно. Оператор return в конце функции возвращает работы функции. То есть переменная $result возвращается наружу и её значение присваивается переменной $d или $e.

Если просто вызвать функцию, не присваивая никакой переменной её возвращаемого значения - то функция просто "отправит в космос" возвращаемое ей значение.

Объясню на примере:

  1. function getValue() {
  2. $string = 'String';
  3. return $string;
  4. }
  5.  
  6. $a = getValue();
  7. echo $a;//выведет String
  8. echo $string;//ничего не выведет, так как переменная $string существует только внутри функции getValue
  9.  
  10. //А если просто вызвать функцию
  11. getValue();
  12. echo $string;// так же ничего не выведет, так как переменная $string существует только внутри функции getValue
  13. //получается, что функция getValue "отработала вхолостую", так как никто не принял её возвращаемого значения.

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

Объектно-ориентированный подход

И третий подход - объектно-ориентированный. Сокращённо ООП(объектно-ориентированное программирование).

Он так называется в связи с тем, что он представляет из себя произвольное количество объектов с определенными наборами свойств и действий, называемых методами.

В ООП так-же есть такие вещи, как наследование, полиморфизм, абстракция, интерфейсы, конструкторы, деструкторы и многое другое. Это огромная тема и раскрывать её в рамках этой статьи я не буду.

Я покажу лишь простейший пример использования класса.

  1. class Calc {
  2. //объявление свойств(переменных) класса
  3. public $a;
  4. public $b;
  5. public $c;
  6. private $d;
  7. private $e;
  8.  
  9. //метод для определения остатка от деления
  10. private function getRemainder() {
  11. $this->d = $this->a % $this->b;
  12. }
  13.  
  14. //метод найдёт сумму
  15. private function getSum() {
  16. $this->e = $this->c + $this->d;
  17. }
  18.  
  19. //метод вернёт результат
  20. public function getResult() {
  21. self::getRemainder();
  22. self::getSum();
  23. return round($this->e);
  24. }
  25. }
  26.  
  27. //создание объекта или экземпляра класса
  28. $calc = new Calc();
  29.  
  30. //задание свойств класса
  31. $calc->a = 12;
  32. $calc->b = 10;
  33. $calc->c = 4;
  34.  
  35. //вызов метода класса
  36. $e = $calc->getResult();
  37.  
  38. //вывод на экран результата
  39. echo $e;

При объявлении свойств я использовал ключевые слова public и private.

public - это значит общедоступный и к этому свойству или методу можно обратиться в любом контексте.

private - к таким свойствам и методам можно получить доступ только внутри класса, в котором они определены.

Так же свойство или метод можно определить как protected. Это означает, что доступ к методу или свойству можно получить из текущего класса или из класса, который наследует свойства и методы текущего класса.

Ключевое слово self ссылается на текущий класс и через него можно обратиться к методам текущего класса.

Переменная $this используется для обращения к переменной или методу в контексте класса.

Вот вкратце и всё, можете проверить, все 3 способа будут выдавать одинаковый результат.

Теперь традиционно ставим задачу и её примере рассматриваем решения, так сказать обкатаем теорию.

Имеем 5 видов фруктов и у каждого указана стоимость(за кг).

По условиям задачи нужно посчитать сколько будет стоить 5кг, 12кг, 14кг, 16кг, 22кг, 135кг, 150кг, 200кг, 254кг, 300кг и 400кг каждого фрукта.

  1. //исходные данные
  2. $fruits = array();
  3. $fruits[] = array(
  4. 'name' => 'Бананы',
  5. 'price' => 65,
  6. );
  7. $fruits[] = array(
  8. 'name' => 'Ананасы',
  9. 'price' => 90,
  10. );
  11. $fruits[] = array(
  12. 'name' => 'Яблоки',
  13. 'price' => 50,
  14. );
  15. $fruits[] = array(
  16. 'name' => 'Груши',
  17. 'price' => 80,
  18. );
  19. $fruits[] = array(
  20. 'name' => 'Кокосы',
  21. 'price' => 130,
  22. );

Для наглядности попробуем решить её процедурно.

  1. foreach ($fruits as $fruit) {
  2. $price_5 = $fruit['price'] * 5;
  3. $price_12 = $fruit['price'] * 12;
  4. $price_14 = $fruit['price'] * 14;
  5. $price_16 = $fruit['price'] * 16;
  6. $price_22 = $fruit['price'] * 22;
  7. $price_135 = $fruit['price'] * 135;
  8. $price_150 = $fruit['price'] * 150;
  9. $price_200 = $fruit['price'] * 200;
  10. $price_254 = $fruit['price'] * 254;
  11. $price_300 = $fruit['price'] * 300;
  12. $price_400 = $fruit['price'] * 400;
  13.  
  14. echo $fruit['name'] . ' 5кг стоят ' . $price_5 . 'руб.<br />';
  15. echo $fruit['name'] . ' 12кг стоят ' . $price_12 . 'руб.<br />';
  16. echo $fruit['name'] . ' 14кг стоят ' . $price_14 . 'руб.<br />';
  17. echo $fruit['name'] . ' 16кг стоят ' . $price_16 . 'руб.<br />';
  18. echo $fruit['name'] . ' 22кг стоят ' . $price_22 . 'руб.<br />';
  19. echo $fruit['name'] . ' 135кг стоят ' . $price_135 . 'руб.<br />';
  20. echo $fruit['name'] . ' 150кг стоят ' . $price_150 . 'руб.<br />';
  21. echo $fruit['name'] . ' 200кг стоят ' . $price_200 . 'руб.<br />';
  22. echo $fruit['name'] . ' 254кг стоят ' . $price_254 . 'руб.<br />';
  23. echo $fruit['name'] . ' 300кг стоят ' . $price_300 . 'руб.<br />';
  24. echo $fruit['name'] . ' 400кг стоят ' . $price_400 . 'руб.<br />';
  25. }

Выглядит это громоздко и некрасиво. А если надо будет посчитать не 11 значений, а 500? И готов поспорить, что при копипасте вы точно не всё поправите и будете получать неверные данные.

Теперь пробуем функционально.
  1. //вернёт стоимость
  2. function getPrice($price, $weight) {
  3. $result_price = $price * $weight;
  4. return $result_price;
  5. }
  6. //вернёт все веса
  7. function getWeights() {
  8. return array(5, 12, 14, 16, 22, 135, 150, 200, 254, 300, 400);
  9. }
  10.  
  11. //получаем веса в переменную
  12. $weights = getWeights();
  13. //перебираем все фрукты
  14. foreach ($fruits as $fruit) {
  15. //перебираем все веса для каждого фрукта
  16. foreach ($weights as $weight) {
  17. echo $fruit['name'] . ' ' . $weight . 'кг стоят ' . getPrice($fruit['price'], $weight) . 'руб.<br />';
  18. }
  19. }

Код вышел гораздо более читабельным. Веса указаны в функции getWeights и простым добавлением их туда посчитать сколько бы стоил другой вес каждых фруктов.

Я перебрал все фрукты и при каждом переборе перебирал все веса. Можно было сделать и наоборот.

И наконец реализация на ООП.
  1. class Fruiting {
  2. public $fruits;
  3. public $weights;
  4.  
  5. public function setData($fruits, $weights) {
  6. $this->fruits = $fruits;
  7. $this->weights = $weights;
  8. }
  9.  
  10. private function getPrice($price, $weight) {
  11. $result_price = $price * $weight;
  12. return $result_price;
  13. }
  14.  
  15. public function getResult() {
  16. //перебираем все фрукты
  17. foreach ($this->fruits as $fruit) {
  18. //перебираем все веса для каждого фрукта
  19. foreach ($this->weights as $weight) {
  20. echo $fruit['name'] . ' ' . $weight . 'кг стоят ' . self::getPrice($fruit['price'], $weight) . 'руб.<br />';
  21. }
  22. }
  23. }
  24. }
  25.  
  26. $fruiting = new Fruiting();
  27. $fruiting->setData($fruits, array(5, 12, 14, 16, 22, 135, 150, 200, 254, 300, 400));
  28. $fruiting->getResult();

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

P.S. Когда пишете код - представьте, что поддерживать его будет психически неуравновешенный маньяк, который знает где вы живёте.