Лекция
Это продолжение увлекательной статьи про идеальный код.
...
шаблон, используемый во многих библиотеках, например в PHPUnit и Doctrine. Он делает вашу кодовую базу более выразительной и менее многословной. Поэтому я рекомендую объединять методы в цепочки (chaining), и вы сами увидите, насколько чистым станет ваш код. В конце каждой функции класса просто возвращайте this — и сможете прикреплять к нему следующий метод класса.
Плохо:
class Car {
private $make, $model, $color;
public function __construct() {
$this->make = 'Honda';
$this->model = 'Accord';
$this->color = 'white';
}
public function setMake($make) {
$this->make = $make;
}
public function setModel($model) {
$this->model = $model;
}
public function setColor($color) {
$this->color = $color;
}
public function dump() {
var_dump($this->make, $this->model, $this->color);
}
}
$car = new Car();
$car->setColor('pink');
$car->setMake('Ford');
$car->setModel('F-150');
$car->dump();
Хорошо:
class Car {
private $make, $model, $color;
public function __construct() {
$this->make = 'Honda';
$this->model = 'Accord';
$this->color = 'white';
}
public function setMake($make) {
$this->make = $make;
// NOTE: Returning this for chaining
return $this;
}
public function setModel($model) {
$this->model = $model;
// NOTE: Returning this for chaining
return $this;
}
public function setColor($color) {
$this->color = $color;
// NOTE: Returning this for chaining
return $this;
}
public function dump() {
var_dump($this->make, $this->model, $this->color);
}
}
$car = (new Car())
->setColor('pink')
->setMake('Ford')
->setModel('F-150')
->dump();
Как говорится в известной книге «Шаблоны проектирования» Банды четырех, по мере возможности нужно выбирать композицию, а не наследование. Об этом говорит сайт https://intellect.icu . Есть много хороших причин использовать как наследование, так и композицию. Главная цель этой максимы заключается в том, если вы инстинктивно склоняетесь к наследованию, то постарайтесь представить, может ли композиция лучше решить вашу задачу. В каких-то случаях это действительно более подходящий вариант.
Вы спросите: «А когда лучше выбирать наследование?» Все зависит от конкретной задачи, но можно ориентироваться на этот список ситуаций, когда наследование предпочтительнее композиции:
Плохо:
class Employee {
private $name, $email;
public function __construct($name, $email) {
$this->name = $name;
$this->email = $email;
}
// ...
}
// Bad because Employees "have" tax data.
// EmployeeTaxData is not a type of Employee
class EmployeeTaxData extends Employee {
private $ssn, $salary;
public function __construct($ssn, $salary) {
parent::__construct();
$this->ssn = $ssn;
$this->salary = $salary;
}
// ...
}
Хорошо:
class EmployeeTaxData {
private $ssn, $salary;
public function __construct($ssn, $salary) {
$this->ssn = $ssn;
$this->salary = $salary;
}
// ...
}
class Employee {
private $name, $email, $taxData;
public function __construct($name, $email) {
$this->name = $name;
$this->email = $email;
}
public function setTaxData($ssn, $salary) {
$this->taxData = new EmployeeTaxData($ssn, $salary);
}
// ...
}
IDE становятся все более популярны в мире разработчиков, т.к. они предоставляют удобные инструменты для комментирования и документирования кода.
Вот пример:

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

В этом примере стиль комментирования основан на PHPDoc, а IDE, которой я пользуюсь, - Aptana.
Я полагаю, что вы уже знаете о важности отступов в вашем коде. Вообще существует несколько стилей форматирования кода.
Стиль 1:

Стиль 2:

Стиль 3:

Также существуют стили, которые объединяют некоторые характеристики. К примеру, стандарты написания кода PEAR, где фигурная скобка "{" в условных операторах остается на той же строке, а в функциях переносится.
Стиль PEAR:

Также следует отметить, что в этом стиле вместо табов используются 4 пробела.
Тут вы сможете узнать больше о различных стилях.
Да, комментирование кода - это хорошо; однако тут не нужно перебарщивать. Вот пример:

Если уж не имется, то можно их немного сократить:

Чаще всего некоторые задачи требуют написания нескольких строк кода. Поэтому лучше всего объединять такие задачи в отдельные блоки, разделенные пробелами.
Вот простой пример:

