Прагніть розбивати програми на менші компоненти
Однією з речей, про яку часто говорять програмісти, є бажання розбити програми на менші компоненти або функції, щоб полегшити їх відстеження, легше читати та легше налагоджувати.
Але це не така рідкість побачити монолітні функції з великою кількістю коментарів до коду, які допомагають пояснити, що відбувається в програмі.
Насправді я цього не роблю, бо не знаю обмежень, у яких працював програміст. Це:
- Який бюджет він/вона мав на створення програми?
- Скільки часу було відведено на виконання проекту?
- Чи багато людей працювало над проектом?
- Чи було програмісту надано час, щоб написати код, щоб вони могли його модульно протестувати, відрефакторити або просто зробити його легшим для читання?
Коротше кажучи, я вважаю, що є багато причин того, що ми можемо читати «поганий код», і це не завжди має бути помилка програміста (це найприродніша річ, яку ми маємо викинути, коли читаємо те, що нам не подобається).
Чи означає це, однак, що ми не повинні прагнути до рефакторингу або написання коду таким чином, щоб полегшити його розуміння? Звичайно, ні. Якщо припустити, що у нас є для цього час, як ми можемо це зробити?
Розбийте програми на менші компоненти
Коли справа доходить до написання на таку тему, особливо в такій активній економіці, як електронна комерція в WordPress, це може бути викликом.
«Давайте конкретизувати, Боб».
Тобто я можу написати про це на дуже детальному рівні, використовуючи набір плагінів, переглядаючи дані, аналізуючи запити та показуючи, як це зробити. Або я можу написати про це на трохи вищому рівні з кінцевою метою показати, як розбивати програми на менші компоненти.
Оскільки існує багато способів досягти першого, я обираю другий. Тобто це не обов’язково використовуватиме якісь конкретні плагіни – це прямі запити. Однак він використовуватиме приклади високого рівня, які допоможуть вам пройти через те, що може бути серією запитів і циклів, і розбити їх на менші функції.
Загальний приклад
Наприклад, припустімо, що я працюю над функцією плагіна WordPress, кінцевою метою якої є отримання всіх різноманітних методів оплати, які зберіг користувач і які пов’язані з його обліковим записом.
Проблема полягає в тому, що ця інформація розкидана по кількох таблицях бази даних (через різні плагіни, які використовуються), тому є деякі запити, які потрібно виконати, а потім отримати.
Кроки для створення таких запитів можуть виглядати приблизно так:
- отримати ідентифікатор клієнта поточного користувача,
- отримати всі ідентифікаційні номери замовлення для клієнта
- визначити, які методи оплати використовувалися для кожного замовлення
- отримати зазначені способи оплати, а потім надіслати інформацію клієнту
Залежно від того, як налаштовано базу даних, залежно від вашого рівня майстерності SQL і залежно від того, як різні плагіни для обробки всіх вищевказаних даних працюють у тандемі, може бути легко написати один великий запит для отримання цієї інформації.
Але якщо ви працювали з електронною комерцією в WordPress і різними плагінами, ви знаєте, що це не завжди так просто.
Натомість ви дивитесь на щось на кшталт:
- нам потрібно отримати профіль клієнта з метаданих користувача,
- нам потрібно знайти всі замовлення, зроблені користувачем, і це часто можна пов’язати з публікацією або таблицею метаданих публікації,
- способи оплати, ймовірно, можуть зберігатися в їхній таблиці, пов’язаній з користувачем через певний тип токена,
- наведений вище токен міститься в таблиці та пов’язаний із даною частиною інформації в іншій таблиці, з якої потім потрібно зробити висновок, переглянувши дані, які існують у всій базі даних.
Зрештою, ви повинні створити набір запитів, лише спочатку зрозумівши, як запитувати дані, які ви шукаєте. Це досить складно. Але коли ви це зробите, припустімо, що ви пишете свої запити послідовно, а потім використовуєте результати кожного, щоб отримати бажаний результат.
Це може призвести до чогось такого :
<?php
// First, read the user ID and meta value to get authorization information
global $wpdb;
$results = $wpdb->get_results(
$wpdb->prepare(
"
SELECT
user_id, meta_value
FROM $wpdb->usermeta
WHERE meta_key LIKE %s
AND user_id = %d
",
'%customer_profile_id%',
filter_input(INPUT_GET, 'customer_id')
),
ARRAY_A
);
$result = isset($results[0])? array_column($results[0], 'meta_value'): [];
if (empty($result)) {
return $result;
}
// Get the ID of the current customer.
$customers = $wpdb->get_results(
$wpdb->prepare(
"", // Your custom query goes here.
filter_input(INPUT_GET, 'customer_id')
),
ARRAY_A
);
$customer = isset($customers[0])? array_column($customers[0], 'customer_id'): [];
if (empty($customer)) {
return $customer;
}
// Get all of the order IDs from the set of orders returned from the previous query.
$orderIds = $wpdb->get_results(
$wpdb->prepare(
"", // Your custom query goes here.
$customer
),
ARRAY_A
);
return $orderIds;
// Finally, get all of the payment methods for the orders based on their Ids.
$orders = [];
foreach ($orderIds as $orderId) {
$results = $wpdb->get_results(
$wpdb->prepare(
"" // The query for retrieving various payment method information based on the $orderId
),
ARRAY_A
);
if (empty($results)) {
continue;
}
$orders[$orderId] = $results;
}
// Now send the information back to the user.
wp_send_json_success($orders);
Але це не повинно бути саме так.
По-перше, це всі незалежні запити з незалежними наборами результатів, навіть якщо їх потрібно використовувати в тандемі. Це означає, що ми можемо розділити їх на частини та оцінити результати кожного, перш ніж переходити до наступного кроку.
Крім того, це дозволяє нам писати менші, більш зв’язані функції. Незважаючи на те, що вони можуть залежати одна від одної, ми можемо налаштувати кожну функцію на прийняття аргументу (або набору аргументів, з якого ми можемо отримати всю інформацію).
Можливо, кінцевий результат може виглядати приблизно так :
<?php
public function getPaymentMethods()
{
$authInfo = $this->getAuthorizationInformation();
$currentCustomerId = $this->getCurrentCustomerId($authInfo);
$orders = $this->getCustomerOrders($currentCustomerId);
$paymentMethods = $this->getPaymentMethodsFromOrders($orders);
wp_send_json_success($orders);
}
private function getAuthorizationInformation()
{
global $wpdb;
$authInfo = $wpdb->get_results(
$wpdb->prepare(
"
SELECT
user_id, meta_value
FROM $wpdb->usermeta
WHERE meta_key LIKE %s
AND user_id = %d
",
'%customer_profile_id%',
filter_input(INPUT_GET, 'customer_id')
),
ARRAY_A
);
return isset($authInfo[0])? array_column($authInfo[0], 'meta_value'): [];
}
private function getCurrentCustomerIdFromAuthInfo($authInfo)
{
global $wpdb;
$customers = $wpdb->get_results(
$wpdb->prepare(
"", // Your custom query goes here.
$authInfo;
),
ARRAY_A
);
return isset($customerId[0])? array_column($customerId[0], 'meta_value'): [];
}
private function getCustomerOrders($customerId)
{
global $wpdb;
$orderIds = $wpdb->get_results(
$wpdb->prepare(
"", // Your custom query goes here.
$customerId
),
ARRAY_A
);
return empty($orderIds)? []: $orderIds;
}
private function getPaymentMethodsFromOrders($orderIds)
{
$paymentMethods = [];
foreach ($orderIds as $orderId) {
$results = $wpdb->get_results(
$wpdb->prepare(
"" // The query for retrieving various payment method information based on the $orderId
),
ARRAY_A
);
if (empty($results)) {
continue;
}
$paymentMethods[$orderId] = $results;
}
return $paymentMethods;
}
Звичайно, я не можу показати будь-який фактичний SQL – добре, принаймні не скрізь – тому що я не знаю загальних налаштувань і не знаю точно, з якими плагінами чи схемами ви працюєте.
Але це ніколи не було метою цієї публікації.
Натомість я намагаюся донести основну думку: навіть якщо ми працюємо в умовах дуже обмежених обмежень, ми все одно можемо розбивати програми на менші компоненти, які допомагають нам описати те, що відбувається, зрозуміти, як це робиться, а потім надсилати дані вперед і назад між різними функціями, а також до користувача та від нього.