All that merge: рибосомная архитектура программного кода

от автора

image

Все начинается с примера.

w // хост (мама) {     function table(qIn = {})     {         q["tit"] = "Wargana"; /** init-секция */         q["render"] = function(q){ };          qIn = merge(q, qIn); /** merge-секция */          if (q["m"] == "img") q = merge(q, img.table(qIn), qIn);          if (q["m"] == "map") q = merge(q, gmap.table(qIn), qIn);          q["render"](q); /** action-секция */      } }  img // плагин 1 (дочка) {     function table(qIn = {})     {         q["render"] = function(q)  {  return "img" + qIn["tit"];   };         return q;     } } gmap // плагин 2 (дочка) {      function table(qIn = {})      {           q["render"] = function(q) {  return "map" + qIn["tit"] + this.markers(qIn); }           return q;      }      function markers(qIn = {})      {           q["layer"] = function(q) { };           qIn = merge(q, qIn);           if (q["layerM"] == "cadastr") q = merge(q, cadastr.markers(qIn), qIn);           return q;      } }  cadastr // плагин 2.1 (внучка) {      function markers(qIn = {})      {          q["layer"] = function(q) { return "cadastr"; }           return q;      } }  /** client-секция, примеры  */  w.table({m : "img"})  w.table({m : "map", layerM : "cadastr"}) w.table({m : input("tableM"), layerM : input("gmapLayerM")}) 

Пояснения к коду

  1. Дефолтизация параметров. Init-cекция задает дефолтное значение параметров, чтобы гарантировать безопасную работу act-секции. При этом merge-секция дает возможность сколь угодно сложно перекрывать параметризацию act-секции.
    q = merge(q, ..., qIn);

    qIn в самом конце merge списка это гарантия высшей власти клиента: насколько сложно код бы себя ни параметризировал, клиент всегда может «навязать в свою игру».

    Главное в схеме «Init — Merge — Act» — это связка «Init — Act», а Merge — вторичен.

    function some(qIn = {}) {      q["render"] = function(q){ return 1; };       q = merge(q, qIn);      return q["render"](q);  } 

    Предельная инит-генеалогия:

    function some(){  q["render"] = function(q){ return 1; };   return q["render"](q); } 

    Выход в ноль:

    function some(){  return 1; } 
  2. Сеточная плагинизация. Используя принцип «Init — Merge — Act» структура кода может вязаться до любого уровня вложенности, потому что аргументы merge()’a внутри тоже строятся по принципу IMA.
    Интерфейсы

    function some(){ q["isGrab"] = 0; q = merge(q, plugSome(q)); } function plugSome(qIn = {}) { q["isGrab"] = 1; q = merge(q, qIn); return q; } 

    Init-секция up-хоста some() дефолтизируя параметр isGrab для гарантии безопасной работы своей act-секции (которой в данном случае вообще нет), одновременно задает интерфейс для всех своих плагинов, подключаемых в merge-секции.

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

    Неймспейсинг

    Сеточная плагинизация на общей шине создает единое пространство имен для всей подсетки плагинов, с одной стороны, создавая возможность повторного использования одних и тех же сигнатур в разных хостах, коими могут быть даже две разные public-функции одного и того же модуль-хоста, например:

    modHost {     function a(){ q["isGrab"] = 0; }     function b(){ q["isGrab"] = 0; } } 

    Благодаря этому устраняется задача вводить modHost.isGrabA, modHost.isGrabB

    А с другой стороны, даже самый камчатский сеточный плагин (если ему позволит автор сетки) может воспользоваться ресурсами всей up-трассы параметризации, чтобы выстроить свое поведение в зависимости от логики контекста.

    Параноики в любой момент для успокоения нервной системы могут устроить неймспейсовый разрыв, отказавшись от мержа перед ретурном шины:

    function a(){ q["isGrab"] = 0; q = merge(q, b(q)); } function b(qPro = {}) {      q["isGrab"] = !qPro["isGrab"]; ;      /* q = merge(q, qIn); */     return q;  } 

    Типизация

    Сведя лепку алгоритма с модульного уровня на уровень потока исполнения, выбор типа становится точно таким же параметром как и параметризация уже выбранного типа:

    if (input.getM() == "img") imgW.table({width : 50})

    vs

    w.table({m : input("m"), width : 50})

    Хостов (как единиц смысла верхнего уровня) становится меньше, параметров становится больше.

Стратегии

Как добавить функционал

  1. скопипастить init-секцию из хоста мамы;
  2. cоздать хост плагина дочки и вставить туда скелет init-секции хоста мамы;
  3. перепрошить каждый параметр или выкинуть, если дефолтное значение устраивает;
  4. добавить в merge-секцию хоста мамы условный мерж на хост дочки;
  5. в клиентской секции использовать новые доп.параметры привычных хостов.

Как убрать функционал

  1. отключить дочку от мамы в merge-секции;
  2. потереть хост дочки.

Как произвести кросс-мерж

function some(qIn = {}) {      // init-секция      q["total"] = 15;       // merge-секция       // q = merge(q, someA(), someB(), qIn);      qA = someA();      qB = someB();      qPro["total"] = q["total"] + qA["total"] + qB["total"] + qIn["total"];      q = merge(a, someA, someB, qIn, qPro);       // act-секция        }

ссылка на оригинал статьи http://habrahabr.ru/post/206896/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *