Універсальний інтерфейс: політ нормальний

Кілька років тому нас, групу розробників АСР "Fastcom", спіткало глибоке розчарування: корпорація Oracle відмовилася від еволюційного розвитку Forms. Наша ж білінгова система, що зародилася ще в минулому столітті, цілком і повністю розроблялася на Oracle Designer + Oracle Developer – технологіях, які здавалися вічними … Вартість переходу на іншу платформу була порівнянна з річним бюджетом нашого проекту. На щастя, ми щось подібне передбачали, і ще в 2001 році було поставлено завдання: розробити принципи універсального інтерфейсу, який би не залежав від засобів його відображення. На перший погляд ідея здається божевільною, але минулі 7 років дозволяють підвести деякі підсумки роботи в даному напрямку, якими я постараюся поділитися.

Що це таке


В основу універсального інтерфейсу, розробленого нами, покладені такі принципи:


1. Розробник прикладної задачі маніпулює тільки загальними для будь-якого засобу відображення поняттями, не опускаючись до конкретики, властивої конкретному засобу відображення інтерфейсу, так званому "Движку".


2. Розробник прикладної задачі ніколи заздалегідь не знає, хто і як буде інтерпретувати задані їм властивості елементів інтерфейсу


3. Розробник прикладної задачі, створюючи інтерфейс до неї, не використовує процедурні мови, не оперує обробники подій, зате він має інструмент "Динамічних властивостей"


4. Розробник конкретного движка для відображення елементів інтерфейсу використовує всі можливості даного йому інструменту для адекватної інтерпретації властивостей об'єктів, зазначених розробником прикладної завдання.


Тепер постараюся докладніше прокоментувати вищевикладені принципи.


Що таке движок? Це засіб (середа + мова) відображення інтерфейсних елементів, за допомогою яких користувач переглядає і модифікує дані з СУБД. Найперший наш движок був написаний на Oracle Forms, хоча це самий невідповідний засіб для даної задачі. Движок може бути реалізований на C + +, Delphi, Java, JavaScript і навіть у вигляді плагіна до FAR.


Що представляють собою "поняття", якими маніпулює Розробник інтерфейсу? Це елементи і їх властивості, які по-різному можуть бути реалізовані в різних движках, в залежності від можливостей конкретного движка. Наприклад, не варто заводити таку властивість елемента, як "Колір шрифту", адже різні засоби відображення можуть мати різні поняття про колір. Одні можуть видати 24-х бітну палітру, а інші працюють у монохромному, чорно-зеленому інтерфейсі. Або інший приклад. Не можна явно вказувати тип інтерфейсного елементу, такий як ListBox, RadioGroup, або ListOfValues. Різні засоби можуть мати немислимі обмеження. Ми повинні мати можливість задати список можливих (чи типових) значень. А далі вже нехай сам движок, вірніше його автор, викручується, щоб адекватно інтерпретувати властивості, задані розробником прикладної задачі.


Висновок із другого пункту такий: розробник прикладної системи не зобов'язаний знати середовище й мову засоби відображення інтерфейсу. І це дуже добре! Відносини між розробником движка і інтерфейсу формалізовані на рівні опису властивостей елементів інтерфейсу, а далі кожен займається суто своєю справою. Розробник прикладної системи, спеціаліст в предметній області, не витрачає час на розміщення інтерфейсних елементів на екрані, а розробник движка може бути повним профаном у конкретній предметній області, для якої, власне, розробляється прикладна система.


Раз розробник інтерфейсу не підозрює про те, хто і як буде інтерпретувати плоди його праці, то він не може оперувати термінами "обробників подій", адже заздалегідь не відомо – які події можуть оброблятися тим чи іншим двигуном. Але навіть якщо б не це, розробнику інтерфейсу все одно неминуче довелося б писати логіку мовою засоби відображення інтерфейсу, що суперечить нашим вимогам. Однак, світ гнучкий і його елементи взаємопов'язані. Як же реалізувати цей взаємозв'язок? Ось тут-то нам і допоможуть "динамічні властивості", тобто властивості, значення яких можуть задаватися не тільки константою, а й структурою, описуваної декларативною мовою, значення якої може залежати від значень будь-яких властивостей будь-яких інтерфейсних елементів. Динамічне властивість Переобчислювати в той момент, коли змінюється хоча б одне значення властивості, на яке посилається даний динамічне властивість. Наприклад, в нашій реалізації як мови динамічних властивостей використовується SQL.


Коли це корисно


