Концепція каркасно-компонентного програмування

Матеріал з Вікіпедії — вільної енциклопедії.
Перейти до навігації Перейти до пошуку

Концепція каркасно-компонентного програмування (К3П) — це певний погляд на набір засобів для створення каркасу проєкту в цілому та розробки каркасів окремих компонентів, наповнення таких каркасів необхідним функціоналом та їх впровадження до самого проєкту чи інших компонентів.

Основна термінологія[ред. | ред. код]

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

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

К3П передбачає можливість створення каркасу проєкту на етапі аналізу предметної області і відповідних технічних завдань, поділ між розробниками ділянок роботи (у разі потреби, із використанням принципів мережевого планування). У найбільш загальному випадку допускається розробка у повному обсязі каркасу проєкту та лише після цього наповнення його предметним змістом. З іншого боку К3П цілком допускає подальше масштабування та редагування проєкту з мінімальною витратою часу на аналіз вже виконаних робіт за рахунок строго визначенмх принципів його побудови.

Клас — структура опису даних та функціоналу їх опрацювання згідно концепції ООП.

Об'єкт — змінна типу даних «клас» в концепції ООП.

Функціонал - набір функцій (методів) класу.

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

Твердження. Будь-який компонент є об'єктом, але не будь-який об'єкт є компонентом.

Каркас кодів — набір певним чином структурованих каталогів та файлів у цих каталогах.

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

СЕ — структурний елемент або елемент структури на певній схемі, що описує загальні принципи функціонування компоненту.

Базовий компонент — компонент, що не містить у своїй структурі інших компонентів.

Асоціаційний компонент — компонент, що містить у своїй структурі інші компоненти (композиція) або зв'язки з іншими компонентами (агрегація).

Із вище зазначеного можна зробити висновок, що проєкт є блоком кодів та асоціаційним компонентом, компонент є міні-проєктом.

Для чого потрібна К3П?[ред. | ред. код]

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

З цієї причини з'явилась концепція ООП, паттерни програмування, парадигма MVC, бібліотеки шаблонів, фреймворки та інше.

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

Як реалізувати К3П?[ред. | ред. код]

У цілому, кожен може самостійно розвинути подібну ідею для власної практичної діяльності та у залежності від того, з чим розробник працює (Java, Pyton, C++ і т. д.).

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

Друге — створення шаблонів, що моделюють елементи попередньо розробленої структури. Розроблені шаблони цілком допускається редагувати у майбутньому, але з врахуванням принципу «не нашкодь!», тобто внесені зміни не повинні впливати на вже працюючі раніше створені компоненти.

Третє — створення засобу для автоматичного генерування каркасу компонента.

Подальший матеріал стосовно реалізації вищеперерахованих кроків буде демонструватися на прикладах з використанням JavaScript.

MVC та К3П[ред. | ред. код]

Перш ніж перейти до JavaScript коротко згадаємо загальновідомий архітектурний шаблон MVC.

Особисто автору даної статті у схемі «Model View Controller» завжди чогось не вистачало… Виникає думка, що не вистачає такої компоненти, як події (Event). Можна зазначити, що Event це частина View або частина іншого СЕ. Але якщо це все ж частина, то чому б цю частину не винести окремо?

Загальні уявлення про MMVCE[ред. | ред. код]

Вище було згадано про події, але з'явилося певне подвоєне «MM» в абревіатурі «MMVCE». Перша «M» — це «Main», ще один СЕ, який є точкою запуску та стикування компонента.

Нижче наведено схему MMVCE.

Загальна схема компонента

У цілому, СЕ схеми майже такі, як на типових схемах MVC.

Але є певні зауваження. Про них йде мова далі.

Коротко про головне і загально відоме: принцип роботи будь-чого у всесвіті взагалі та в програмуванні зокрема, у загальному, зводиться до трьох кроків.

1-й крок — сприймання даних (отримання даних, у програмуванні це передавання даних на опрацювання до певного алгоритму).

2-й крок — опрацювання даних (певні обчислювальні процеси у залежності від поставленої задачі, власне, пропускання цих даних через деякий алгоритм).

3-й крок — зворотна реакція (повернення результату опрацювання даних).

Таким чином на схемі жовті стрілки — це (механізми, потоки, процеси) передавання даних.

Сині стрілки — це (механізми, потоки, процеси) отримання результату.

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

Тут СЕ «модель», «вигляд», та «контролер» мають приблизно той самий зміст, що і у концепції MVC.

Кожен СЕ реалізується у вигляді окремого класу.

Коротко про суть кожного СЕ в схемі MMVCE.[ред. | ред. код]

Модель (Model). Містить дані, закриті для безпосереднього доступу користувача та функції опрацювання цих даних. Структура таких даних залежить від призначення компонента. Наприклад, для компонента "Меню" СЕ "Модель" може містити структуру, що моделює дерево меню та функції опрацювання вузлів такого дерева.

Контролер (Controller). Ні, він нічим не керує, як про це вказують у парадигмі MVC, це лише місток, що пов'язує СЕ моделі, вигляду та подій. Ще одне важливе значення контролера — це точка підключення інших компонентів (нижче буде наведена відповідна схема та деякі приклади).

Вигляд (View). СЕ з функціоналом, що відповідає за інтерпретацію та відображення даних.

Події (Event). СЕ у якому описані усі події, що можуть відбуватися над компонентом.

BEC (bus for external connection — шина для зовнішнього підключення). Це лише позначення можливості використовувати одні компоненти, як елементи у інших компонентах (така собі агрегація).

На схемі СЕ прямокутники Main та View зображені, відповідно, червоним та зеленим кольором і з певним виходом за межі прямокутника, який позначає компонент. Це зроблено для того, щоб підкреслити, що такі СЕ застосовуються для безпосереднього використання користувачем-програмістом (Main) та користувачем вашого програмного засобу (View).

Main — це «точка» створення/запуску компонента (такий собі ключ запалювання).

View — це «площина» взаємодії користувача програмного засобу з компонентом (така собі консоль).

Прямокутники СЕ Controller, Model, Event та BEC позначені сірим кольором, що демонструє обмежений доступ до цих елементів для користувача-програміста та закритий доступ для користувачів програмного засобу.

Загальна структура проєкту у концепції каркасно-компонентного програмування[ред. | ред. код]

Кожен завершений додаток (програма), готовий до кінцевого використання сторонніми користувачами — це компонент.

Деякі компоненти можуть містити у своїй структурі інші компоненти (асоціаційні компоненти).

Отже кожен завершений додаток — це ієрархічна структура, що складається з компонентів.

Існують компоненти, що не містять у своїй структурі інших компонентів (базові компоненти).

Компоненти поєднуються у певну ієрархію через точку виходу на СЕ Main та точку входу на СЕ Controller. Інші способи поєднання компонентів теоретично можливі, але вважаються недоцільними, оскільки це порушує загальну структуру побудови проєкту і, таким чином, його подальше, у разі потреби, масштабування та редагування.

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

Усе вище описане демонструється на наступній схемі:

Загальна схема mmvce-проєкту

На схемі у крайньому лівому положенні розташовано блок, що позначає проєкт (Main Prj), праворуч від нього показано розгортання усіх компонентів, які входять до його складу.

Так, наприклад, компоненти 'C 01' , 'C 02' та 'C 04' - базові компоненти. Компонент 'C 03' - асоціаційний.

Проєкт 'Main Prj' , фактично, є асоціаційним компонентом, який включає в себе компоненти 'C 03' та 'C 04' .

Загальний опис реалізації парадигми MMVCE мовою JavaScript[ред. | ред. код]

Загальна структура компоненту "Меню"

На малюнку зображено структуру файлів та каталогів компонента "Menu".

Файл "Menu.css" містить опис css-стилів для відображення компонента. Якщо компонент не візуальний - вміст цього файлу залишається порожнім. Якщо передбачається, що компонент міститиме додаткові ресурси, притаманні лише йому, то допускається у каталозі "css" або у кореневому каталозі компонента створення вкладених каталогів, наприклад, "fonts", "images", "sounds", інше. Спільні ресурси доцільно розміщувати у каталогах проєкту чи каталогах асоціаційних компонентів.

Кожен *.js файл містить опис відповідного класу та отримує назву відповідно до шаблону C[NameComponent][NameFile]. Так, наприклад, якщо компонент має назву Menu, то клас у файлі controller.js матиме назву CMenuController.

Кожен клас компонента допускає успадкування від відповідного базового класу. Так, наприклад, похідний клас CMenuView може бути успадкованим від базового класу CView. Таким чином для опису компонента можна використати п'ять базових класів CMain, CModel, CView, CController, CEvent, які у свою чергу можуть бути успадковані від базового класу CObject. Подібна ієрархія класів використовується, зокрема, такими мовами програмування, як Object Pascal, MFC C++, NET Framework, Java, інше.

Файл «test.php» слугує для тестування можливостей використання компонента.

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

Про можливості використання сторонніх бібліотек та фреймворків[ред. | ред. код]

