Правильне розміщення PL / SQL, Інші СУБД, Бази даних, статті

Краща практика розміщення, розміщення і ще раз розміщення PL / SQL


Я пишу пакети та процедури як в Oracle Database, так і в Oracle Developer (Oracle Forms). Як мені вибрати, де розмістити код?


По іншому це питання звучить так: “В якому контексті повинна бути програма?” Тобто, з яких програм додатка вона може викликатися? Тільки з єдиною форми? Будь форми? З єдиною програми на сервері? З будь-якої схеми примірника БД?


З цих питань я приймаю рішення, керуючись принципом: “включаючи програму якомога ближче до того місця, де вона буде використовуватися (викликатися)”.


Демонстрація розміщення і переміщення


Для демонстрації різних можливих і відповідних способів визначення коду я в цій статті використовую наступні бізнес-умови:


Моя команда створює додаток для call-центру. Продавці продукту моєї компанії телефонують нам, коли у них виникають проблеми, а ми поміщаємо їх дзвінки в чергу, якщо вони не можуть бути оброблені відразу. І ось я повинен написати програму, яка розподіляє необроблені дзвінки по членам групи підтримки. Пакет, який буде містити всю логіку, називається call_manager. Процедура для розподілу необроблених дзвінків називається distribute_calls. Лістинг 1 показує специфікацію і тіло цієї програми.


Лістинг 1: процедура distribute_calls


PROCEDURE distribute_calls (
department_id_in IN departments.department_id%TYPE)
IS
BEGIN
WHILE ( calls_are_unhandled ( ) )
LOOP
FOR emp_rec IN emps_in_dept_cur (department_id_in)
LOOP
IF current_caseload (emp_rec.employee_id) <
avg_caseload_for_dept (department_id_in)
THEN
assign_next_open_call (emp_rec.employee_id);
END IF;
END LOOP;
END LOOP;
END distribute_calls;

Як бачите, цей виконуваний розділ легко читається: Поки є хоч би один необроблений виклик для кожного співробітника заданого відділу, перевіряємо, менше чи поточна завантаження середньої по відділу, і пов’язуємо черговий виклик з цим співробітником. Продовжуємо обробку, якщо залишилися інші необроблені виклики.


Виконуваний розділ викликає кілька підпрограм для виконання цієї роботи:



Один момент: жодна з цих програм ще не реалізована. Я використовую проектування верхнього рівня, яке називається також покроковим уточненням (refinement), щоб зосередитися на програмі з узагальненою, високорівневої логікою. Цей спосіб допомагає уникнути витрат на всякі дрібні подробиці.


Тепер можна перейти до наступного рівня деталізації і позначити місце, звідки повинні будуть викликатися ці підпрограми.


Як я вже говорив, моє правило таке: Опис підпрограми повинно бути якомога ближче до місця її використання. Якщо слідувати цьому правилу без будь-якого додаткового аналізу, мені слід було б описати кожну програму як локальну підпрограму в самій distribute _ calls, як показано на Лістингу 2 (трикрапкою […] показано місце реалізації цих підпрограм).


Лістинг 2: Чотири локальні підпрограми в distribute _ calls


PROCEDURE distribute_calls (
department_id_in IN departments.department_id%TYPE)
IS
FUNCTION calls_are_handled RETURN BOOLEAN
IS BEGIN … END calls_are_handled;

FUNCTION current_caseload (
employee_id_in IN employees.employee_id%TYPE)
RETURN PLS_INTEGER
IS BEGIN … END current_caseload;
FUNCTION avg_caseload_for_dept (
employee_id_in IN employees.employee_id%TYPE)
RETURN PLS_INTEGER
IS BEGIN … END current_caseload;

PROCEDURE assign_next_open_call (
employee_id_in IN employees.employee_id%TYPE)
IS BEGIN … END assign_next_open_call;
BEGIN


Процедури і функції, визначені безпосередньо в деклараційної секції будь-якого PL / SQL-блоку, називаються локальними, або вкладеними, підпрограмами. У цьому прикладі вони можуть викликатися тільки з процедури distribute _ calls, і це, звичайно ж, відповідає визначенню наібліжайшего їх використання.


Але, як тільки я це зробив, одразу ж піймав себе на думці про інші програми, які вже написані в цьому пакеті і про те, що можу захотіти використовувати деякі з цих підпрограм в програмах, які напишу в майбутньому.


