Языки 5го поколения. Разве они существуют?

в 13:09, , рубрики: java, workflow, Веб-разработка, Проектирование и рефакторинг, метки: ,

Java, как и C#, по-прежнему занимают львиную долю рынка корпоративных приложений, сделанных на заказ. Java на этом рынке уже около 20 лет, C# около 10 лет, при этом они относится к языкам программирования 3го поколения. В то время, как языки 4го поколения предлагают более эффективный способ создания бизнес приложений, но всё равно Java и C# вне конкуренции. Что же говорить про языки программирования 5го поколения, если языки программирования 4го поколения, предлагая увеличение производительности программиста на порядок, не смогли потеснить лидеров из 3го поколения. Разве языки 5го поколения вообще существуют?

Поколения языков программирования

  • 1е поколение

    Программирование на уровне машинных команд. Программы на них писались с помощью нулей и единиц. Для этих языков не было ни компиляторов, ни трансляторов, код выполнялся процессором напрямую. Программный код был зависим от процессора, поэтому первое поколение языков программирования можно классифицировать, как языки уровня процессора.

  • 2е поколение

    В отличие от языков программирования первого поколения, программы можно было задавать с помощью символов, используя мнемонические сокращения английских слов. Например, MOV — означал «move» — передвинуть. JMP — означал «jump» — перепрыгнуть. Такие программы впоследствии конвертировались в машинный код программой ассемблером. Ассемблерные языки были также специфическими для компьютера и процессора, но при этом были намного более понятны человеку, чем языки 1го поколения. Их также можно классифицировать, как языки уровня процессора, но с более дружелюбным синтаксисом.

  • 3е поколение

    Эта группа языков представляет собой значительные улучшения по сравнению со вторым поколением языков. Эти языки отличаются наличием составных типов данных, именованными переменными и возможностью определять участки кода, как подпрограммы. Первыми представителями этих языков были Fortran, ALGOL и COBOL. Языки программирования общего назначения такие, как C, С++, Pascal, Java, C# тоже являются языками 3го поколения.

    Программы, написанные на языках 3го поколения, не зависят от конкретного типа процессора. Они могут не зависеть даже от типа операционной системы компьютера, поэтому их можно классифицировать, как языки уровня компьютерной системы.

  • 4е поколение

    Языки этого поколения создавались для решения задач в строго определённой предметной области, и их синтаксис наиболее приближен к языку понятному человеку. Примером этих языком могут послужить SQL, XUL, FoxPro, PowerBuilder и др. Языки четвёртого поколения часто сравнивают с предметно-ориентированными языками программирования (domain-specific language, DSL). Эти языки значительно упрощают разработку приложений в определённой предметной области, но по-прежнему программы выполняются в рамках определённой компьютерной системы. Поэтому их можно классифицировать, как языки уровня компьютерной системы, но с более дружелюбным синтаксисом.

  • 5е поколение

    На теперешний момент нет чёткого определения, по каким критериям определяются языки 5ых поколений. Пишут, что с его помощью компьютер может решить определённую проблему без участия программиста. Программисту нужно будет только задать, какую проблему должен решить компьютер и условия, которые нужно достичь, чтобы решить её. И это без необходимости того, чтобы реализовывать алгоритм или процедуру для её решения.

Если проанализировать классификации поколений языков, приведённых выше, то можно заметить, что 1е и 2е поколения работали на уровне процессоров. 3е и 4е поколения — на уровне компьютерных систем, будь то просто компьютер или кластер компьютеров. А что же с 5м поколением?

Мне видится, что языки 5го поколения должны работать на ещё более высоком уровне и оперировать не только ресурсами определённой компьютерной системы. Они должны работать на уровне распределённых компьютерных ресурсов, связывая относительно независимые системы в одно большое приложение. При этом вовлекая не только компьютерные, но и человеческие ресурсы.

Т.е. языки 5го поколения находятся между компьютерными системами и человеком. Они определяют алгоритм взаимодействия человека с программой и наоборот. Это очень похоже на use case сценарии, только на этапе выполнения программы, а не на этапе её проектирования.

Предвестниками языков 5го поколения можно считать веб фреймворки. Вернее, ту часть веб фреймворка, которая отвечает за соответствие запроса пользователя и выполняемой команды со стороны сервера. В одних фреймворках — это называется маппинг запросов на действия (actions), в других роутинг (routing) запросов.

Пример маппинга в Spring MVC:

@Controller
public class HelloWorldController {
 