Иногда даже в языке PHP можно найти противоречия именования функций. И вот многочисленные примеры:
Существует несколько популярных стилей:
Если смешивать эти техники, то рано или поздно можно попасть в неловкую ситуацию. Если вы работаете над проектом, в котором применяется одна из этих техник, то вам надо следовать их примеру. Все еще может зависеть от языка программирования. К примеру, большинство Java разработчиков используют camelCase а PHP разработчики предпочитают underscores.
Но и тут не обошлось без гибрида. Некоторые разработчики используют подчеркивания в именовании классов и методов (вне классов), а в остальных случаях используют camelCase:

DRY (Don’t Repeat Yourself) - не повторяйся. Так же известно как DIE: Дублирование - это зло.
Главная задача любой системы, будь то веб приложение или что-то еще, - автоматизировать повторяющиеся задачи. Этому принципу нужно следовать всегда и везде, особенно если ты разработчик. Один и тот же кусок кода не должен повторяться снова и снова.
К примеру, большинство веб приложений состоит из одной и более страниц. Понятное дело, что на этих страницах будут присутствовать одинаковые элементы. Заголовок, футер - самые яркие примеры. Вы удивитесь, но многие люди все еще дублирует эти элементы на каждой странице.

Читабельность кода резко уменьшается, если у вас глубокая вложенность.

Для того чтобы исправить ситуацию, вам следует пересмотреть принцип работы вашего кода и оптимизировать его:

Всем известно, что процесс чтения становится куда приятней, когда текст разбит на колонки. Это главная причина, по которой наши газеты выглядят именно так:

Подобную технику можно применить и к нашему коду:

Большинство разработчиков придерживаются лимита в 80 и 120 символов.
Технически вы можете поместить весь код вашего приложения в один файл :) Но что вы будете делать, когда надо будет что-то изменить или добавить.
Помню свои первые проекты, в которых я присоединял файлы. Однако организация у меня сильно хромала. Я создавал папку “inc”, в которой располагал несколько файлов: db.php и functions.php. В процессе написания приложения эта папка пухла и пухла и в конечном итоге было трудно понять что где.
Чтобы решить эту проблему лучше пользоваться различного рода фрэймворками или хотя бы придерживаться их структуры. Вот так выглядит проект на CodeIgniter:

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

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

Это еще один принцип, который поможет вам писать более понятные программы. Он заключается в том, чтобы вы готовили данные в одном месте (допустим моделях), а взаимодействовали с ними в другом.
Когда PHP только начинал развиваться, он больше был похож на систему шаблонов. Проекты на данном языке содержали смешанный HTML и PHP код. Сейчас все изменилось, и всем следует переходить на новый уровень написания приложений.
Вы можете сами выработать для себя какой-то особый стиль, а можете воспользоваться самыми популярными на сегодняшний день средствами.
Популяреные PHP Фрэймворки:
Системы Шаблонов:
Популярные CMS
Если вы не хотите использовать систему шаблонов, то вам скорее всего придется выработать свой собственный стиль внедрения PHP кода в HTML.
А вот и пример:

Такая техника позволит вам избежать лишних скобок. Также такой код удачно вписывается в HTML контекст.
Объектно ориентированное программирование поможет вам придерживаться более или менее четкой структуры, но это все не значит, что вы должны отступать от процедуральных принципов написания приложений.
Объекты прекрасно подходят для представления данных. Пример:

Процедуральные методы имеют свою специфическую пользу.

15. Читайте Open Source Код
Обычно проекты Open Source пишутся большим количеством разработчиков. С этой точки зрения, изучение написанного кода в подобных проектах может помочь вам набраться опыта. Так что не жалейте на это времени.

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

