Використовуйте динамічні мови динамічно: Частина 1. Введення в Java scripting API (исходники), Різне, Програмування, статті

Java-розробники знають, що Java не завжди є кращим мовою для рішення деякого роду завдань. Цього року релізи версій 1.0 для JRuby і Groovy посилили інтерес до вбудовуванню динамічних мов в додатка Java. Groovy, JRuby, Rhino, Jython та інші проекти з відкритим кодом надають можливість писати код на так званих скриптових мовах і запускати його під керуванням JVM. До сих пір інтеграція таких мов з Java-кодом, звичайно, мала на увазі необхідність вивчення для кожного інтерпретатора його унікального API і особливостей реалізації.


Пакет javax.script, Доданий в Java SE 6, полегшує процес інтеграції динамічних мов. Він надає однаковий, простий спосіб виклику безлічі скриптових мов при використанні невеликого набору інтерфейсів і реальних класів. Але Java scripting API – це більше, ніж просто полегшення у створенні скриптових фрагментів програми; пакет підтримки скриптинга дозволяє вам зчитувати і викликати зовнішні скрипти на льоту, що означає можливість динамічної модифікації самих скриптів для зміни поведінки виконуваного додатку.


Ця стаття, перша в серії з двох частин, є введенням в можливості і ключові класи Java scripting API з використанням програми в стилі “Hello World”. Частина 2 представляє більш реалістичний приклад програми, що розкриває додаткові сильні сторони скриптової API. У цьому додатку scripting API задіяний для створення движка з динамічно настроюються правилами, в якому правила кодуються у вигляді зовнішніх скриптів, написаних на Groovy, JavaScript і Ruby. Правила приймають рішення – чи може претендент внутрішнього позички претендувати на окремі іпотечні продукти. Втілення таких правил допомогою Java scripting API дозволяє змінювати самі правила і додавати в процесі виконання нові продукти іпотеки.


API Java-скриптинга







 



Скриптінг проти динаміки

Термін скриптова звичайно відноситься до мов, запускаються в интерпретирующей оболонці без виділення етапу копміляціі. Термін динамічний, як правило, відноситься до мов, які очікують моменту, коли середу виконання визначить тип змінної або поведінку об’єкта і містить можливості на подобу замикань (closures) і продовжень (continuations). Деяким мов програмування загального призначення підходять обидва терміни. Перевага терміну скриптова мова тут віддано з тієї причини, що ця стаття сфокусована на Java Scripting API, а не тому що згадуються мов не дістає динамічних характеристик.


Пакет підтримки скриптинга був доданий в мову Java в грудні 2006 для забезпечення уніфікованого способу інтеграції скриптових мов у додаток Java. Для розробників мов пакет надає спосіб написання сполучного коду, що дозволяє їх мовам викликатися динамічно з Java-додатки. Для Java-розробників пакет пропонує невеликий набір класів і інтерфейсів, які дають можливість скриптам, написаним на якому багатьма мовами, бути викликаним через узагальнений API. Пакет скриптинга, таким чином, подібний пакету Java Database Connectivity (JDBC) – з ним різні мови (як різні бази даних) можуть бути інтегровані в платформу Java за допомогою узгоджувального інтерфейсу.


Раніше динамічний виклик скриптової мови з Java-коду ускладнювався використанням специфічних класів, що поставляються в дистрибутиві кожної мови, або ж використанням Bean Scripting Framework (BSF) від Apache Jakarta. BSF об’єднує невелику кількість скриптових мов під управлінням єдиного API. Більше двох дюжин скриптових мов, включаючи AppleScript, Groovy, JavaScript, Jelly, PHP, Python, Ruby і Velocity, можуть бути інтегровані в Java-код при використанні Java SE 6 Scripting API, головним чином базується на BSF.


Scripting API забезпечує двосторонній видимість між Java-додатками і зовнішніми скриптами. Ваш Java-код може не тільки викликати зовнішні скрипти, але також може надати таким скриптам доступ до обраним Java-об’єктів. Зовнішній скрипт Ruby, наприклад, може викликати методи Java-об’єктів і мати доступ до їх властивостями, що дозволяє скриптам додавати в виконувався додаток поведінку, не передбачене на момент розробки.


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



Привіт, скриптова світ