Одне із основних завдань К3П - створення "чистого коду на чистій архітектурі" (див. " Роберт Мартін. "Чистий код"). У свою чергу один із постулатів створення "чистого коду" - обережне використання сторонніх бібліотек та фреймфорків. Сторонні інструменти не повинні визначати та керувати логікою проєкту, їх задача спростити розробку, а не підпорядкувати її.

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

Приклад компонента Menu мовою JavaScript[ред. | ред. код]

Нижче наведений лістинг кодів тестування компонента Menu (файл test.php).

<?php
    $p = "?p=".time();
    
    $path_to_js = "../../js/";
    $path_to_base_class = "../../js/bc/";
    $use_jquery = true;
    
    extract($_GET);
?>

<!DOCTYPE html>

<html>
    <head>
        <title>Testing the "Menu" component</title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
                                
        <link rel='stylesheet' type='text/css' href='css/Menu.css<?php echo $p; ?>'>                
    </head>
    <body>
        <div id="container"></div>
                        
        <?php if ($use_jquery){ ?>
                  <script src="<?php echo $path_to_js; ?>jquery/jquery.js"></script>        
        <?php }?>
                                
        <script src="<?php echo $path_to_base_class; ?>object.js<?php echo $p; ?>"></script>
        <script src="<?php echo $path_to_base_class; ?>controller.js<?php echo $p; ?>"></script>
        <script src="<?php echo $path_to_base_class; ?>model.js<?php echo $p; ?>"></script>
        <script src="<?php echo $path_to_base_class; ?>view.js<?php echo $p; ?>"></script>
        <script src="<?php echo $path_to_base_class; ?>event.js<?php echo $p; ?>"></script>
        <script src="<?php echo $path_to_base_class; ?>main.js<?php echo $p; ?>"></script>
                        
        <script src="js/controller.js<?php echo $p; ?>"></script>
        <script src="js/model.js<?php echo $p; ?>"></script>
        <script src="js/view.js<?php echo $p; ?>"></script>
        <script src="js/event.js<?php echo $p; ?>"></script>
        <script src="js/main.js<?php echo $p; ?>"></script>

        <script>              
            let data = {
                "Main" : {
                    title : "Головна"
                },
                "News" : {
                    title : "Новини",
                    submenu : {
                        "Culture" : {
                            title : "Культура"
                        },
                        "Science" : {
                            title : "Наука",
                            submenu : {
                                "Math" : {
                                    title : "Математика"
                                },
                                "Physics" : {
                                    title : "Фізика",
                                    submenu : {
                                        "Mechanics" : {
                                            title : "Механіка"
                                        },
                                        "Molecular" : {
                                            title : "Молекулярна фізика"
                                        },
                                        "Electrodynamics" : {
                                            title : "Електродинаміка"
                                        },
                                        "Optics" : {
                                            title : "Оптика"
                                        },
                                        "Quantum" : {
                                            title : "Квантова фізика"
                                        }
                                    }
                                },
                                "Inforamatica" : {
                                    title : "Інформатика"
                                }
                            }
                        },
                        "Art" : {
                            title : "Мистецтво"
                        }
                    }
                },
                "Contacts" : {
                    title : "Контакти"
                }
            };
                
            let objMenu = new CMenu({
                id_container : "container",
                type : "h",
                menu: data,
                Click : function(id_pm){
                    console.log("User event 'Click'. Id of the clicked menu item: ", id_pm);
                }
            });
            objMenu.Render();                                  
        </script>
                                
    </body>
</html>

У цьому лістингу повністю відсутні коментарі, що відповідає одному з правил написання кодів, вже згадуваного тут Роберта Мартіна - коментарі, зайві, той, хто читає ваш код має розуміти його сутність. У іншому випадку варто переписати код так, щоб він читався без коментарів.

Тут все очевидно (принаймні, автор на це щиро сподівається).

  1. У разі потреби, підключається зовнішня бібліотека (jQuery).
  2. Вказується HTML-об'єкт, як контейнер для відображення меню (шар <div id="container"></div>)
  3. Додаються базові класи.
  4. Додаються класи самого компонента "Меню"
  5. Описується структура меню (об'єкт data)
  6. Створюється об'єкт "Меню" (об'єкт objMenu)
  7. Об'єкт "Меню" відображається у потрібному контейнері (шар з айді "container")

Зазначу про деякі особливості використання компонента "Меню" зокрема та будь-яких компонентів в цілому.

  1. Структура меню (як і певні дані будь-якого компонента) може бути, у залежності від поставлених завдань, статично прописана в моделі компонента (файл model.js) або винесена в окремий файл, або завантажуватися із зовнішніх ресурсів (бази даних, тощо).
  2. Компонент не відображається у контейнері автоматично. Для цього використовується функція "Render". Це зроблено з метою більш гнучкого використання асоціаційних компонентів. Так, наприклад, об'єкти компонентів можуть бути створені заздалегідь, але їх відображення здійснюватиметься за мірою необхідності.

[За цим посиланням] можна ознайомитися з текстами кодів, що демонструють можливості використання концепції каркасно-компонентного програмування. Звичайно, ці коди не претендують на "повноту", "глибину" та "довершеність". Це лише приклади для унаочнення вищезазначених міркувань. Але у подальшому планується доповнити ці приклади іншими частозатребуваними компонентами ("Навігатор", "Таймер", "Календар" і т. д. ), які мають більш розширений функціонал, ніж той, що пропонується поширеними бібліотеками та, що не менш важливо, з можливістю власного швидкого та ефективного доопрацювання їх функціоналу відповідно до поставлених завдань.