Я згадав, наприклад, що на останньому тижні написав іншу функцію, яка дуже схожа на current _ caseload. Вона зараз “живе” в процедурі show _ caseload. Перш ніж реалізовувати двічі одну й ту ж логіку (а значить, необхідно налагоджувати і підтримувати код в обох місцях), краще витягти функцію current _ caseload і з distribute _ calls, і з show _ caseload.


Отже, після невеликої перегрупування коду, я закінчив тіло пакета, показане на Лістингу 3.


Лістинг 3: Переміщення функції current_caseload


CREATE OR REPLACE PACKAGE BODY call_manager
IS
FUNCTION current_caseload (
employee_id_in IN employees.employee_id%TYPE)
RETURN PLS_INTEGER
IS BEGIN … END current_caseload;
PROCEDURE show_caseload (
department_id_in IN departments.department_id%TYPE)
IS BEGIN … END show_caseload;
PROCEDURE distribute_calls (
department_id_in IN departments.department_id%TYPE
)
IS BEGIN … END distribute_calls;
END;
/

І ось я перемістив функцію current _ caseload далі від distribute _ calls, але зробив це, тому що вона використовується двома підпрограмами пакета. Тому тепер вона близька наскільки можливо обом її використанням. Проте я ще не розглянув і не бачу необхідності використання current _ caseload ззовні пакета distribute _ calls, тому я не розміщую заголовок current _ caseload в специфікацію пакета.


Тепер мою увагу переключилася на avg _ caseload _ for _ dept. Щось у цій програмі виглядає знайомим. Що ж це, що ж це? Так! Моя колега Сандра надіслала на останньому тижні лист по електронній поштою, що сповіщає всіх нас, що вона склала в один пакет call _ util кілька корисних утиліт, що включають функцію, яка повертає середню завантаження співробітника.


Я ляснув себе по лобі, відкопав лист, і знайшов, що ця функція називається dept _ avg _ caseload. Я перевірив, що call _ util у мене є, і – дивіться-но – функція call _ util. dept _ avg _ caseload взагалі вже прекрасно реалізована в ньому, і чекає, коли її будуть використовувати.


Тоді я повертаюся до процедури distribute _ calls, видаляю функцію avg _ caseload _ for _ dept і замінюю мою виконувану частину як показано на Лістингу 4.


Лістинг 4: Перероблена виконувана частина distribute _ calls


BEGIN
WHILE ( calls_are_unhandled ( ) )
LOOP
FOR emp_rec IN emps_in_dept_cur (department_id_in)
LOOP
IF current_caseload (emp_rec.employee_id) <
call_util.dept_avg_caseload (department_id_in)
THEN
assign_next_open_call (emp_rec.employee_id);
END IF;
END LOOP;
END LOOP;
END distribute_calls;

Тепер одна з процедур, які використовує в моїй процедурою, оголошена так далеко, що я не можу навіть контролювати її реалізацію, і можу навіть ніколи не побачити цю реалізацію. Чи є це проблемою ? Ні. Мені більше нічого не треба робити і турбуватися про неї!


Функція call _ util. dept _ avg _ caseload реалізована поза мого використання, але вона найбільш близька до всіх використанням з різних пакетів, і тому оголошена в специфікації пакета call _ utils .


Вау. Я думаю, що закінчив оптимізацію розташування визначень моїх підпрограм. Я залишився з двома локальними підпрограмами (calls _ are _ unhandled і assign _ next _ open _ call), однією програмою (Current _ caseload), визначеної безпосередньо на рівні пакета (без включення в специфікацію пакета), і ще однією функцією (call _ util. Dept _ avg _ caseload), яка написана кимось іншим і яка доступна всім схемами з правом на виконання пакета call _ util.


Я сподіваюся, що кроки, які я виконав, створюючи distribute _ calls, допоможуть вам приймати власні рішення в тому, де краще реалізувати ваші складні, багаторівневі програми.


Розміщення коду Oracle Developer


У цій статті приділялася увага тому, де і як описати код в Oracle Database, однак такі ж правила і логіка застосовні до Oracle Developer. Я пропоную робити так:



Якщо клієнтська програма дуже специфічна форма або звіт, опишіть її в цьому ж модулі.

Схожі статті:


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

Коментарів поки що немає.

Ваш отзыв

Поділ на параграфи відбувається автоматично, адреса електронної пошти ніколи не буде опублікований, допустимий HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

*