Клас HelloScriptingWorld, Який ви можете завантажити поряд з іншим кодом для цієї статті (див. Файли для завантаження), демонструє ключові можливості пакета підтримки Java-скриптинга. У ньому використовуються жорстко закодовані фрагменти на JavaScript, взятого в якості прикладу скриптової мови. Що міститься в класі метод main(), Показаний в лістингу 1, створює виконує оточення для JavaScript скрипта, а потім викликає п’ять методів (показаних в наступних лістингах), що підкреслюють можливості пакету:


Лістинг 1. Метод main для HelloScriptingWorld





public static void main(String[] args) throws ScriptException, NoSuchMethodException {
ScriptEngineManager scriptEngineMgr = new ScriptEngineManager();
ScriptEngine jsEngine = scriptEngineMgr.getEngineByName(“JavaScript”);
if (jsEngine == null) { System.err.println (“Для JavaScript не знайдено скриптової движка”);
System.exit(1);
}
System.out.println (“Викликаємо invokeHelloScript …”);
invokeHelloScript(jsEngine);
System.out.println(“Викликаємо defineScriptFunction … “);
defineScriptFunction(jsEngine);
System.out.println(“Викликаємо invokeScriptFunctionFromEngine … “);
invokeScriptFunctionFromEngine(jsEngine);
System.out.println(“Викликаємо invokeScriptFunctionFromJava … “);
invokeScriptFunctionFromJava(jsEngine);
System.out.println(“Викликаємо invokeJavaFromScriptFunction … “);
invokeJavaFromScriptFunction(jsEngine);
}

Головне завдання методу main() – Отримання примірника javax.script.ScriptEngine (Дві перші конструкції в лістингу 1). Скриптова движок завантажує та виконує скрипти для деякого конкретного мови. Це найбільш часто використовуваний і затребуваний клас в пакеті Java-скриптинга. Ви витягаєте скриптова движок з javax.script.ScriptEngineManager (Перший вираз присвоювання). Типова необхідність – отримання в програмі тільки одного примірника движка, якщо тільки не використовується безліч скриптових мов.


Клас ScriptEngineManager


ScriptEngineManager, Можливо, єдиний реальний клас в пакеті скриптинга до якого ви будете звертатися регулярно; більшість іншого – інтерфейси. І це, можливо, єдиний клас з пакету скриптинга, примірники якого ви будете створювати безпосередньо (чи опосередковано – через механізм впровадження залежності (dependency-injection) так, як це робиться в Spring Framework.) ScriptEngineManager може повертати скриптова движок одним з трьох способів:








 



Чому в нашому прикладі – JavaScript?

У нашому прикладі Hello World ми використовуємо JavaScript почасти тому що його код простий для розуміння, але більшою мірою – бо середовища виконання для Java 6 у постачанні від Sun Microsystems і BEA Systems комплектуються JavaScript-інтерпретатором на базі реалізації з відкритим кодом Mozilla Rhino. Для JavaScript у вас немає необхідності додавати JAR-файли підтримки скриптової мови в змінну оточення CLASSPATH.


ScriptEngineManager-И знаходять і створюють скриптові движки опосередковано. Тобто при створенні примірників ScriptEngine-менеджерів вони звертаються до механізму пошуку сервісу (додано в Java 6) для виявлення всіх зареєстрованих реалізацій javax.script.ScriptEngineFactory в CLASSPATH Ці фабричні класи поставляються в пакетах з реалізаціями Java scripting API; вам, швидше за все, ніколи не доведеться безпосередньо мати справу з цими класами.


Після того, як ScriptEngineManager знайшов все фабричні класи відповідних скриптових движків, він опитує кожну з фабрик щоб з’ясувати: чи можна створити движок необхідного типу – JavaScript для випадку в лістингу 1. Якщо фабрика відповідає ствердно, менеджер просить фабрику створити движок і він буде повернутий споживачу. Менеджер повертає null якщо фабрика для цільового мови не знайдено, що передбачено в коді лістингу 1, З метою надійності перевіряючому повертається значення на null.


Інтерфейс ScriptEngine