Тепер, коли викладені основні принципи універсального інтерфейсу, саме час відповісти на запитання: "А навіщо це все потрібно?".


Більшість замовних проектів розвивається екстенсивним шляхом: обстеження => технічне завдання => реалізація. Потім нові вимоги => технічне завдання => реалізація. Знову "божевільні" вимоги замовника => технічне завдання => реалізація. Потім виправлення невірних рішень замовника за допомогою нового ТЗ => реалізація. Тут головне – добре технічне завдання. Потім група програмістів, використовуючи найбільш підходящий інструмент, реалізує ТЗ аж до останньої примхи замовника. Не встигаємо? Набираємо студентів, 2 тижні на навчання, ТЗ в руки – і вони працюють на благо проекту.


Тепер уявімо, що замовників декілька, і не 2 – 3, а десятки, а може бути й сотні. Можливо, бізнес замовників схожий, але не настільки, щоб один раз створити проект, а потім інсталювати його всім, як Windows. У цих умовах немислимо тримати окрему групу розробників – програмістів на кожного замовника. Неминуче виникає думка про тиражному проекті.


Що ж таке "Тиражний проект" з точки зору інтерфейсу? Численні налаштування – глобальні та локальні параметри, довідники, конфігурації можуть до невпізнання вплинути на вигляд однієї і тієї ж форми у різних замовників. Простий приклад: включення мультивалютності в системі призведе як мінімум до появи додаткового стовпця (у національній валюті) в кожній формі, де є поле або стовпець з ціною або вартістю.


Чим більше замовників, тим форми стають все більш інтелектуальними. Бізнес-вимоги одних споживачів не прийнятні для інших? Тоді ми збільшуємо кількість параметрів і розширюємо довідники … Замовників вже стільки, що ми не встигаємо заводити нові сутності? І ми випускаємо інструмент для створення і опису бізнес – логіки нових сутностей. І так далі … Бідні інтерфейсні форми!


Куди рухатися


Формалізуємо наші вимоги до інтерфейсу:


а). Інтерфейсні форми повинні змінюватися залежно від значення глобальних і локальних параметрів, кількісного та якісного наповнення довідників, а також від властивостей відображуваних ними примірників сутностей.


б). Зміна кон'юнктури ринку засобів розробки, а також віяння моди на інтерфейс серед споживачів не повинні бути загрозою проекту.


в). Програміст – розробник не повинен знати всі засоби розробки, за допомогою яких будуть реалізовуватися інтерфейси користувачів.


г). Впровадження нового движка має займати не більше півроку. Він повинен вміти відображати всі форми, розроблені до його появи. Форми, розроблені після впровадження нового движка, повинні правильно працювати на старих засобах відображення інтерфейсу (двостороння сумісність).


Перечитайте початок статті, де описані принципи універсального інтерфейсу. Він повністю задовольняє всім перерахованим вимогам.


Як це працює


Уявіть собі HTML-сторінку. Вона має деревно-ієрархічну структуру. Кожен HTML-елемент має одного батька і може мати безліч нащадків. Навіть якщо WEB-сторінка динамічна, все одно нові елементи породжуються від одного батька. У принципі, таку ж структуру може мати будь-який інтерфейс, тільки інтерфейсні елементи треба віднести до абстрактних типів, наділивши їх тими чи іншими ролями через їх самі властивості.


Приклад: Елементи типу "Пункт меню" мають природну ієрархію. При навігації на листовий Пункт меню в деякій певній галузі відображаються елементи типу "Список". Кожен список у підпорядкуванні має Рядки і Дії (такі, наприклад, як "Додати", "Пошук" і т.п.). Елементи типу "Рядок" складаються з "Полів" і теж можуть мати Дії (такі як "Змінити", "Видалити"), а при навігації по рядках може відображатися інший Список (або Списки), реалізуючи концепцію "Майстер – Деталь".


Це тільки маленький шматочок всієї ідеї, наведений для прикладу. Адже є ще Бланки, Поля введення даних, Локальні меню зі своїми Пунктами і, нарешті, Групи, що поєднують елементи різних типів за інтересами.


Вся ієрархія об'єктів описується деревоподібної структурою Протоелементов. Чому "прото …"? Тому що кожен протоелемент може породжувати від 0 до N елементів інтерфейсу одного типу. Протоелементу зіставлений SQL-запит. Кількість рядків, що повертається цим запитом, визначає число породжуваних інтерфейсних елементів. SQL-запит може спиратися на параметри, передані з вищого рівня, і він же породжує свої параметри або перевизначає значення батьківських параметрів (якщо їх імена збігаються). Параметри з будь-якого рівня можуть використовуватися в динамічних властивостях інтерфейсних елементів.


