Loading…

Flash Player 9 (or above) is needed to view presentations.
We have detected that you do not have it on your computer. To install it, go here.

Like this presentation? Why not share!

JavaScript Event-driven architecture

on

  • 3,766 views

 

Statistics

Views

Total Views
3,766
Views on SlideShare
3,727
Embed Views
39

Actions

Likes
6
Downloads
67
Comments
0

2 Embeds 39

http://cloud.aylesbury.ac.uk 38
http://twitter.com 1

Accessibility

Categories

Upload Details

Uploaded via as Apple Keynote

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />

JavaScript Event-driven architecture JavaScript Event-driven architecture Presentation Transcript

  • JavaScript event-driven architecture Радослав Станков OpenFest 2010
  • Кой съм аз? @rstankov http://rstankov.com http://blog.rstankov.com http://github.com/rstankov
  • Какво е Event-driven architecture?
  • Демонстрация
  • <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>OpenFest Demo</title> <link href="/application.css" media="screen" rel="stylesheet" type="text/css" /> </head> <body> <div id="images_widget"> <form method="post" action="/images" enctype="multipart/form-data" id="new_image"> <input type="file" id="image_file" name="image[file]" /> <input type="submit" value="Добави снимка" /> </form> <ol> <li> <div style="background-image: url(/system/images/2/thumb.png);"></div> <span> <a href="/system/images/2/big.png" target="_blank" class="view">Преглед</a> <a href="/images/2" rel="nofollow" class="delete">Изтрий</a> </span> </li> <!-- more items --> </ol> </div> </body> </html>
  • <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>OpenFest Demo</title> <link href="/application.css" media="screen" rel="stylesheet" type="text/css" /> </head> <body> <div class="question"> <h1>Сигурни ли сте, че искате да изтриете тази снимка?</h1> <p> <img alt="Thumb" src="/system/images/1/thumb.png" /> </p> <form method="post" action="/images"> <input type="hidden" name="_method" value="delete" /> <input type="submit" value="Да" /> <a href="/images">Не</a> </form> </div> </body> </html>
  • <form method="post" action="/images" enctype="multipart/form-data" id="new_image"> <input type="file" id="image_file" name="image[file]" /> <input type="submit" value="Добави снимка" /> </form>
  • $.fileUploadSupported && $('#new_image').submit(function(e){ var form = $(this), file = form.children('input[type="file"]'), files = file[0].files; if (files && files.length > 0){ $.uploadFile({ url: form.attr("action"), dataType: "html", name: file.attr("name"), beforeSend: function(){ /* disable form */ }, complete: function(){ /* enable form */ }, error: function(xhr){ xhr.status == 422 && alert(xhr.responseText); }, success: function (html){ $("#images_widget ol").prepend(html); }, }, files[0]); } this.reset(); return false; });
  • $.fileUploadSupported && $('#new_image').submit(function(e){ var form = $(this), file = form.children('input[type="file"]'), files = file[0].files; if (files && files.length > 0){ $.uploadFile({ url: form.attr("action"), dataType: "html", name: file.attr("name"), beforeSend: function(){ /* disable form */ }, complete: function(){ /* enable form */ }, error: function(xhr){ xhr.status == 422 && alert(xhr.responseText); }, success: function (html){ $("#images_widget ol").prepend(html); }, }, files[0]); } this.reset(); return false; });
  • $.fileUploadSupported && $('#new_image').submit(function(e){ var form = $(this), file = form.children('input[type="file"]'), files = file[0].files; if (files && files.length > 0){ $.uploadFile({ url: form.attr("action"), dataType: "html", name: file.attr("name"), beforeSend: function(){ /* disable form */ }, complete: function(){ /* enable form */ }, error: function(xhr){ xhr.status == 422 && alert(xhr.responseText); }, success: function (html){ $("#images_widget ol").prepend(html); }, }, files[0]); } this.reset(); return false; });
  • $.fileUploadSupported && $('#new_image').submit(function(e){ var form = $(this), file = form.children('input[type="file"]'), files = file[0].files; if (files && files.length > 0){ $.uploadFile({ url: form.attr("action"), dataType: "html", name: file.attr("name"), beforeSend: function(){ /* disable form */ }, complete: function(){ /* enable form */ }, error: function(xhr){ xhr.status == 422 && form.trigger("action:error", xhr.responseText); }, success: function (html){ form.trigger("action:insert", html); }, }, files[0]); } this.reset(); return false; });
  • $("#images_widget").each(function(){ var widget = $(this), images = element.children("ol"); element.bind("action:insert", function(e, content){ images.prepend(content); }); element.bind("action:error", function(e, errorMessage){ alert(errorMessage); }); });
  • <li> <div style="background-image: url(...);"></div> <span> <a href="..." class="view">Преглед</a> <a href="..." class="delete">Изтрий</a> </span> </li>
  • <li> <div style="background-image: url(...);"></div> <span> <a href="..." class="view">Преглед</a> <a href="..." class="delete" data-method="delete" data-confirm="."> Изтрий </a> </span> </li>
  • <li> <div style="background-image: url(...);"></div> <span> <a href="..." class="view">Преглед</a> <a href="..." class="delete" data-method="delete" data-confirm="."> Изтрий </a> </span> </li>
  • $("#images_widget").each(function(){ // ... code ... widget.delegate('[data-method="delete"]', "click", function(e){ e.preventDefault(); var element = $(this); if (confirm(element.data("confirm"))){ $.ajax({ url: element.attr("href"), type: "DELETE" }); element.parent("li").remove(); } }); };
  • <span> <a href="..." class="view">Преглед</a> <a href="..." class="delete" data-method="delete" data-confirm="."> Изтрий </a> </span>
  • <span> <a href="..." class="view" data-action="preview">Преглед</a> <a href="..." class="delete" data-method="delete" data-confirm="."> Изтрий </a> </span>
  • <span> <a href="..." class="view" data-action="preview">Преглед</a> <a href="..." class="delete" data-method="delete" data-confirm="."> Изтрий </a> </span>
  • $("#images_widget").each(function(){ // ... code ... widget.delegate('[data-action="preview"]', "click", function(e){ e.preventDefault(); preview($(this).attr("href")); }); }; function preview(url){ // ... code ... }
  • <ol> <li><!-- image --></li> <li><!-- image --></li> <li><!-- image --></li> </ol>
  • <ol data-sortable-url="/images/reorder"> <li id="image_3"><!-- image --></li> <li id="image_2"><!-- image --></li> <li id="image_1"><!-- image --></li> </ol>
  • <ol data-sortable-url="/images/reorder"> <li id="image_3"><!-- image --></li> <li id="image_2"><!-- image --></li> <li id="image_1"><!-- image --></li> </ol>
  • <ol data-sortable-url="/images/reorder"> <li id="image_3"><!-- image --></li> <li id="image_2"><!-- image --></li> <li id="image_1"><!-- image --></li> </ol>
  • $("#images_widget").each(function(){ // ... code ... images.sortable({ placeholder: "ui-state-highlight", update: function(e, ui){ $.ajax({ url: images.data("sortable-url"), type: "PUT", data: $(this).sortable("serialize") }); } }); });
  • $("#images_widget").each(function(){ var widget = $(this), images = widget.children("ol"); widget.bind("action:insert", function(e, content){ images.prepend(content); }); widget.bind("action:error", function(e, errorMessage){ alert(errorMessage); }); widget.delegate('[data-method="delete"]', "click", function(e){ e.preventDefault(); var element = $(this); if (confirm(element.data("confirm"))){ $.ajax({ url: element.attr("href"), type: "DELETE" }); element.remove(); } }); widget.delegate('[data-action="preview"]', "click", function(e){ e.preventDefault(); preview($(this).attr("href")); }); images.sortable({ placeholder: "ui-state-highlight", update: function(e, ui){ $.ajax({ url: images.data("sortable-url"), type: "PUT", data: $(this).sortable("serialize") }); } }); });
  • $("#images_widget").each(function(){ var widget = $(this), images = widget.children("ol"); widget.bind("action:insert", function(e, content){ images.prepend(content); }); widget.bind("action:error", function(e, errorMessage){ alert(errorMessage); }); widget.delegate('[data-method="delete"]', "click", function(e){ e.preventDefault(); var element = $(this); if (confirm(element.data("confirm"))){ $.ajax({ url: element.attr("href"), type: "DELETE" }); element.remove(); } }); widget.delegate('[data-action="preview"]', "click", function(e){ e.preventDefault(); preview($(this).attr("href")); }); images.sortable({ placeholder: "ui-state-highlight", update: function(e, ui){ $.ajax({ url: images.data("sortable-url"), type: "PUT", data: $(this).sortable("serialize") }); } }); });
  • $("#images_widget").each(function(){ var widget = $(this), images = widget.children("ol"); widget.bind("action:insert", function(e, content){ images.prepend(content); }); widget.bind("action:error", function(e, errorMessage){ alert(errorMessage); }); widget.delegate('[data-method="delete"]', "click", function(e){ e.preventDefault(); var element = $(this); if (confirm(element.data("confirm"))){ $.ajax({ url: element.attr("href"), type: "DELETE" }); element.remove(); } }); widget.delegate('[data-action="preview"]', "click", function(e){ e.preventDefault(); preview($(this).attr("href")); }); images.sortable({ placeholder: "ui-state-highlight", update: function(e, ui){ $.ajax({ url: images.data("sortable-url"), type: "PUT", data: $(this).sortable("serialize") }); } }); });
  • $("#images_widget").each(function(){ // ... code ... widget.bind("action:error", function(e, errorMessage){ alert(errorMessage); }); widget.delegate('[data-action="preview"]', "click", function(e){ e.preventDefault(); preview($(this).attr("href")); }); });
  • $(document).bind("action:error", function(e, errorMessage){ alert(errorMessage); }); $(document).delegate('[data-action="preview"]', "click", function(e){ e.preventDefault(); preview($(this).attr("href")); });
  • $("#images_widget").each(function(){ var widget = $(this), images = widget.children("ol"); widget.bind("action:insert", function(e, content){ images.prepend(content); }); widget.delegate('[data-method="delete"]', "click", function(e){ e.preventDefault(); var element = $(this); if (confirm(element.data("confirm"))){ $.ajax({ url: element.attr("href"), type: "DELETE" }); element.remove(); } }); images.sortable({ placeholder: "ui-state-highlight", update: function(e, ui){ $.ajax({ url: images.data("sortable-url"), type: "PUT", data: $(this).sortable("serialize") }); } }); });
  • $("#images_widget").each(function(){ var widget = $(this), images = widget.children("ol"); widget.bind("action:insert", function(e, content){ images.prepend(content); }); widget.delegate('[data-method="delete"]', "click", function(e){ e.preventDefault(); var element = $(this); if (confirm(element.data("confirm"))){ $.ajax({ url: element.attr("href"), type: "DELETE" }); element.remove(); } }); images.sortable({ placeholder: "ui-state-highlight", update: function(e, ui){ $.ajax({ url: images.data("sortable-url"), type: "PUT", data: $(this).sortable("serialize") }); } }); });
  • $("#images_widget").each(function(){ // ... code ... widget.delegate('[data-method="delete"]', "click", function(e){ e.preventDefault(); var element = $(this); if (confirm(element.data("confirm"))){ $.ajax({ url: element.attr("href"), type: "DELETE" }); element.remove(); } }); });
  • $(document).delegate('[data-method="delete"]', "click", function(e){ e.preventDefault(); var element = $(this); if (confirm(element.data("confirm"))){ $.ajax({ url: element.attr("href"), type: "DELETE" }); element.trigger("action:delete"); } });
  • $("#images_widget").each(function(){ // ... code ... widget.delegate('[data-method="delete"]', "click", function(e){ e.preventDefault(); var element = $(this); if (confirm(element.data("confirm"))){ $.ajax({ url: element.attr("href"), type: "DELETE" }); element.remove(); } }); });
  • $("#images_widget").each(function(){ // ... code ... widget.delegate("li", "action:delete", function(){ $(this).remove(); }); });
  • $("#images_widget").each(function(){ var widget = $(this), images = widget.children("ol"); widget.bind("action:insert", function(e, content){ images.prepend(content); }); widget.delegate("li", "action:delete", function(){ $(this).remove(); }); images.sortable({ placeholder: "ui-state-highlight", update: function(e, ui){ $.ajax({ url: images.data("sortable-url"), type: "PUT", data: $(this).sortable("serialize") }); } }); });
  • $("#images_widget").each(function(){ var widget = $(this), images = widget.children("ol"); widget.bind("action:insert", function(e, content){ images.prepend(content); }); widget.delegate("li", "action:delete", function(){ $(this).remove(); }); images.sortable({ placeholder: "ui-state-highlight", update: function(e, ui){ $.ajax({ url: images.data("sortable-url"), type: "PUT", data: $(this).sortable("serialize") }); } }); });
  • $("#images_widget").each(function(){ // ... code ... images.sortable({ placeholder: "ui-state-highlight", update: function(e, ui){ $.ajax({ url: images.data("sortable-url"), type: "PUT", data: $(this).sortable("serialize") }); } }); });
  • $("#images_widget").each(function(){ // ... code ... images.sortable({ placeholder: "ui-state-highlight", update: function(e, ui){ images.trigger("action:reorder", $(this).sortable("serialize")); } }); });
  • $(document).delegate("[data-sortable-url]", "action:reorder", function(e, data){ $.ajax({ url: $(this).data("sortable-url"), type: "PUT", data: data }); });
  • $("#images_widget").each(function(){ var widget = $(this), images = widget.children("ol"); widget.bind("action:insert", function(e, content){ images.prepend(content); }); widget.delegate("li", "action:delete", function(){ $(this).remove(); }); images.sortable({ placeholder: "ui-state-highlight", update: function(e, ui){ images.trigger("action:reorder", $(this).sortable("serialize")); } }); });
  • $("#images_widget").each(function(){ var widget = $(this), images = widget.children("ol"); widget.bind("action:insert", function(e, content){ images.prepend(content); }); widget.delegate("li", "action:delete", function(){ $(this).remove(); }); images.sortable({ placeholder: "ui-state-highlight", update: function(e, ui){ images.trigger("action:reorder", $(this).sortable("serialize")); } }); });
  • $("#images_widget").each(function(){ var widget = $(this), images = widget.children("ol"); widget.bind("action:insert", function(e, content){ images.prepend(content); }); widget.delegate("li", "action:delete", function(){ $(this).remove(); }); images.sortable({ placeholder: "ui-state-highlight", update: function(e, ui){ images.trigger("action:reorder", $(this).sortable("serialize")); } }); });
  • $("#images_widget").each(function(){ var widget = $(this), images = widget.children("ol"); widget.bind("action:insert", function(e, content){ images.prepend(content); }); widget.delegate("li", "action:delete", function(){ $(this).remove(); }); images.sortable({ placeholder: "ui-state-highlight", update: function(e, ui){ images.trigger("action:reorder", $(this).sortable("serialize")); } }); });
  • $("#images_widget").each(function(){ var widget = $(this), images = widget.children("ol"); widget.bind("action:insert", function(e, content){ images.prepend(content); }); widget.delegate("li", "action:delete", function(){ $(this).remove(); }); images.sortable({ placeholder: "ui-state-highlight", update: function(e, ui){ images.trigger("action:reorder", $(this).sortable("serialize")); } }); });
  • $("#images_widget").each(function(){ var widget = $(this), images = widget.children("ol"); widget.bind("action:insert", function(e, content){ images.prepend(content); }); widget.delegate("li", "action:delete", function(){ $(this).remove(); }); images.sortable({ placeholder: "ui-state-highlight", update: function(e, ui){ images.trigger("action:reorder", $(this).sortable("serialize")); } }); });
  • var OpenFest = { createDynamicWidget: function(widget, opts){ widget = $(widget); opts = jQuery.extend({ list: "ol", item: "li" }, opts || {}); var list = widget.children(opts.list); widget.bind("action:insert", function(e, content){ list.prepend(content); }); widget.delegate(opts.item, "action:delete", function(){ $(this).remove(); }); list.sortable({ placeholder: "ui-state-highlight", update: function(e, ui){ list.trigger("action:reorder", $(this).sortable("serialize")); } }); } };
  • OpenFest.createDynamicWidget("#images_widget");
  • $("#images_widget").dynamicWidget();
  • $(document).bind("action:error", function(e, errorMessage){ alert(errorMessage); }); $(document).delegate('[data-action="preview"]', "click", function(e){ e.preventDefault(); preview($(this).attr("href")); }); $(document).delegate('[data-method="delete"]', "click", function(e){ e.preventDefault(); var element = $(this); if (confirm(element.data("confirm"))){ $.ajax({ url: element.attr("href"), type: "DELETE" }); element.trigger("action:delete"); } });
  • $(document).bind("action:error", function(e, errorMessage){ alert(errorMessage); }); $(document).delegate('[data-action="preview"]', "click", function(e){ e.preventDefault(); preview($(this).attr("href")); }); $(document).delegate('[data-method="delete"]', "click", function(e){ e.preventDefault(); var element = $(this); if (confirm(element.data("confirm"))){ $.ajax({ url: element.attr("href"), type: "DELETE" }); element.trigger("action:delete"); } });
  • $(document).bind("action:error", function(e, errorMessage){ alert(errorMessage); }); $(document).delegate('[data-action="preview"]', "click", function(e){ e.preventDefault(); preview($(this).attr("href")); }); $(document).delegate('[data-method="delete"]', "click", function(e){ e.preventDefault(); var element = $(this); if (confirm(element.data("confirm"))){ $.ajax({ url: element.attr("href"), type: "DELETE" }); element.trigger("action:delete"); } });
  • $(document).bind("action:error", function(e, errorMessage){ alert(errorMessage); }); $(document).delegate('[data-action="preview"]', "click", function(e){ e.preventDefault(); preview($(this).attr("href")); }); $(document).delegate('[data-method="delete"]', "click", function(e){ e.preventDefault(); var element = $(this); if (confirm(element.data("confirm"))){ $.ajax({ url: element.attr("href"), type: "DELETE" }); element.trigger("action:delete"); } });
  • var OpenFest = { createDynamicWidget: function(){ /* code */ }, confirm: function(question){ return !question || confirm(question); }, errorMessage: function(errorMessage){ alert(errorMessage); }, preview: function(){ /* code */ } };
  • $(document).bind("action:error", function(e, errorMessage){ alert(errorMessage); }); $(document).delegate('[data-action="preview"]', "click", function(e){ e.preventDefault(); preview($(this).attr("href")); }); $(document).delegate('[data-method="delete"]', "click", function(e){ e.preventDefault(); var element = $(this); if (confirm(element.data("confirm"))){ $.ajax({ url: element.attr("href"), type: "DELETE" }); element.trigger("action:delete"); } });
  • $(document).bind("action:error", function(e, errorMessage){ OpenFest.errorMessage(errorMessage); }); $(document).delegate('[data-action="preview"]', "click", function(e){ e.preventDefault(); OpenFest.preview($(this).attr("href")); }); $(document).delegate('[data-method="delete"]', "click", function(e){ e.preventDefault(); var element = $(this); if (OpenFest.confirm(element.data("confirm"))){ $.ajax({ url: element.attr("href"), type: "DELETE" }); element.trigger("action:delete"); } });
  • //= require "jquery/core" //= require "jquery/ui" //= require "jquery/html5_upload" //= require "open_fest" //= require "behaviors/error" //= require "behaviors/delete" //= require "behaviors/preview" //= require "behaviors/reorder" OpenFest.createDynamicWidget("#images_widget"); $.fileUploadSupported && $('#new_image').submit(function(e){ // ... the ugly code ... });
  • Кода стъпка по стъпка: https://github.com/RStankov/OpenFest-2010/
  • Какво е Event-driven architecture? Основната идея, е да разделите приложeнието си на малки независими части, които не знаят еднa за другa и контактуват помежду си чрез предаване на събития.
  • Няколко полезни съвета
  • Няколко полезни съвета • вашите модули да са изолирани и да контактуват само чрез събития
  • Няколко полезни съвета • вашите модули да са изолирани и да контактуват само чрез събития • следвайте един модел на именуване на събитията и data-* атрибутите си
  • Няколко полезни съвета • вашите модули да са изолирани и да контактуват само чрез събития • следвайте един модел на именуване на събитията и data-* атрибутите си • колкото по-общо е едно събитие толкова по-високо в йерархията трябва да е
  • Няколко полезни съвета • вашите модули да са изолирани и да контактуват само чрез събития • следвайте един модел на именуване на събитията и data-* атрибутите си • колкото по-общо е едно събитие толкова по-високо в йерархията трябва да е • използвайте често делегиране на събития
  • Няколко полезни съвета • вашите модули да са изолирани и да контактуват само чрез събития • следвайте един модел на именуване на събитията и data-* атрибутите си • колкото по-общо е едно събитие толкова по-високо в йерархията трябва да е • използвайте често делегиране на събития • използвайте кратки и бързи selector-и при делегиране на събития
  • Няколко полезни съвета • вашите модули да са изолирани и да контактуват само чрез събития • следвайте един модел на именуване на събитията и data-* атрибутите си • колкото по-общо е едно събитие толкова по-високо в йерархията трябва да е • използвайте често делегиране на събития • използвайте кратки и бързи selector-и при делегиране на събития • предефинирайте действията на потребителя
  • Няколко полезни съвета • вашите модули да са изолирани и да контактуват само чрез събития • следвайте един модел на именуване на събитията и data-* атрибутите си • колкото по-общо е едно събитие толкова по-високо в йерархията трябва да е • използвайте често делегиране на събития • използвайте кратки и бързи selector-и при делегиране на събития • предефинирайте действията на потребителя • внимавайте с често повтарящи се събития като mousemove, keyup
  • Няколко полезни съвета • вашите модули да са изолирани и да контактуват само чрез събития • следвайте един модел на именуване на събитията и data-* атрибутите си • колкото по-общо е едно събитие толкова по-високо в йерархията трябва да е • използвайте често делегиране на събития • използвайте кратки и бързи selector-и при делегиране на събития • предефинирайте действията на потребителя • внимавайте с често повтарящи се събития като mousemove, keyup • следвайте добрите практики за писане на JavaScript
  • Няколко полезни съвета • вашите модули да са изолирани и да контактуват само чрез събития • следвайте един модел на именуване на събитията и data-* атрибутите си • колкото по-общо е едно събитие толкова по-високо в йерархията трябва да е • използвайте често делегиране на събития • използвайте кратки и бързи selector-и при делегиране на събития • предефинирайте действията на потребителя • внимавайте с често повтарящи се събития като mousemove, keyup • следвайте добрите практики за писане на JavaScript • тествайте!
  • Какво харесвам в този начин на работа?
  • Какво харесвам в този начин на работа? • JavaScript е направен да работи по подобен начин
  • Какво харесвам в този начин на работа? • JavaScript е направен да работи по подобен начин • Пише се сравнително малко код с висока честота на пре-използваемост
  • Какво харесвам в този начин на работа? • JavaScript е направен да работи по подобен начин • Пише се сравнително малко код с висока честота на пре-използваемост • Лесно се сменят различните части от приложението
  • Какво харесвам в този начин на работа? • JavaScript е направен да работи по подобен начин • Пише се сравнително малко код с висока честота на пре-използваемост • Лесно се сменят различните части от приложението • Точно изразен начин, по който комуникират частите на приложението
  • Какво харесвам в този начин на работа? • JavaScript е направен да работи по подобен начин • Пише се сравнително малко код с висока честота на пре-използваемост • Лесно се сменят различните части от приложението • Точно изразен начин, по който комуникират частите на приложението • Лесно тестване
  • Благодаря за вниманието @rstankov