WEBES ALKALMAZÁSFEJLESZTÉS 1. Horváth Győző Egyetemi adjunktus 1117 Budapest, Pázmány Péter sétány 1/C, 2.420 Tel: (1) 372-2500/1816
2 Programozási minták
Programozási minták 3 Jól bevált, újrahasznosítható, hatékony programozási technika Egyszerű és bonyolult is van közöttük Gyakorlati minták, sokféle esetben felhasználhatjuk Tipikus hibák elkerülése, időspórolás, szemléletmód Előre is tervezünk
Chaining láncolás 4 A legtöbb metódus, ha nem getter, akkor semmivel sem tér vissza (undefined). Helyette adjuk vissza az objektumunkat, és így egymás után hívhatjuk meg a függvényeinket return this;
Chaining 5 var macska = { nev: null, korom: true, setnev: function(nev) { this.nev = nev; return this;, dorombol: function() { console.log( this.nev + ' Purr puurrr!'); return this;, torzuz: function(koromletor) { console.log( this.nev + ' tör s zúz!'); this.korom =!koromletor; return this;, printallapot: function() { if (this.korom === false) { console.log( this.nev + ' fájlalja a letört körmét :(' ); else { console.log( this.nev + ' boldog!' ); return this; ; var sc = macska.setnev("simon's Cat").dorombol().printAllapot().torZuz(true).printAllapot(); // "Simon's Cat Purr puurrr!" // "Simon's Cat boldog!" // "Simon's Cat tör s zúz!" // "Simon's Cat fájlalja a letört körmét :("
Curry 6 Függvény-alkotóhoz hasonló minta Univerzálisabb, letisztultabb formája Lényeg: olyan függvények létrehozását végzi el, amelyek korábbi függvények meghívása, néhány paraméterükkel automatikusan kitöltve. Működése: closure-ben tárolja el a hívandó függvényt és paramétereit. Híváskor az eltárolt paraméterek mellé teszi az aktuálisakat.
Curry 7 // Az eltárolandó függvény, szimpla összeadás var add = function(a, b) { return a + b; // Shorthand függvények legyártása var inc = curry(add, 1), add2 = curry(add, 2); console.log(inc(6)); // 7 console.log(add2(6)); // 8 var curry = function (func) { var args = Array.prototype.slice.apply(arguments, [1]); return function () { return func.apply(null, args.concat(array.prototype.slice.apply(arguments))); ;
Curry 8 // Az eltárolandó függvény, mely két szelektort vár, harmadik paraméterként pedig egy színkódot, amire a kijelölt elemek hátterét színezni fogja var elemeketszinez = function(hol, milyenelemeket, milyenszinre) { return $(hol).find(milyenelemeket).css('backgroundcolor', milyenszinre); elemeketszinez('#adatok', '.adat', 'yellow'); // Ha ezzel a két szelektorral rengetegszer hívjuk meg a függvényt, érdemes eltárolni az első két paramétert és egy másik függvényt készíteni, melynek csak a színeket kell megadni: var adatokatszinez = function(milyenszinre) { return elemeketszinez('#adatok', '.adat', milyenszinre); // Ám ahelyett, hogy minden hasonló esetben írnánk egy ilyen egyedi Shorthand függvényt, egyszerűen használhatjuk a curryt: var adatokatszinez = curry(elemeketszinez, '#adatok', '.adat'); adatokatszinez('yellow');
Alaptípusok bővítése 9 Mivel a sztringet a String() konstruktorfüggvény hozza létre, ezért minden string prototípusa a String.prototype. Ha ezt bővítjük, minden sztring objektumon elérhető lesz a metódus. if (typeof String.prototype.trim!== 'function') { String.prototype.trim = function() { return this.replace(/^\s*(\s+)\s*$/, "$1"); ' I wanna whitespace! :( '.trim(); // => "I wanna whitespace! :("
Alapértelmezett értékek 10 Bővítéssel var defaultvalues = { a: 1, b: { c: 2 ; var o1 = extenddeep({, defaultvalues); var o2 = extenddeep({, defaultvalues); o2.b.c = 42; o1.a === 1 o1.b.c === 2 o1.b.c!== o2.b.c
Alapértelmezett értékek 11 Prototípus-objektummal var defaultvalues = { a: 1, b: { c: 2 ; var o1 = Object.create(defaultValues); var o2 = Object.create(defaultValues); o1.a === 1 o2.b.c = 42; o1.b.c === 42 o1.b = { c: 100 o2.b.c === 42
12 Programtervezési minták
Programtervezési minták 13 A programozási minták szűkebb csoportja Tipikus objektumorientált probléma megoldása Elvileg nyelvfüggetlen minták Főleg OOP-s szemmel vizsgálták ezeket JavaScriptben, a nyelv dinamikus volta miatt, néha meglepően egyszerű megoldások születnek
Flyweight minta 14 A Flyweight minta a rendszer erőforrásaival gazdálkodik oly módon, hogy egy külön objektumban tárolja az újrahasznosítható tulajdonságokat és metódusokat ahelyett, hogy minden célobjektumba ezek külön bemásolásra kerülnének.
15 Flyweight //A prototípus var moleculeproto = { velocity: 10, position: { x: 100, y: 100, setposition: function setposition(x, y) { this.position = { x: x, y: y ; ; //A konkrét molekulák var m1 = Object.create(moleculeProto); var m2 = Object.create(moleculeProto); //Változások m1-ben m1.velocity = 5; m1.setposition(90, 102); //Teszt m2.velocity === 10 m1.position.x === 90 m2.position.x === 100
Prototype minta 16 A klasszikus OOP világában Prototype mintának nevezik azt, amikor egy objektumot egy meglévő sablon alapján hozunk létre. JavaScript bővítés prototípus-objektum
Singleton (Egyke) 17 Cél: egy osztálynak csak egy példánya legyen JavaScriptben: egy objektum mindig csak egy példányban létezik Modul mintával létrehozott objektumokat is singletonnal nevezik var obj = { tul: 'valami'; ;
18 Singleton var childsingleton = (function () { var instance, createinstance = function createinstance() { //Privát adattagok és metódusok //... return { name: 'Sári', dateofbirth: { /*...*/, getname: function getname() { /*...*/, setname: function setname(name) { /*...*/ ; ; return { get: function () { if (!instance) { instance = createinstance(); return instance; ; )(); var c1 = childsingleton.get(); var c2 = childsingleton.get(); c1 === c2
Factory (Gyár) 19 Cél objektumok létrehozása Ismétlődő tevékenységek hasonló objektumok létrehozásakor futásidejű létrehozás JavaScript Ld. objektumgyárak
Factory 20 Példa: galéria, ami különböző objektumokat jelenít meg egy felugró dobozban galeriagyar('kep').megjelenit('http://www.www.com/majomafan.jpg'); galeriagyar('video').megjelenit('http://www.youtube.com/notexists.flv'); galeriagyar('url').megjelenit('http://www.www.com');
Factory 21 // Absztrakt, tehát nem teljes objektum. Csak a tőle származó objektumok "példányosíthatóak", ha azokban kidolgozásra kerültek az itt nem implementált metódusok. var galeria = function() { return { doboztkirajzol: function() { /* Kirajzol egy dobozt */, megjelenit: function() { console.log('absztrakt!'); ; ; // A kepgaleria, videogaleria és wwwgaleria a galeria-tól származó objektumok (funkcionális származtatás). Bennük csak az őstől különböző függvényeket kell implementálni, jelen esetben a megjelenítést - ez mindegyik esetben más és más lesz. var kepgaleria = function() { var that = galeria(); that.megjelenit = function(imgurl) { that.doboztkirajzol(); /* Megjeleníti a képet */ ; return that; ; var videogaleria = function() { var that = galeria(); that.megjelenit = function(videourl) { that.doboztkirajzol(); /* Megjeleníti a videót */ ; return that; ; var wwwgaleria = function() { var that = galeria(); that.megjelenit = function(url) { that.doboztkirajzol(); /* Megjeleníti a weboldalt */ ; return that; ;
Factory 22 // A Factory minta gyártó-függvénye, interfészt biztosít a különböző galériák használatához var galeriagyar = function(mit) { switch (mit) { case 'kep': return kepgaleria(); case 'video': return videogaleria(); case 'url': return wwwgaleria(); ;
Mediator (Közvetítő) 23 Alkalmazás = sok objektum Szoros kapcsolat nehezebb változtatni Mediator lazább kapcsolatok nem közvetlenül egymással kommunikálnak közvetítő objektumon keresztül Ld. pl. delegálás
24 Mediator
25 Mediator var scoreboard = { // HTML element to be updated element: document.getelementbyid('results'), // update the score display update: function(score) { var i, msg = ''; for (i in score) { if (score.hasownproperty(i)) { msg += '<p><strong>' + i + '<\/strong>: '; msg += score[i]; msg += '<\/p>'; this.element.innerhtml = msg; ; function Player(name) { this.points = 0; this.name = name; Player.prototype.play = function() { this.points += 1; mediator.played(); ;
26 Mediator // go! mediator.setup(); window.onkeypress = mediator.keypress; // game over in 30 seconds settimeout(function() { window.onkeypress = null; alert('game over!');, 30000); var mediator = { // all the players players: {, // initialization setup: function() { var players = this.players; players.home = new Player('Home'); players.guest = new Player('Guest');, // someone plays, update the score played: function() { var players = this.players, score = { Home: players.home.points, Guest: players.guest.points ; scoreboard.update(score);, // handle user interactions keypress: function(e) { e = e window.event; // IE if (e.which === 49) { // key "1" mediator.players.home.play(); return; if (e.which === 48) { // key "0" mediator.players.guest.play(); return; ;
Observer (megfigyelő) 27 A megfigyelő mintában egy vagy több objektum (a megfigyelők) fejezi ki érdeklődését egy másik objektum (a megfigyelt) állapotváltozásai iránt feliratkozás leiratkozás értesítés
Observer 28 Ld. eseménykezelés a böngészőben observable.addeventlistener('esemény', observer, false); Elnevezések megfigyelő/előfizető/feliratkozó (observer/subscriber/listener) megfigyelt/tárgyobjektum/kiadó/értesítő/eseménykül dő (observable/subject/publisher/notifier/event emitter)
29 var observable = { observers: {, addobserver: function(type, fn) { if (typeof this.observers[type] === "undefined") { this.observers[type] = []; this.observers[type].push(fn);, removeobserver: function(type, fn) { var i, observers = this.observers[type]; for (i = 0; i < observers.length; i += 1) { if (observers[i] === fn) { observers.splice(i, 1);, notify: function(type) { var i, observers = this.observers[type], args = [].slice.call(arguments, 1); for (i = 0; i < observers.length; i += 1) { observers[i].apply(this, args); ;
//observable var weatherservice = extenddeep({ state: 'sunny', changestate: function (newstate, hours) { 30 this.state = newstate; this.notify(newstate, hours);, observable); //observer var smartphone = { warnforrain: function (hours) { console.log('rain is coming in ', hours, 'hours!');, prepareforsun: function (hours) { console.log('the sun will shine in ', hours, 'hours!'); ; //registering at observable weatherservice.addobserver('rainy', smartphone.warnforrain); weatherservice.addobserver('sunny', smartphone.prepareforsun); //notify observers about changes in the weather weatherservice.changestate('rainy', 3); weatherservice.changestate('rainy', 2); weatherservice.changestate('rainy', 1); weatherservice.changestate('sunny', 1);
Pubsub (Központi eseményvezérlés) 31 Observer szoros kapcsolat Mediator lazább kapcsolat pubsub mediator megfigyelése var pubsub = extenddeep({, observable);
Pubsub 32 //observable var weatherservice = { state: 'sunny', changestate: function (newstate, hours) { this.state = newstate; pubsub.notify(newstate, hours); ; //observer var smartphone = { warnforrain: /*...*/, prepareforsun: /*...*/ ; //registering at observable pubsub.addobserver('rainy', smartphone.warnforrain); pubsub.addobserver('sunny', smartphone.prepareforsun); //notify observers about changes in the weather weatherservice.changestate('rainy', 3); weatherservice.changestate('rainy', 2); weatherservice.changestate('rainy', 1); weatherservice.changestate('sunny', 1);
Pubsub példa 33 <form id="flickrsearch"> <input type="text" name="tag" id="query"/> <input type="submit" name="submit" value="submit"/> </form> <div id="lastquery"></div> <div id="searchresults"></div> <script id="resulttemplate" type="text/html"> <% _.each(items, function( item ){ %> <li><p><img src="<%= item.media.m %>"/></p></li> <% );%> </script>
// Pre-compile template and "cache" it using closure var resulttemplate = _.template($( "#resulttemplate" ).html()); // Subscribe to the new search tags topic $.subscribe( "/search/tags", function( e, tags ) { $( "#lastquery" ) 34.html("<p>Searched for:<strong>" + tags + "</strong></p>"); ); // Subscribe to the new results topic $.subscribe( "/search/resultset", function( e, results ){ $( "#searchresults" ).append(resulttemplate( results )); ); // Submit a search query and publish tags on the $.subscribe("/search/tags", topic function( e, $( "#flickrsearch" ).submit( function( e ) { tags ) { e.preventdefault(); $.getjson( "url",{ var tags = $(this).find( "#query").val(); tags: tags, if (!tags ){ tagmode: "any", return; format: "json", $.publish( "/search/tags", [ $.trim(tags) ]); ); function( data ){ if(!data.items.length ) { return; $.publish( "/search/resultset", { items: data.items ); ); );
35 Minták a gyakorlatban példák
var jquery = (function() { var jquery = function( selector, context ) { return new jquery.fn.init( selector, context, rootjquery );,... 36 jquery.fn = jquery.prototype = { constructor: jquery, init: function( selector, context, rootjquery ) {..., length: 0, each: function( callback, args ) {..., eq: function( i ) {..., end: function() {..., push: push, sort: [].sort, splice: [].splice ; jquery.fn.init.prototype = jquery.fn; jquery.extend = jquery.fn.extend = function() {... ; //deep copy jquery.extend({ parsejson: function( data ) {..., each: function( object, callback, args ) {..., trim:..., ); return jquery; )();
jquery 37 init() prototype jq obj = $(.adat ) = new jquery.fn.init() proto context length [ ] selector jquery() prototype jquery.fn=jquery.prototype init() size length each() eq() selector constructor
jquery plugin készítés 38 Privát névtér létrehozása önkioldó függvénnyel Biztosítsuk, hogy nem ütközik a $ függvény más könyvtárak $ jelével (function( $ ){ // Plugin deklarálása )( jquery );
jquery plugin készítés 39 Plugin helye: $.fn névtérbe (function( $ ){ $.fn.myplugin = function() { // Plugin kódja ; )( jquery );
jquery plugin készítés 40 this a jquery objektum, nem kell $ jel jquery metóduson belül DOM elem (function( $ ){ $.fn.myplugin = function() { // this === $('#element') this.fadein('normal', function(){ // this === DOM elem ); ; )( jquery ); $('#element').myplugin();
jquery plugin készítés 41 getter (function( $ ){ $.fn.maxheight = function() { var max = 0; this.each(function() { max = Math.max( max, $(this).height() ); ); return max; ; )( jquery ); var tallest = $('div').maxheight(); // A legmagasabb div magasságával tér vissza
jquery plugin készítés 42 setter: chaining biztosítása (function( $ ){ $.fn.myplugin = function() { // this === $('#element') this.fadein('normal', function(){ // this === DOM elem ); return this; ; )( jquery ); $('#element').myplugin().addclass('piros');
jquery plugin készítés 43 Alapértelmezett értékek és opciók ($.extend) Függvények elegáns paraméterezése (function( $ ){ $.fn.tooltip = function( options ) { var settings = { 'location' : 'top', ; )( jquery ); 'background-color' : 'blue' ; return this.each(function() { if ( options ) { $.extend( settings, options ); // Tooltip plugin code here //... );
jquery plugin készítés 44 Csak egy metódussal bővítse az $.fn névteret események kötése bind-dal, névterezve