Як я вже згадував, ваш код використовує примірник ScriptEngine для виконання скрипта. Скриптова движок діє як посередник між вашим скриптовою кодом і цільовим мовним інтерпретатором або компілятором, який, в кінцевому рахунку, і виконує код. Отже, вам не потрібно знати які класи використовуються кожним інтерпретатором для відпрацювання коду. Наприклад, скриптова движок для JRuby може спочатку передати ваш код в екземпляр класу org.jruby.Ruby для компіляції скрипта в деяку проміжну форму, потім викликати його знову для прогону скрипта і обробки повертаються значень. Реалізація скриптова підтримки приховує деталі, включаючи те, як інтерпретатор погоджує визначення класів, об’єкти програми та потоки введення / виводу з Java-кодом.


На рис. 1 в загальному вигляді показані взаємозв’язки між вашим додатком, Java scripting API, реалізацією ScriptEngine та інтерпретатором скриптової мови. Ви можете зазначити, що ваш додаток спирається тільки на API скриптинга, що надається класом ScriptEngineManager і інтерфейсом ScriptEngine. Компонент реалізації інтерфейсу ScriptEngine обслуговує всю специфіку використання конкретного мовного інтерпретатора.


Малюнок 1: Взаємозв’язки компонентів Scripting API

Вас, мабуть, зацікавить питання де ж узяти необхідні JAR-файли, що імплементують скриптова движок і мовний інтерпретатор. Найкращим місцем для пошуку реалізації движка є, передусім, проект з відкритим кодом Scripting, підтримуваний java.net (див. Ресурси). Тут ви знайдете реалізації скриптових движків для багатьох мов і посилання на інші ресурси по темі. Проект Scripting також надає посилання для завантаження інтерпретаторів підтримуваних скриптових мов.


В лістингу 1 метод main() передає ScriptEngine в кожен метод для відпрацювання відповідного JavaScript-коду. Перший метод приведений в лістингу 2. Метод invokeHelloScript() викликає на боці движка метод eval для обчислення та виконання переданої рядка коду на JavaScript. Інтерфейс ScriptEngine визначає шість перевантажених методів eval(), Що приймають скрипт до обробки як у вигляді рядка, так і через об’єкт java.io.Reader, Широко використовуваний для вичитування скриптів із зовнішніх джерел, таких як файли.


Лістинг 2. Метод invokeHelloScript





private static void invokeHelloScript(ScriptEngine jsEngine) throws ScriptException {
jsEngine.eval(“println(“Hello from JavaScript”)”);
}





 



Контекст виконання скрипта

Приклад скрипта в додатку HelloScriptingWorld здійснює виведення в консоль використовуючи JavaScript-функцію println(), Але у вас є повний контроль над потоками введення і виведення. Скриптові движки надають опцію для зміни контексту виконання скрипта, тобто ви можете перепризначувати потоки, закріплені за standard input, standard output і standard error, а також визначати – які глобальні змінні і об’єкти Java доступні виконуваному скрипту.


JavaScript у методі invokeHelloScript() виводить Hello from JavaScript в потік стандартного виводу (standard output), що є, в даному випадку, консольним вікном. (лістинг 6 містить остаточний висновок після запуску HelloScriptingWorldApplication.)


Зверніть увагу – цей та інші методи класу декларують, що вони викидають виняток javax.script.ScriptException. Це перевіряється виняток – єдине, визначене в пакеті скриптинга – вказує на те, що движок зазнав невдачі при синтаксичному розборі або виконанні коду. Всі методи eval() скриптової движка викидають ScriptException, Тому в коді вам необхідно відповідним чином обробити цю ситуацію.


У лістингу 3 показані два пов’язаних методу: defineScriptFunction() і invokeScriptFunctionFromEngine(). Метод defineScriptFunction() також викликає на движку метод eval() з явно заданими фрагментом JavaScript-коду. Однак відзначте – цей метод всього лише задає визначення JavaScript-функції sayHello(). Виконання коду не відбувається. Функція sayHello() приймає один параметр, який потім виводиться в консоль наступним далі викликом println(). JavaScript-інтерпретатор скриптової движка додає цю функцію в своє глобальне оточення, роблячи її доступною в наступних викликах eval, Що відбувається (і це не дивно) в методі invokeScriptFunctionFromEngine().


Лістинг 3. Методи defineScriptFunction і invokeScriptFunctionFromEngine