    @RequestMapping("/helloWorld")
    public String helloWorld(Model model) {
        model.addAttribute("message", "Hello World!");
        return "helloWorld";
    }
}

Фреймворк, обрабатывая URL запрос "/helloWorld", вызывает соответствующий метод helloWorld(…). Метод возвращает строчку «helloWorld», что указывает фреймворку передать управление соответствующему представлению, например «hellowWorld.jsp».

Так как возможности маппинга и роутинга запросов достаточно ограничены, в большинстве случаев их соответствие прописывается один к одному. Запрограммировать какие-либо условия, ветвления маппингов достаточно сложно, поэтому их можно считать только предвестниками языков 5го поколения.

Представителями языков 5го поколения можно считать разнообразные workflows, так как workflow задаёт, какие действия, в какой последовательности и при каких условиях должны выполнить программа и человек, участвующий в этом процессе. Существуют некоторые веб фреймворки, у которых маппинг задаётся посредством определённого Workflow.

В Apache Struts 1, маппинг выглядит примерно так:

<struts-config>
 
    <action-mappings> 
 
        <action path="/submitDetailForm" 
                type="mybank.example.CustomerAction" 
                name="CustomerForm" 
                scope="request" 
                validate="true" 
                input="/CustomerDetailForm.jsp"> 
 
                <forward name="success" 
                        path="/ThankYou.jsp" 
                        redirect=”true” /> 
                 <forward name="failure" 
                        path="/Failure.jsp" /> 
        </action> 
 
        <action path=”/logoff” parameter=”/logoff.jsp” 
                type=”org.apache.struts.action.ForwardAction” /> 
 
    </action-mappings>
 
</struts-config>

По событию со страницы CustomerDetailForm.jsp форма отправляется на action — "/submitDetailForm". Фреймворк вызывает соответствующий обработчик в классе «mybank.example.CustomerAction», который возвращает результат в виде строки «success» или «failure». По результату возвращает пользователю либо «ThankYou.jsp», либо «Failure.jsp».

В Spirng Web Flow маппинг будет выглядеть примерно так:

<?xml version="1.0" encoding="UTF-8"?>
<flow ...>
 
    <input name="id" />
 
    <action-state id="edit">
        <evaluate expression="personService.findById(id)"
            result="flowScope.person" />
        <transition to="personForm" />
    </action-state>
 
    <view-state id="personForm" model="person" view="/person/form">
        <transition on="save" to="save">
            <evaluate expression="personService.save(person)" />
 
            <evaluate expression="personService.find()"
                      result="flowScope.persons" />
        </transition>
        <transition on="cancel" to="cancel" bind="false">
            <evaluate expression="personService.find()"
                      result="flowScope.persons" />
        </transition>
    </view-state>
 
    <end-state id="save"/>
 
    <end-state id="cancel"/>
 
</flow>

Фреймоворк, выполняя запрос пользователя переходит в «action-state» — edit. Где находит Person объект по Id, пришедшему из URL. Кладёт его во flowScope. Дальше переходит на «view-state» — personForm, где передаёт управление "/person/form.jsp" представлению. После того, как пользователь обновит форму, он может сохранить изменения или же отменить их. В этих случаях происходят соответствующие события «save» или «cancel», которые ведут на соответствующие end-states. При этом флоу заканчиват свою работу.

Также workflows могут задаваться не только на уровне приложения, но и на уровне интеграций приложений (business process management — BPM), обеспечивая взаимодействие между собой относительно независимых приложений. Достаточно известным примером из BPM может послужить Activiti. Пример его XML представления бизнес-процесса:

<definitions id="definitions"
  targetNamespace="http://activiti.org/bpmn20" 
  xmlns:activiti="http://activiti.org/bpmn"
  xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL">
        
        <process id="financialReport" name="Monthly financial report reminder process">
          
          <startEvent id="theStart" />
            
          <sequenceFlow id='flow1' sourceRef='theStart' targetRef='writeReportTask' />
            
          <userTask id="writeReportTask" name="Write monthly financial report" >
            <documentation>
              Write monthly financial report for publication to shareholders.
            </documentation>
            <potentialOwner>
              <resourceAssignmentExpression>
                <formalExpression>accountancy</formalExpression>
              </resourceAssignmentExpression>
            </potentialOwner>
          </userTask>
            
          <sequenceFlow id='flow2' sourceRef='writeReportTask' targetRef='verifyReportTask' />
              
          <userTask id="verifyReportTask" name="Verify monthly financial report" >
            <documentation>
              Verify monthly financial report composed by the accountancy department.
              This financial report is going to be sent to all the company shareholders.  
            </documentation>
            <potentialOwner>
              <resourceAssignmentExpression>
                <formalExpression>management</formalExpression>
              </resourceAssignmentExpression>
            </potentialOwner>
          </userTask>
            
          <sequenceFlow id='flow3' sourceRef='verifyReportTask' targetRef='theEnd' />
              
          <endEvent id="theEnd" />
              
        </process>
        