Для прикладу, розглянемо ієрархію Протоелементов, визначальну деревоподібну структуру об'єктів словника Oracle:


Select USERNAME, UserName PROMPT from All_Users order by 1


Select Max ("Таблиці") PROMPT from All_Tables where owner= “{USERNAME}”


Select TABLE_NAME, Table_Name PROMPT from All_Tables where owner= “{USERNAME}” order by 1


Select "Колонки" PROMPT from dual


Select COLUMN_NAME, Column_Name PROMPT, DATA_TYPE from All_Tab_Columns
where owner= “{USERNAME}” and Table_Name= “{TABLE_NAME}” order by Column_ID


Select "Первинний ключ" PROMPT from dual


Select CONSTRAINT_NAME, Constraint_Name PROMPT from All_Constraints
where Owner= “{USERNAME}” and Table_Name= “{TABLE_NAME}” and Constraint_Type= “P”


Select Column_Name PROMPT from All_Cons_Columns where Owner= “{USERNAME}”
and Constraint_Name = “{CONSTRAINT_NAME}” order by Position



Select "Зовнішні ключі" PROMPT from dual


Select C1.CONSTRAINT_NAME, C1.Constraint_Name / / "(" / / C2.TABLE_NAME / / ")" PROMPT
from All_Constraints C1, All_Constraints C2 where C1.Owner= “{USERNAME}”
and C1.Table_Name= “{TABLE_NAME}” and C1.Constraint_Type= “R”
and C1.R_CONSTRAINT_NAME=C2.CONSTRAINT_NAME and C1.R_OWNER=C2.OWNER


Select Column_Name PROMPT from All_Cons_Columns where Owner= “{USERNAME}”
and Constraint_Name = “{CONSTRAINT_NAME}” order by Position



Select Max ("Пакети") PROMPT from All_Objects where owner= “{USERNAME}” and Object_Type= “PACKAGE”


Select Object_Name PACKAGE , Object_Name PROMPT from All_Objects
where owner= “{USERNAME}” and Object_Type= “PACKAGE” order by 1





Рис. 1


Якщо параметр PROMPT використовувати в якості імені вузла ієрархії, то вийде структура, зображена на Мал.2


 На цьому наочному прикладі, зрозумілою кожному ораклісту, проілюстрований процес опису об'єктів інтерфейсу, що визначає їх суть, кількість і відношення один з одним. Однак цього недостатньо. Треба ще вміти визначати якісні характеристики інтерфейсних об'єктів, такі як: ролі, виділення, групування, поведінка, способи взаємодії зі світом і один з одним і т.п. Все це визначається властивостями, які свої для кожного типу об'єктів інтерфейсу. Саме їх інтерпретує кожен движок в міру своїх можливостей.


Ці властивості можуть містити динамічні фрагменти, які являють собою або посилання на контекстні параметри, або посилання на інші властивості (наприклад, поточні значення полів вводу), або фрагменти коду на SQL або PLSQL. Динамічні фрагменти можуть бути вкладені одна в одну. Наприклад, SQL-текст може містити в собі посилання на інформацію та / або значення полів. У складних випадках результат одного SQL-запиту може використовуватися в іншому і навіть не забороняється, щоб результат SQL-запиту представляв собою новий динамічний фрагмент.


У найпростішому варіанті, властивість цілком може складатися з динамічного елемента. Такий випадок ми вже зустрічали – значення властивості "Назва пункту меню" цілком складається з посилання на контекстний параметр PROMPT .


Подумаємо, яке ще властивість, крім назви, може бути у пункту меню. Перше, що спадає на думку, – ім'я іконки, якої може позначатися кожен пункт меню. Проте прямо так називати властивість не варто, так як наш інтерфейс, можливо, будуть інтерпретувати Пакети, які не зможуть відображати іконки. Іконки, по суті, є засобом рольової розмальовки пунктів меню. Ось саме так і варто називати це властивість: "Роль пункту меню". Далі, в просунутих движках кожної ролі може бути порівняна ілюструє її іконка.