private static void defineScriptFunction(ScriptEngine engine) throws ScriptException {
// Define a function in the script engine
engine.eval(
“function sayHello(name) {” +
” println(“Hello, ” + name)” +
“}”
);
}
private static void invokeScriptFunctionFromEngine(ScriptEngine engine)
throws ScriptException
{
engine.eval(“sayHello(“World!”)”);
}

Ця пара методів демонструє, що скриптові движки можуть зберігати стан компонентів програми і робити це стан доступним протягом наступних викликів методів eval(). Метод invokeScriptFunctionFromEngine() використовує перевагу, надане збереженим станом і викликає JavaScript-функцію, визначену в попередньому виклику eval().


Отже, багато скриптові движки зберігають стан глобальних змінних і функцій між викликами eval(). Однак, і це важливо враховувати, Java Scripting API не вимагає від движків підтримки такої можливості. Скриптові движки JavaScript, Groovy і JRuby, використовувані в цій статті, забезпечують збереження стану між викликами eval().


Лістинг 4 є варіацією попереднього прикладу. Метод invokeScriptFunctionFromJava() відрізняється тим, що викликає JavaScript-функцію sayHello() не вдаючись до методу eval(), Що належить ScriptEngine або до JavaScript-коду. Замість цього він використовує інтерфейс javax.script.Invocable зі складу Java Scripting API для виклику функції, підтримуваної скриптовою движком. Метод invokeScriptFunctionFromJava() призводить об’єкт скриптової движка до багатофункціонального типу Invocable, А потім звертається до интерфейсному методу invokeFunction() для виклику JavaScript-функції sayHello() із заданим параметром. Якщо викликається функція повертає значення, метод invokeFunction() поверне його, упакувавши в Java-тип Object.


Лістинг 4. Метод invokeScriptFunctionFromJava





private static void invokeScriptFunctionFromJava(ScriptEngine engine)
throws ScriptException, NoSuchMethodException
{
Invocable invocableEngine = (Invocable) engine;
invocableEngine.invokeFunction(“sayHello”, “from Java”);
}





 



Просунутий виклик скриптів з використанням проксі

Якщо скриптова функція або метод реалізують Java-інтерфейс – доступно більше розвинене використання Invocable. Інтерфейс Invocable визначає метод getInterface(), Що приймає як параметр інтерфейс і повертає проксі-об’єкт Java, який реалізує цей наданий раніше інтерфейс. Як тільки ви отримали проксі-об’єкт з скриптової движка, ви можете розглядати його як звичайний Java-об’єкт. Викликаються на проксі методи делегуються в скриптова движок для виконання скриптовою мовою.


Відзначте також, що лістинг 4 не містить JavaScript. Інтерфейс Invocable дозволяє Java-коду викликати скриптова функцію не знаючи мови її реалізації. Метод invokeFunction() викидає java.lang.NoSuchMethodException якщо скриптова движок не зміг знайти функцію з даними ім’ям або типом параметрів.


Java Scripting API не вимагає від скриптової движка імплементації інтерфейсу Invocable. Насправді, код в лістингу 4 мав би перед приведенням типу задіяти оператор instanceof, Щоб переконатися в тому, що движок реалізує інтерфейс Invocable.


Виклик Java-методів з скриптової коду


Приклади в лістингу 3 і лістингу 4 показують як Java-код може викликати функції або методи, визначені в скриптовій мовою. Тепер же вас, ймовірно, зацікавить – чи може код скриптової мови, в свою чергу, викликати методи Java-об’єктів. Може. Метод invokeJavaFromScriptFunction() в лістингу 5 показує – як отримати доступ до Java-об’єктів з боку скриптової движка і як скриптова код може викликати методи цих Java-об’єктів. Зокрема, метод invokeJavaFromScriptFunction() використовує наданий движком метод put() для передачі екземпляра того ж класу HelloScriptingWorld в движок. Після того, як движок отримав доступ до Java-об’єкту через ім’я, передане при виклику put(), Скриптова код викликом методу eval() використовує його.


Лістинг 5. Методи invokeJavaFromScriptFunction і getHelloReply





private static void invokeJavaFromScriptFunction(ScriptEngine engine)
throws ScriptException
{
engine.put(“helloScriptingWorld”, new HelloScriptingWorld());
engine.eval( “Println (” Викликаємо метод getHelloReply з JavaScript … “);” +
“var msg = helloScriptingWorld.getHelloReply(vJavaScript”);” + “Println (” Отримали з Java: “+ msg)”
);
}
/ ** Метод, що викликається з вищенаведеного скрипта і повертає рядок. * /
public String getHelloReply(String name) { return “Java-метод getHelloReply говорить” Привіт, “+ name +” “”;
}