Принципы программной инженерии из книги Роберта Мартина « Чистый код» , адаптированные для JavaScript. Это не руководство по стилю. Это руководство по созданию читаемого, многоразового и рефакторируемого программного обеспечения на JavaScript.
Не все принципы, изложенные в этом документе, должны строго соблюдаться, и даже меньшее из них будет согласовано во всем мире. Это рекомендации и не более того, но они систематизированы в результате многолетнего коллективного опыта авторов « Чистого кода» .
Нашему ремеслу в области разработки программного обеспечения чуть более 50 лет, и мы все еще многому учимся. Когда архитектура программного обеспечения так же стара, как сама архитектура, может быть, тогда нам придется соблюдать более жесткие правила. А пока позвольте этим рекомендациям служить пробным камнем для оценки качества кода JavaScript, создаваемого вами и вашей командой.
Еще одна вещь: знание этого не сразу сделает вас лучшим разработчиком программного обеспечения, а работа с ними в течение многих лет не означает, что вы не будете делать ошибок. Каждый фрагмент кода начинается с первого наброска, как мокрая глина, которая принимает окончательную форму. Наконец, мы устраняем недостатки, когда обсуждаем это с нашими коллегами. Не ругайте себя за первые наброски, которые нужно улучшить. Вместо этого взбейте код!
Плохой:
const ггггммдстр = момент ( ) . формат ( «ГГГГ / ММ / ДД» ) ;
Хороший:
const currentDate = момент ( ) . формат ( «ГГГГ / ММ / ДД» ) ;

Плохой:
getUserInfo ( ) ; getClientData ( ) ; getCustomerRecord ( ) ;
Хороший:
getUser ( ) ;

Мы прочитаем больше кода, чем когда-либо напишем. Важно, чтобы код, который мы пишем, был доступен для чтения и поиска. К не называя переменные , которые в конечном итоге смысл для понимания нашей программы, мы раним наших читателей. Сделайте ваши имена доступными для поиска. Такие инструменты, как buddy.js и ESLint, могут помочь идентифицировать безымянные константы.
Плохой:
// Какого черта 86400000? setTimeout ( blastOff , 86400000 ) ;
Хороший:
// Объявить их как именованные константы с заглавной буквы. const MILLISECONDS_PER_DAY = 60 * 60 * 24 * 1000 ; // 86400000; setTimeout ( blastOff , MILLISECONDS_PER_DAY ) ;

Плохой:
const address = "Один бесконечный цикл, Купертино 95014" ; const cityZipCodeRegex = / ^ [ ^, \\ ] + [ , \\ \ s ] + ( . + ? ) \ s * ( \ d { 5 } ) ? $ / ; saveCityZipCode ( адрес . match ( cityZipCodeRegex ) [ 1 ] , адрес .совпадение ( cityZipCodeRegex ) [ 2 ] ) ;
Хороший:
const address = "Один бесконечный цикл, Купертино 95014" ; const cityZipCodeRegex = / ^ [ ^, \\ ] + [ , \\ \ s ] + ( . + ? ) \ s * ( \ d { 5 } ) ? $ / ; const [ _ , city , zipCode ] = адрес . совпадение ( cityZipCodeRegex ) || ; saveCityZipCode ( город , почтовый индекс ) ;

