За последние два дня я прочитал две книги «Разгадка коллекций Laravel» и «Рефакторинг коллекций».
Узнали, как преобразовать элементы массива в коллекцию и почему.
Одна из основных идей: никогда больше не писать еще один цикл.
Ниже приводится краткое изложение полученных знаний с акцентом на коллекцию, используемую Laravel.
Зачем проводить рефакторинг
Я не хочу называть рефакторинг панацеей, но он может помочь вам сохранить хороший контроль над вашим кодом. Рефакторинг — это инструмент, который можно (и нужно) использовать для нескольких целей.
Рефакторинг для улучшения дизайна программного обеспечения
То же самое, плохо разработанные программы, как правило, требуют большего количества кода, часто потому, что код использует один и тот же оператор в разных местах для выполнения одной и той же задачи. Поэтому важным направлением улучшения дизайна является устранение дублирующегося кода. Важность этого действия заключается в облегчении будущих модификаций. Уменьшение количества кода не ускоряет работу системы, потому что почти не оказывает заметного влияния на траекторию программы. Однако уменьшенный объем кода значительно облегчит будущие модификации программы.
Рефакторинг для облегчения понимания программного обеспечения
Программирование во многом связано с общением с компьютером: вы пишете код, чтобы сказать компьютеру, что делать, и он отвечает, делая именно то, что вы ему говорите. Вы должны вовремя заполнить пробел между «что вы хотите, чтобы он сделал» и «скажите ему, что делать». Суть этой модели программирования заключается в том, чтобы «говорить именно то, что я хочу». Помимо компьютеров, у вашего исходного кода есть и другие читатели: через несколько месяцев может появиться другой программист, который попытается прочитать ваш код и внести некоторые изменения. Второго читателя легко забыть, но он самый важный. Имеет ли значение, если компьютеру требуется несколько часов для компиляции? Если программист тратит неделю на модификацию определенного фрагмента кода, это ад — если он понимает ваш код, модификация займет всего час.
Короче говоря, не позволяйте вашему коду быть в устах следующего получателя:мусорный код
Рефакторинг помогает найти ошибки
Понимание кода может помочь мне найти ошибки. Я признаю, что я не очень хорош в отладке. Некоторые люди могут просто посмотреть на кусок кода и найти в нем ошибки, но я не могу. Но я обнаружил, что если бы я рефакторил код, я мог бы получить глубокое понимание того, что делает код, и вернуть это новое понимание в самый раз. Когда я разбирался со структурой программы, я также знал некоторые сделанные предположения, поэтому было сложно не найти баги.
Это напоминает мне фразу, которую Кент Бек использовал для описания самого себя: «Я не великий программист, я просто хороший программист с некоторыми хорошими привычками».
Рефакторинг повышает скорость программирования
Я абсолютно уверен, что хороший дизайн — это основа быстрой разработки — на самом деле, хороший дизайн делает возможной быструю разработку. Без хорошего дизайна вы можете какое-то время быстро прогрессировать, но плохой дизайн очень быстро вас замедлит. Вы потратите время на отладку и не сможете добавлять новые функции. Модификации занимают все больше и больше времени, потому что вам приходится тратить все больше и больше времени на понимание системы и поиск повторяющегося кода. По мере того, как вы устанавливаете патч за патчем в исходную программу, для реализации новых функций требуется много кода. Какой порочный круг.
Хороший дизайн имеет основополагающее значение для поддержания скорости разработки программного обеспечения. Рефакторинг может помочь вам быстрее разрабатывать программное обеспечение, потому что он предотвращает повреждение системы и может даже улучшить качество вашего дизайна.
Я считаю, что именно поэтому многие отличные фреймворки могут быть признаны и использоваться многими людьми, потому что их фреймворки могут улучшить нашу скорость программирования, иначе зачем нам их использовать? Среди них Laravel является одним из представителей.
Вышеизложенное в основном выдержки из «Рефакторинг — улучшение дизайна существующего кода», я рекомендую всем прочитать эту книгу.
Рефакторинг для коллекции Три элемента
Основываясь на принципе рефакторинга «Никогда больше не писать еще один цикл», нам нужно найти «оператор цикла», который наиболее часто используется в массиве, инкапсулировать его, затем создать различные общие функции более высокого порядка и, наконец, сформировать класс Collection. . Наконец, когда мы используем массив, пока мы конвертируем его в объект Collection, мы никогда не сможем снова написать еще один цикл, насколько это возможно.
оператор цикла
При работе с элементами массива мы не можем избежать использования операторов цикла для обработки нашей логики.
Например, мы хотим получить адреса электронной почты всех пользователей, возможно, мы напишем:
function getUserEmails($users) {
// 1. 创建空数组用于保存结果
$emails = [];
// 2. 初始化变量 $i,用于遍历所有用户
for ($i = 0; $i < count($users); $i++) {
$emails[] = ?users[$i]->email;
}
return $emails;
}
В другом примере мы хотим вычислить *3 для каждого элемента массива:
function cheng3($data) {
for ($i = 0; $i < count($data); $i++) {
$data[$i] *= 3;
}
}
В другом примере мы хотим выбрать дорогие продукты:
function expensive($products) {
$expensiveProducts = [];
foreach ($products as $product) {
if ($product->price > 100) {
$expensiveProducts[] = $product;
}
}
return $expensiveProducts;
}
Примеров такого рода операций над массивами слишком много, ведь все они оперируют каждым элементом массива в цикле.
Идея нашей реконструкции состоит в том, чтобы инкапсулировать цикл, чтобы мы могли избежать самостоятельного написания операторов цикла при написании бизнес-логики (К черту оператор цикла).
Higher Order Functions
Общее название: функция высшего порядка Функция высшего порядка — это функция, которая принимает другую функцию в качестве параметра, возвращает функцию или выполняет и то, и другое.
Четыре приведенных выше примера изменены с использованием функций высшего порядка.
В первом примере основная бизнес-логика заключается в этом операторе, который получает почтовый ящик каждого пользователя:
$emails[] = ?users[$i]->email;
Инкапсулируйте другой код в следующую функцию карты:
function map($items, $func) {
$results = [];
foreach ($items as $item) {
$results[] = $func($item);
}
return $results;
}
Тогда рефакторинг с использованием функции карты будет таким же простым, как:
function getUserEmails($users) {
return $this->map($users, function ($user) {
return $user->email;
});
}
Очевидно, что по сравнению с первоначальным методом написания он намного проще, а также позволяет избежать ненужных переменных.
Точно так же отрефакторите второй пример, чтобы обернуть оператор цикла в каждую функцию:
function each($items, $func) {
foreach ($items as $item) {
$func($item);
}
}
Самая большая разница между функциями each и map заключается в том, что функция each обрабатывает логику для каждого элемента и не возвращает новый массив.
Использование каждой функции проще:
function cube($data) {
$this->each($data, function ($item) {
return $item * 3;
});
}
Таким же образом реконструируется третий пример, и объектом реконструкции является скрининговое суждение о цене.
if ($product->price > 100) {
$expensiveProducts[] = $product;
}
Мы обращаемся к функции карты для рефакторинга:
function filter($items, $func) {
$result = [];
foreach ($items as $item) {
if ($func($item)) {
$result[] = $item;
}
}
return $result;
}
когда удовлетворен$func($item)
Элементы условия помещаются в массив $result.
Его легко использовать:
return $this->filter($products, function ($product) {
return $product->price > 100;
});
Разница между функцией фильтра и функцией карты здесь заключается в том, что функция карты получает набор атрибутов, соответствующий исходному массиву или новому массиву, сгенерированному вычислением, в то время как функция фильтра больше представляет собой массив, сформированный путем фильтрации подходящих элементов.
Создайте класс коллекции
Мы объединяем эти методы map, each и filter, чтобы сформировать класс Collection.
A collection is an object that bundles up an array and lets us perform array operations by calling methods on the collection instead of passing the array into functions.
где items является единственным свойством. Суть в том, чтобы перемещаться по элементам и выполнять различные операции. Подробности смотрите в коде:
class Collection {
protected $items;
public function __construct($items) {
$this->items = $items;
}
function map($items, $func) {
$results = [];
foreach ($items as $item) {
$results[] = $func($item);
}
return $results;
}
function each($items, $func) {
foreach ($items as $item) {
$func($item);
}
}
function filter($items, $func) {
$result = [];
foreach ($items as $item) {
if ($func($item)) {
$result[] = $item;
}
}
return $result;
}
public function toArray() {
return $this->items;
}
}
Конечно, пока что прототип сборника, упакованный сам по себе, уже существует, но он еще не может достичь того уровня, который можно было бы использовать повсеместно. Так что нам нужно посмотреть, как это пишут другие, конечно, в это время мы должны сделать большой шаг — использовать Laravel.
Illuminate\Support\Collection
Объясните Illuminate\Support\Collection.
The Illuminate\Support\Collection class provides a fluent, convenient wrapper for working with arrays of data.
Коллекция в основном реализует следующие интерфейсы:
- ArrayAccess
- Countable
- IteratorAggregate
- JsonSerializable and Laravel's own Arrayable and Jsonable
Позвольте мне объяснить функции этих интерфейсов один за другим.
ArrayAccess
interface ArrayAccess {
public function offsetExists($offset);
public function offsetGet($offset);
public function offsetSet($offset, $value);
public function offsetUnset($offset);
}
Реализуйте эти четыре функции:
/**
* Determine if an item exists at an offset.
*
* @param mixed $key
* @return bool
*/
public function offsetExists($key)
{
return array_key_exists($key, $this->items);
}
/**
* Get an item at a given offset.
*
* @param mixed $key
* @return mixed
*/
public function offsetGet($key)
{
return $this->items[$key];
}
/**
* Set the item at a given offset.
*
* @param mixed $key
* @param mixed $value
* @return void
*/
public function offsetSet($key, $value)
{
if (is_null($key)) {
$this->items[] = $value;
} else {
$this->items[$key] = $value;
}
}
/**
* Unset the item at a given offset.
*
* @param string $key
* @return void
*/
public function offsetUnset($key)
{
unset($this->items[$key]);
}
В большей степени этот интерфейс отвечает за то, чтобы сделать класс Collection похожим на массив, главным образом для добавления и удаления элементов и определения того, существует ли элемент.
Countable
interface Countable {
/**
* Count elements of an object
* @link http://php.net/manual/en/countable.count.php
* @return int The custom count as an integer.
* </p>
* <p>
* The return value is cast to an integer.
* @since 5.1.0
*/
public function count();
}
Выполнение:
/**
* Count the number of items in the collection.
*
* @return int
*/
public function count()
{
return count($this->items);
}
Метод count() широко используется, а в PHP массивы специально не реализуют этот интерфейс, и мы в основном не видим ничего подобного.array->count()
из.
IteratorAggregate
Обычно известен как интерфейс "агрегатного итератора".
/**
* Interface to create an external Iterator.
* @link http://php.net/manual/en/class.iteratoraggregate.php
*/
interface IteratorAggregate extends Traversable {
/**
* Retrieve an external iterator
* @link http://php.net/manual/en/iteratoraggregate.getiterator.php
* @return Traversable An instance of an object implementing <b>Iterator</b> or
* <b>Traversable</b>
* @since 5.0.0
*/
public function getIterator();
}
Реализация также проста, просто создайте экземпляр ArrayIterator:
/**
* Get an iterator for the items.
*
* @return \ArrayIterator
*/
public function getIterator()
{
return new ArrayIterator($this->items);
}
Описание ArrayIterator смотрите здесь:PHP.go потяните Ravel.com/class.array…
Arrayable
interface Arrayable
{
/**
* Get the instance as an array.
*
* @return array
*/
public function toArray();
}
Конкретная реализация, вывод массива:
/**
* Get the collection of items as a plain array.
*
* @return array
*/
public function toArray()
{
return array_map(function ($value) {
return $value instanceof Arrayable ? $value->toArray() : $value;
}, $this->items);
}
array_map — применить функцию обратного вызова к каждому элементу массива
Jsonable + JsonSerializable
interface Jsonable
{
/**
* Convert the object to its JSON representation.
*
* @param int $options
* @return string
*/
public function toJson($options = 0);
}
Конкретная реализация конвертируется в формат JSON, этот метод используется чаще:
/**
* Convert the object into something JSON serializable.
*
* @return array
*/
public function jsonSerialize()
{
return array_map(function ($value) {
if ($value instanceof JsonSerializable) {
return $value->jsonSerialize();
} elseif ($value instanceof Jsonable) {
return json_decode($value->toJson(), true);
} elseif ($value instanceof Arrayable) {
return $value->toArray();
}
return $value;
}, $this->items);
}
/**
* Get the collection of items as JSON.
*
* @param int $options
* @return string
*/
public function toJson($options = 0)
{
return json_encode($this->jsonSerialize(), $options);
}
другие функции
tap()Обнаружено, что в классе Collection есть функция касания:
/**
* Pass the collection to the given callback and then return it.
*
* @param callable $callback
* @return $this
*/
public function tap(callable $callback)
{
$callback(new static($this->items));
return $this;
}
Про использование тапа вы можете посмотреть в предыдущей статьецепное программирование
Для получения дополнительных функций см.:
Конечно, если вам недостаточно этих традиционных методов, вы также можете расширить класс Collection с помощью метода Collection::macro:
use Illuminate\Support\Str;
Collection::macro('toUpper', function () {
return $this->map(function ($value) {
return Str::upper($value);
});
});
$collection = collect(['first', 'second']);
$upper = $collection->toUpper();
// ['FIRST', 'SECOND']
Для конкретной реализации см. Macroable:
trait Macroable
{
/**
* The registered string macros.
*
* @var array
*/
protected static $macros = [];
/**
* Register a custom macro.
*
* @param string $name
* @param object|callable $macro
*
* @return void
*/
public static function macro($name, $macro)
{
static::$macros[$name] = $macro;
}
/**
* Mix another object into the class.
*
* @param object $mixin
* @return void
*/
public static function mixin($mixin)
{
$methods = (new ReflectionClass($mixin))->getMethods(
ReflectionMethod::IS_PUBLIC | ReflectionMethod::IS_PROTECTED
);
foreach ($methods as $method) {
$method->setAccessible(true);
static::macro($method->name, $method->invoke($mixin));
}
}
/**
* Checks if macro is registered.
*
* @param string $name
* @return bool
*/
public static function hasMacro($name)
{
return isset(static::$macros[$name]);
}
/**
* Dynamically handle calls to the class.
*
* @param string $method
* @param array $parameters
* @return mixed
*
* @throws \BadMethodCallException
*/
public static function __callStatic($method, $parameters)
{
if (! static::hasMacro($method)) {
throw new BadMethodCallException("Method {$method} does not exist.");
}
if (static::$macros[$method] instanceof Closure) {
return call_user_func_array(Closure::bind(static::$macros[$method], null, static::class), $parameters);
}
return call_user_func_array(static::$macros[$method], $parameters);
}
/**
* Dynamically handle calls to the class.
*
* @param string $method
* @param array $parameters
* @return mixed
*
* @throws \BadMethodCallException
*/
public function __call($method, $parameters)
{
if (! static::hasMacro($method)) {
throw new BadMethodCallException("Method {$method} does not exist.");
}
$macro = static::$macros[$method];
if ($macro instanceof Closure) {
return call_user_func_array($macro->bindTo($this, static::class), $parameters);
}
return call_user_func_array($macro, $parameters);
}
}
Суммировать
Из этого класса Collection мы можем увидеть намерение Laravel и почему мы можем изящно использовать фреймворк Laravel.
Пока речь идет об операции и использовании массива, мы рекомендуем преобразовать его в collect($items) — объект Collection, который может легко работать с массивом.
Далее мы изучим использование Eloquent: Collections с Collection в качестве базового класса.
Ссылаться на
1.Коллекции:docs.go потяните Ravel.com/docs/5.6/co…
2. Never write another loop again. Адамаватан Хан Что/рефакторинг…
3."коллекции laravel разгаданы"
4.«Рефакторинг — улучшение дизайна существующего кода»
"Продолжение следует"
Добавьте личный WeChat, попробуйте Laravel вместе