JavaScript-код, що міститься у виклику методу eval() з лістингу 5 використовує Java-об’єкт HelloScriptingWorld за допомогою доступу до нього через змінну з ім’ям helloScriptingWorld, Переданим у виклик методу put() на стороні скриптової движка. Другий рядок JavaScript-коду викликає публічний Java-метод getHelloReply(), Також наведений в лістингу 5. Метод getHelloReply() повертає рядок Java-метод getHelloReply каже “Привіт, <параметр>“. Код JavaScript в методі eval() присвоює повертається з Java значення змінної msg, Потім виводить це значення в консоль.







 



Портування Java-об’єктів

Коли скриптова движок робить Java-об’єкт доступним скрипту, запущеного в виконуючою середовищі, движок повинен упакувати його в об’єктний тип, відповідний поточному скриптовій мови. Така упаковка повинна виконувати відповідні перетворення об’єкт-значення, наприклад, допускати використання Java-об’єкта Integer безпосередньо в математичних виразах скриптової мови. З’ясування того, як Java-об’єкти портируют в скриптові об’єкти специфічно для кожного скриптової движка і виходить за рамки обговорюваного в цій статті. І все ж ви повинні усвідомлювати, що така трансляція відбувається, тому ви можете проводити тестування використовуваного скриптової мови, щоб переконатися в передбачуваності виконуваних перетворень.


ScriptEngine.put і пов’язаний з ним метод get() є основними способами розподілу доступу до об’єктів і даними між Java-кодом і виконуваними движком скриптами. (Розширене обговорення цієї теми див. нижче в Область видимості виконуваного скрипта.) Коли ви викликаєте на движку метод put(), Скриптова движок асоціює другий параметр (довільний Java-об’єкт) з заданим рядковим ключем. Більшість скриптових движків забезпечують доступність цих Java-об’єктів в скриптах допомогою заданого імені змінної. Движки вільні у поводженні з переданими вами в метод put() іменами. Наприклад, скриптова движок JRuby робить helloScriptingWorld доступною в Ruby-коді у вигляді глобальної змінної $helloScriptingWorld, Що відповідає синтаксису Ruby для глобальних змінних.


Метод get() движка витягує значення, доступні в скриптовій оточенні. У загальному випадку, кожна глобальна змінна і функція з оточення доступні в Java-коді через метод get(). Але для скриптів доступні тільки ті об’єкти Java, які заявлені скриптовій движку безпосередньо – викликом put().


Така можливість доступу і маніпулювання Java-об’єктами в виконуваному додатку з боку зовнішніх скриптів є потужною технікою розширення функціональності ваших Java-програм. (Ця техніка задіяна у прикладі з Частини 2)


Запуск програми HelloScriptingWorld


Ви можете запустити додаток HelloScriptingWorld завантаживши і скомпонувавши вихідний код. zip-файл містить компонувальні сценарії як для Ant так і для Maven, щоб полегшити компіляцію і запуск прикладу додатки. Виконайте наступні кроки:



  1. Скачайте zip-архів.
  2. Створіть новий каталог, скажімо, java-scripting і розпакуйте сюди отриманий на попередньому кроці архів.
  3. Відкрийте командне вікно і перейдіть в цей каталог.
  4. Запустіть ant run-hello.

Ви повинні побачити консольний висновок з Ant, подібний до наведеного в лістингу 6. Зверніть увагу на те, що метод defineScriptFunction() не генерує виводу, тому що він визначає, але не викликає JavaScript-функцію.


Лістинг 6. Висновок з запущеного HelloScriptingWorld




                 Викликаю invokeHelloScript …
Hello from JavaScript
Викликаю defineScriptFunction …
Викликаю invokeScriptFunctionFromEngine …
Hello, World!
Викликаю invokeScriptFunctionFromJava …
Hello, from Java
Викликаю invokeJavaFromScriptFunction … Виклик методу getHelloReply з JavaScript … Отримали з Java: Java-метод getHelloReply говорить “Привіт, JavaScript”