</definitions>

Процесс описывает создание некой финансовой отчётности. По «theStart» событию процесс переходит в состояние пользовательского задания — writeReportTask. На этом этапе некий бухгалтер готовит отчёт. После того, как он его закончил, он его сохраняет в систему и процесс переходит в состояние «verifyReportTask», где менеджер проверяет отчёт. После проверки, процесс заканчивается и переходит в «theEnd» состояние.

Workflows можно сравнить с чисто процедурными языками 3го поколения (Fortran, ALGOL, COBOL, С, Pascal), где workflow процессы соответствуют процедурам. В то время, как процедуры работают с определёнными структурами в памяти, то workflow процессы работают на уровне всего приложения или нескольких приложений. Главное отличие workflows от языков 3го поколения, что они являются асинхронными, их процессы выполнения растянуты во времени, и они описывают взаимодействие человека и компьютера на уровне процесса.

Из-за того, что workflows используют процедурный стиль программирования, их использование несколько ограничено, что позволяет создавать приложения только до определённого уровня сложности. Это одна из причин, почему Apache Struts 1 потерял свою привлекательность. Так как системы, написанные на нём, упирались в возможности, заложенные на уровне конфигурационных файлов (workflows).

А представьте, если бы конфигурация Struts поддерживала полноценный объектно-ориентированный подход, который присутствует в современных языках. Тогда можно было бы создавать workflows в более компактном виде, избегать дублирования кода, что позволило строить намного более расширяемые приложения.

Lexaden Web Flow представляет эту возможность, и даже более того, он позволяет группировать разные workflows в модули, модули в пакеты, пакеты в профайлы, профайлы в приложение. Весь этот функционал открыл возможность создавать динамические модели приложения под определённые роли в системе. Каждая роль получает своё уникальное приложение, строящееся из общих для всех компонентов. Структуру приложения на Lexaden Web Flow можно сравнить с HTML, который интерпретируется на сервере, а не в браузере, где в роли CSS выступают контроллеры, написанные на Java.

Как это всё работает… Всё начинается с определения состояния «application» и входящих в него «profiles».

  <application id="application" ...>
            … 
            <profile id="admin" ... />
            <profile id="manager" ... />
            <profile id="employee" ... />
            <profile id="customer" ... />
            …
  </application>

Профайл — это часть приложения, привязанная к определённой роли в системе. Профайл администратора имеет доступ ко всему функционалу в системе, когда customer имеет доступ только к ограниченному функционалу, например, сделать заказ, посмотреть статус заказа.

Профайлы описывают набор модулей доступных определённой роли.

   <profile id="customer" ...>
          …
        <module id="orders"  … >
              <on event="go_account" to="account"/>
              <on event="go_addresses" to="addresses"/>
        </module>
        <module id="account" … >
        </module>
        <module id="addresses" … >
        </module>
        …
   </profile>
 

Модули независимы между собой и обмениваются информацией только посредством событий. Например, из модуля «orders», можно перейти в модуль «addresses» по событию «go_addresses».

Модули в бизнес-приложениях можно представлять, как CRUD'ы, состоящие из контроллеров, например:

<module id="addresses" initial="list">
   ... 
   <controller id="list" … >
         <on event="go_create" to="create"/>
         <on event="go_read" to="read"/>
         <on event="go_update" to="update"/>
         <on event="go_delete" to="delete"/>
   </controller>
   <controller id="create" … >
        …
   </controller>
   <controller id="read" … >
         …
   </controller>
   <controller id="update" … >
         …
   </controller>
   <controller id="delete" … >
         …
   </controller>
   ...
</module>

Заходя в модуль «addresses», приложение переходит на «list» контроллер. К «list» контроллеру в Java коде привязывается, AddressesListController примерно таким кодом.

    flowControllerContext.bindController("addresses/list", new AddressesListController ());