Явное лучше, чем неявное.
Плохой:
const location = [ "Остин" , "Нью-Йорк" , "Сан-Франциско" ] ; локации . forEach ( l => { doStuff ( ) ; doSomeOtherStuff ( ) ; // ... // ... // ... // Подождите, для чего снова нужен `l`? dispatch ( l ) ; } ) ;
Хороший:
const location = [ "Остин" , "Нью-Йорк" , "Сан-Франциско" ] ; локации . forEach ( location => { doStuff ( ) ; doSomeOtherStuff ( ) ; // ... // ... // ... dispatch ( location ) ; } ) ;

Если имя вашего класса / объекта вам что-то говорит, не повторяйте это в имени переменной.
Плохой:
const Car = { carMake : "Honda" , carModel : "Accord" , carColor : "Blue" } ; функция paintCar ( car , color ) { car . carColor = цвет ; }
Хороший:
const Car = { марка : "Honda" , модель : "Accord" , цвет : "Blue" } ; функция paintCar ( car , color ) { car . color = цвет ; }

Аргументы по умолчанию часто чище, чем короткое замыкание. Имейте в виду, что если вы их используете, ваша функция будет предоставлять только значения по умолчанию для undefined аргументов. Другие «falsy» ценности , такие как '', "", false, null, 0, и NaN, не будут заменены на значения по умолчанию.
Плохой:
функция createMicrobrewery ( name ) { const breweryName = name || "Hipster Brew Co." ; // ... }
Хороший:
function createMicrobrewery ( name = "Hipster Brew Co." ) { // ... }

Ограничение количества параметров функции невероятно важно, потому что это упрощает тестирование вашей функции. Наличие более трех приводит к комбинаторному взрыву, когда вам нужно проверять тонны разных случаев с каждым отдельным аргументом.
Один или два аргумента - идеальный случай, а трех следует по возможности избегать. Все, что больше этого, следует консолидировать. Обычно, если у вас более двух аргументов, ваша функция пытается сделать слишком много. В тех случаях, когда это не так, в большинстве случаев в качестве аргумента будет достаточно объекта более высокого уровня.
Поскольку JavaScript позволяет вам создавать объекты на лету, без большого количества шаблонов классов, вы можете использовать объект, если вам нужно много аргументов.
Чтобы сделать очевидным, какие свойства ожидает функция, вы можете использовать синтаксис деструктуризации ES2015 / ES6. У этого есть несколько преимуществ:
Плохой:
function createMenu ( title , body , buttonText , cancellable ) { // ... } createMenu ( "Foo" , "Bar" , "Baz" , истина ) ;
Хороший:
function createMenu ( { заголовок , тело , buttonText , cancellable } ) { // ... } createMenu ( { заголовок : "Foo" , body : "Bar" , buttonText : "Baz" , cancellable : true } ) ;

Это, безусловно, самое важное правило в разработке программного обеспечения. Когда функции выполняют несколько задач, их сложнее составлять, тестировать и обдумывать. Когда вы можете изолировать функцию до одного действия, ее можно легко реорганизовать, и ваш код будет читаться намного чище. Если вы не вынесете из этого руководства ничего, кроме этого, вы опередите многих разработчиков.
Плохой:
функция emailClients ( клиенты ) { клиенты . forEach ( клиент => { const clientRecord = база данных . поиск ( клиент ) ; если ( clientRecord . isActive ( ) ) { электронная почта ( клиент ) ; } } ) ; }
Хороший:
функция emailActiveClients ( клиенты ) { клиенты . фильтр ( isActiveClient ) . forEach ( электронная почта ) ; } функция isActiveClient ( клиент ) { const clientRecord = база данных . поиск ( клиент ) ; вернуть clientRecord . isActive ( ) ; }

Плохой:
function addToDate ( число , месяц ) { // ... } const date = новая дата ( ) ; // По названию функции сложно сказать, что добавлено addToDate ( date , 1 ) ;
Хороший:
function addMonthToDate ( месяц , число ) { // ... } const date = новая дата ( ) ; addMonthToDate ( 1 , дата ) ;

Когда у вас более одного уровня абстракции, ваша функция обычно делает слишком много. Разделение функций приводит к повторному использованию и упрощению тестирования.
Плохой:
функция parseBetterJSAlternative ( код ) { const REGEXES = [ // ... ] ; Операторы const = код . сплит ( "" ) ; const tokens = ; РЕГЕКСЫ . forEach ( REGEX => { операторы . forEach ( оператор => { // ... } ) ; } ) ; const ast = ; жетоны . forEach ( token => { // lex ... } ) ; аст . forEach ( node => { // анализировать ... } ) ; }
Хороший:
функция parseBetterJSAlternative ( код ) { const tokens = tokenize ( код ) ; const syntaxTree = parse ( токены ) ; syntaxTree . forEach ( node => { // анализировать ... } ) ; } функция tokenize ( код ) { const REGEXES = [ // ... ] ; Операторы const = код . сплит ( "" ) ; const tokens = ; РЕГЕКСЫ . forEach ( REGEX => { операторы . forEach ( оператор => { токены . push ( / * ... * / ) ; } ) ; } ) ; вернуть жетоны ; } функция синтаксического анализа ( токены ) { const syntaxTree = ; жетоны . forEach ( токен => { syntaxTree . push ( / * ... * / ) ; } ) ; return syntaxTree ; }

Сделайте все возможное, чтобы избежать дублирования кода. Дублирование кода - это плохо, потому что это означает, что есть более одного места, где можно что-то изменить, если вам нужно изменить какую-то логику.
Представьте, что вы управляете рестораном и отслеживаете свой инвентарь: все ваши помидоры, лук, чеснок, специи и т.д. в них. Если у вас только один список, есть только одно место для обновления!
Часто у вас есть дублированный код, потому что у вас есть две или более немного разных вещи, которые имеют много общего, но их различия вынуждают вас иметь две или более отдельных функций, которые делают много одинаковых вещей. Удаление повторяющегося кода означает создание абстракции, которая может обрабатывать этот набор разных вещей с помощью всего одной функции / модуля / класса.
Правильная абстракция имеет решающее значение, поэтому вы должны следовать принципам SOLID, изложенным в разделе « Классы ». Плохие абстракции могут быть хуже дублированного кода, поэтому будьте осторожны! Сказав это, если вы можете сделать хорошую абстракцию, сделайте это! Не повторяйтесь, иначе вы обнаружите, что обновляете несколько мест в любое время, когда захотите изменить что-то одно.
Плохой:
функция showDeveloperList ( разработчики ) { разработчики . forEach ( разработчик => { const expectedSalary = developer . calculateExpectedSalary ( ) ; const experience = developer . getExperience ( ) ; const githubLink = developer . getGithubLink ( ) ; const data = { expectedSalary , опыт , githubLink } ; рендер ( данные ) ; } ) ; } функция showManagerList ( менеджеры ) { менеджеры . forEach ( менеджер => { const expectedSalary = manager . calculateExpectedSalary ( ) ; const experience = manager . getExperience ( ) ; const портфолио = manager . getMBAProjects ( ) ; const data = { expectedSalary , experience, портфолио } ; рендер ( данные ) ; } ) ; }
Хороший:
функция showEmployeeList ( сотрудники ) { сотрудники . forEach ( сотрудник => { сопз expectedSalary = сотрудника . calculateExpectedSalary ( ) ; Const опыт = работника . getExperience ( ) ; const data = { ожидаемая зарплата , опыт } ; переключатель ( сотрудник . тип ) { кейс "менеджер" : данные . портфель = сотрудник . getMBAProjects ( ) ; перерыв ; кейс «разработчик» : данные . githubLink = сотрудник . getGithubLink ( ) ; перерыв ; } рендер ( данные ) ; } ) ; }

Плохой:
const menuConfig = { заголовок : null , body : "Bar" , buttonText : null , cancellable : true } ; функция createMenu ( config ) { config . title = config . название || «Фу» ; config . body = config . тело || «Бар» ; config . buttonText = конфигурация . buttonText || «Баз» ; config . cancellable = config . отменяемый ! == undefined ? config . отменяемый :правда ; } createMenu ( menuConfig ) ;
Хороший:
const menuConfig = { title : "Order" , // Пользователь не включил ключ 'body' buttonText : "Отправить" , cancellable : true } ; function createMenu ( config ) { let finalConfig = Object . assign ( { title : "Foo" , body : "Bar" , buttonText : "Baz" , cancellable : true } , config ) ; return finalConfig // конфигурация теперь равна: {title: "Order", body: "Bar", buttonText: "Send", cancellable: true} // ... } createMenu ( menuConfig ) ;

Флаги сообщают вашему пользователю, что эта функция выполняет несколько функций. Функции должны делать одно. Разделите свои функции, если они следуют разным путям кода на основе логического.
Плохой:
функция createFile ( имя , темп ) { если ( темп ) { фс . создать ( `./temp/ $ { имя } ` ) ; } else { fs . создать ( имя ) ; } }
Хороший:
функция createFile ( имя ) { fs . создать ( имя ) ; } функция createTempFile ( имя ) { createFile ( `./temp/ $ { name } ` ) ; }

Функция производит побочный эффект, если она делает что-либо, кроме приема значения и возврата другого значения или значений. Побочным эффектом может быть запись в файл, изменение какой-либо глобальной переменной или случайная передача всех ваших денег незнакомому человеку.
Теперь вам действительно нужно иногда иметь побочные эффекты в программе. Как и в предыдущем примере, вам может потребоваться запись в файл. Что вы хотите сделать, так это централизовать то, где вы это делаете. У вас нет нескольких функций и классов, которые пишут в конкретный файл. Есть одна служба, которая это делает. Один-единственный.
Главное - избегать распространенных ошибок, таких как совместное использование состояния между объектами без какой-либо структуры, использование изменяемых типов данных, в которые может быть записано что угодно, и отсутствие централизации того, где возникают ваши побочные эффекты. Если вы сможете это сделать, вы будете более счастливы, чем подавляющее большинство других программистов.
Плохой:
// Глобальная переменная, на которую ссылается следующая функция. // Если бы у нас была
продолжение следует...
Часть 1 Идеальный код на PHP и Javascript
Часть 2 1. Комментарии и Документация - Идеальный код на PHP и
Часть 3 Объекты и структуры данных - Идеальный код на PHP и
Часть 4 Тестирование - Идеальный код на PHP и Javascript
Комментарии