Сумісність з Java 5


Java Scripting API з’явився в Java SE 6, але ви також можете використовувати його і з Java SE 5. Вам всього лише необхідно надати реалізацію відсутніх класів з пакету javax.script. На щастя, реалізація доступна з Java Specification Request 223 reference implementation. JSR 223 визначає Java scripting API.


Якщо ви завантажте посилальну реалізацію JSR 223, розпакуйте архів і помістіть файли script-api.jar, script-js.jar і js.jar в місці, доступному за classpath. Ці файли з комплекту поставки Java SE 6 втілюють скриптова API, інтерфейс скриптової движка JavaScript і сам скриптова движок JavaScript.


Область видимості виконуваного скрипта


За тим, як ви передаєте Java-об’єкти в виконувані движком скрипти, варто більш розвинена реалізація, ніж просто виклик движкових методів get() і put(). Коли ви викликаєте get() або put() на движку, він витягує або зберігає потрібний ключ у спеціально передбаченому примірнику інтерфейсу javax.script.Bindings. (Інтерфейс Bindings це просто інтерфейс Map, Що обслуговує рядкові ключі.)


Коли ваш код викликає Движковий метод eval(), На стороні движка використовується зумовлене зв’язування ключів зі значеннями. Проте, ви можете надати свій власний об’єкт Bindings для обслуговування викликів eval(), Щоб обмежити видимість змінних і об’єктів для даного скрипта. Тоді виклик буде виглядати як eval(String, Bindings) або eval(Reader, Bindings). Щоб полегшити створення ваших специфічних Bindings, Скриптові движки пропонують метод createBindings(), Що повертає порожній об’єкт Bindings. Виклик eval на об’єкті Bindings тимчасово приховує Java-об’єкти, збережені раніше з використанням зумовленого в движку зв’язування.


Для накопичення історії скриптова движок має в своєму складі два зумовлених механізму зв’язування: зв’язування з областю видимості на рівні движка (engine scope bindings) використовуються при викликах get() і put(), А зв’язування з глобальною областю видимості (global scope bindings) движок може застосовувати для пошуку об’єктів у випадку, якщо їх не вдалося виявити на рівні зв’язування “engine scope”. Формулювання може – істотна. Скриптові движки не зобов’язані забезпечувати доступність глобального зв’язування для скриптів. Хоча багато скриптові движки такий доступ надають.


Конструктивне призначення “global scope”-зв’язування – спільне використання об’єктів різними скриптовими движками. Кожен движок, що повертається примірником ScriptEngineManager, Комплектується одним і тим же об’єктом глобального зв’язування. Ви можете отримати цей об’єкт викликом методу getBindings(ScriptContext.GLOBAL_SCOPE) і призначити об’єкт глобального зв’язування для движка за допомогою setBindings(Bindings, ScriptContext.GLOBAL_SCOPE).


ScriptContext – Це інтерфейс, який визначає і управляє контекстом, виконавчі скриптової движка. ScriptContext містить зв’язування з “движкові” і “глобальної” областями видимості, а також потоки введення / виводу, що використовуються движком для стандартних операцій введення / виводу. Ви можете отримати контекст скриптової движка і маніпулювати ним за допомогою движкового методу getContext().


Концепції Scripting API, такі як область видимості scope, зв’язування bindings і контекст можуть, спочатку, збивати з пантелику, через своїх частково перекриваються смислів. Завантажувальний файл з вихідними кодами для цієї статті включає тестовий файл JUnit, що називається ScriptApiRhinoTest і розташований в каталозі src / test / java. Міститься там Java-код покликаний допомогти вам розібратися з цими концепціями.


Що далі?


Тепер, коли ви маєте в своєму розпорядженні базовими знаннями з Java Scripting API, Частина 2 цієї статті поліпшить і розширить ці знання за допомогою більш реалістичного прикладу програми. Ця програма використовує комбінацію зовнішніх скриптових файлів, написаних на Groovy, Ruby і JavaScript для завдання бізнес-логіки з можливістю її оперативного зміни. Як ви побачите, визначення бізнес-правил в скриптовій мовою полегшує написання цих правил і, можливо, спрощує сприйняття при читанні непрограмістів, такими як бізнес-аналітики або фахівці з опису предметної області.

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


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

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

Ваш отзыв

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

*

*