С контроллером ассоциировано определённое представление (AddressesListView), которое становится активным, когда приложение переходит в состояние «addresses/list». Таким же образом привязываются другие контроллеры со своими представлениями к «create», «read», «update», «delete» состояниям. По событиям из контроллера, привязанного к состоянию «list», приложение переходит в соответствующие состояния «create», «read», «update» или «delete» и отображает соответствующие представления пользователю.

В Lexaden Web Flow поддерживается наследование, которое позволяет повторно использовать шаблоны состояний. Например, определив модуль «t_addresses» на верхнем уровне, можно его включить в разные профайлы, используя наследование.

<flow>
    ...
    <profile id="t_admin" ...>
        …
         <module id="addresses" extends="t_addresses">
               <controller id="list" extends="addresses/list">
                      <on event="go_export" to="export"/>
               </controller>
               <controller id="export" >
                      ...
               </controller>
         </module>
        …
    </profile>
 
    <profile id="t_customer" ...>
        …
         <module id="addresses" extends="t_addresses"/>
        …
     </profile>
 
    <module id="t_addresses" initial="list">
       ...
    </module>
    ...
</flow>

Профайл «t_admin» и профайл «t_customer» получают копии модуля «t_addresses», определённого на верхнем уровне.
Также, при помощи наследования в профайле «t_admin», модуль «address» расширяет возможности «list» контроллера, добавляя событие «go_export», по которому приложение перейдет в состояние «admin/addresses/export». Получается наследование состояний с полиморфизмом, что предоставляет широкие возможности по настройке динамической модели приложения.

Дальше профайлы при помощи того же наследования включаются в «application».

     <application id="application" ...>
            … 
            <profile id="admin"  extends="t_admin"/>
            <profile id="manager"   extends="t_manager" />
            <profile id="team_leader"   extends="t_manager" >
                  <on event="go_team" to="team"/>
                  <module id="team"…> … </module>
             </profile>
            <profile id="employee" extends="t_employee"/>
            <profile id="customer"  extends="t_customer"/>
            …
    </application>
 
    <profile id="t_admin" ...> ... </profile>
    <profile id="t_manager" ...>... </profile>
    <profile id="t_employee" ...>... </profile>
    <profile id="t_customer" ...>... </profile>

Профайлы, как и другие состояния application, profile, controller…, также поддерживают наследование и полиморфизм, позволяя повторно использовать конфигурации, внося в них лишь незначительные коррективы.

Используя в workflows такие возможности, как наследование, полиморфизм, иерархию состояний можно довольно легко превратить всё приложение в динамический визард. Динамический визард будет представлять собой набор сценариев работы с системой, описывающих асинхронную доменную модель, основанную на событиях. Используя механизм событий, пользователь будет перемещаться по этой модели со страницы на страницу, решая задачу в рамках определённой предметной области.

В будущем, благодаря такой гибкой архитектуре, можно будет связывать несколько распределённых приложений в одно целостное, предоставляя возможность задавать бизнес-процессы (workflows), используя те же технологии, но уже на уровне интегрированных приложений. Это могло бы выглядеть примерно так:

<profile id="customer" ...>
          …
        <module id="orders" refers="lwf://server1/profile/customer/orders"  … >
              <on event="go_account" to="account"/>
              <on event="go_addresses" to="addresses"/>
        </module>
        <module id="account" refers="lwf://server1/profile/customer/account" … >
        </module>
        <module id="addresses" refers="lwf://server2/profile/common/addresses" … >
        </module>
        …
 </profile>

Модули «orders» и «account» ссылаются на модули в приложении на сервере «server1», когда как модуль адрес ссылается на модуль в приложении на сервере «server2». При этом модули будут оставаться независимыми друг от друга и общаться только посредством событий. К контроллерам в модулях будут привязываться свои Java контроллеры, которые будут обеспечивать работу интеграционного решения. Но это всё только в будущем, надеюсь, не в таком далёком. ;)

Подводя итог, можно сказать, что языки программирования 5го поколения уже используются программистами. Только их эволюция ещё не закончена. Они находятся на стадии развития языков 3го поколения конца 50х. Их не так легко обнаружить и классифицировать. Думаю, что пройдут годы, прежде чем они наберут силу и займут своё почётное место наряду с популярными ныне языками 3го поколения — C++, Ruby, Python, Java, C#, и т.д.

P.S. Lexaden Web Flow – это язык программирования нового поколения с открытым исходным кодом (распространяется под Apache 2.0 лицензией).

Автор: hitmark

Источник


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js