Ще одна корисна властивість – групування пунктів всередині одного рівня. У нашому прикладі ми могли б не робити окремі гілки меню для різних типів ключів таблиці, а відобразити їх на одному рівні, відсортувати та згрупувавши за типами: первинний ключ, унікальні ключі, зовнішні ключі. Знову ж таки, різні движки можуть по-різному інтерпретувати властивість "Група". Одні відокремлювати групи пунктів один від одного полуторним інтервалом, інші – виділяти різні групи кольором. Але в будь-якому випадку, це – не турбота розробника бізнес-логіки. Його справа – згрупувати елементи. А як угруповання буде реалізована – навіть думати йому не варто.


Може виникнути природне запитання: а де ж такі властивості пунктів меню, як "Рівень вкладеності", "Розкрита" / "Закритий", "Листовий" / "Проміжний", "Активний" / "Пасивний" і т.п.? Деякі з цих властивостей є вторинними, а інші – технологічними для кожного конкретного движка. Саме те, що розробник прикладної системи не вдається в ці тонкощі, є одним з головних переваг універсального інтерфейсу.


Кому це треба


Зауважу, що мені весь час доводиться долати здивування нових членів нашої команди, які вперше стикаються з універсальним інтерфейсом. Уважно вислухавши лекцію, дуже схожу на все вищевикладене, вони, трохи подумавши, говорять до смішного дуже схожі слова: "Навіщо так складно? Чому не можна взяти і швидко написати потрібні форми, використовуючи такий чудовий інструмент, як JDeveloper / NetBeans / Eclipse / APEX / BEA WorkShop … (потрібне підкреслити) ".


Це питання видає в людині розробника замовних прикладних Систем. Можливо, що в подібних проектах, дійсно, не варто зв'язуватися з універсальним інтерфейсом, ламаючи через коліно стереотипи розробників. Але в чому я тепер абсолютно впевнений: у великих, довгострокових тиражованих проектах гра коштує свічок! Все навколо зміниться, і ваша Система не буде виглядати динозавром на тлі молодих і зубастих конкурентів.


У чому небезпека


Не варто впадати і в іншу крайність: далеко не всі проекти доцільно розробляти з використанням універсального інтерфейсу. Як відомо – за будь-яку універсальність треба платити. Купуючи простоту, легкість і швидкість в одному, ми змушені поступатися деякими приватними можливостями, йти на обмеження … Наведу далеко неповний перелік випадків, коли я б не рекомендував використання універсального інтерфейсу:



Що далі


Отже, припустимо, у вас вже реалізований універсальний інтерфейс. Що ще можна оптимізувати? Дам кілька порад.



  1. Можна створювати типові Списки і Бланки, іншими словами "універсальні форми універсального інтерфейсу". Це ще більш високий рівень абстракції. Такі інтерфейсні модулі будуть типовим чином обслуговувати типові ситуації. Приклад: Список і Бланки для відображення і редагування довідкової таблиці з Кодом, Найменуванням і Приміткою. Єдиним вхідним параметром такого Списку буде ім'я таблиці, яка містить довідник.
  2. Постарайтеся уніфікувати ролі стовпців таблиць при проектуванні. У цьому випадку можна буде автоматично підключати додаткові сервісні функції, обробні пов'язані з ролями стовпців значення їх властивостей. Приклади:

    1. CODE: Стовпець, що містить код запису, який є первинним ключем. Як привило, використовується в довідниках. Типові властивості: обов'язковий до заповнення, не довше N символів, не може бути змінений, перетвориться до верхнього регістру, містить тільки букви латинського алфавіту, цифри і знак підкреслення.
    2. NAME: найменування чогось. Типові властивості: Текстовий рядок, обов'язкове для заповнення, не довше M символів, може мати багатомовні варіації.
    3. HIST _ FROM: Дата / час, обов'язкове для заповнення, може викликати типової календар для редагування значення.
    4. UID: Унікальний ідентифікатор. Не може редагуватись. При вставці заповнюється автоматично функцією Sys _ Guid ()

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


  3. Такі ж ролі можна надавати й інтерфейсним елементам. Якщо код дії вказується рівним "OK", то воно має властивість дії за замовчуванням, призначається напис (підпис) "Зберегти" та коментар "Запам'ятати зміни і вийти", має характерну піктограму. Якщо код рядка вказується рівним TOTAL, то вони додається в кінець (на даному рівні), виділяється роллю, забезпечується дією "Перевичісліть" ну і т.д. Знову ж таки, будь-яку властивість будь-якої миті можна змінити, але зазвичай, при грамотній попередньому налаштуванні, розробника все влаштовує.

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


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

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

Ваш отзыв

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

*

*