webes alkalmazÁsfejlesztÉs 1.webprogramozas.inf.elte.hu/weaf1/ea/weaf1_06.pdf · webes...

Post on 31-Mar-2020

8 Views

Category:

Documents

0 Downloads

Preview:

Click to see full reader

TRANSCRIPT

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

Programozási minták2

Programozási minták3

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ás4

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;

Chaining5

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 :("

Curry6

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.

Curry7

// Az eltárolandó függvény, szimpla összeadásvar add = function(a, b) {

return a + b;}

// Shorthand függvények legyártásavar inc = curry(add, 1),

add2 = curry(add, 2);

console.log(inc(6)); // 7console.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)));};

}

Curry8

// 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 fogjavar 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 Shorthandfüggvényt, egyszerűen használhatjuk a curryt:var adatokatSzinez = curry(elemeketSzinez, '#adatok', '.adat');adatokatSzinez('yellow');

Alaptípusok bővítése9

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ékek10

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 === 1o1.b.c === 2o1.b.c !== o2.b.c

Alapértelmezett értékek11

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

Programtervezési minták12

Programtervezési minták13

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 minta14

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.

Flyweight15

//A prototípusvar moleculeProto = {

velocity: 10,position: {

x: 100,y: 100

},setPosition: function setPosition(x, y) {

this.position = {x: x,y: y

};}

};//A konkrét molekulákvar m1 = Object.create(moleculeProto);var m2 = Object.create(moleculeProto);

//Változások m1-benm1.velocity = 5;m1.setPosition(90, 102);

//Tesztm2.velocity === 10m1.position.x === 90m2.position.x === 100

Prototype minta16

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';

};

Singleton18

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

Factory20

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');

Factory21

// 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;

};

Factory22

// A Factory minta gyártó-függvénye, interfészt biztosít a különböző galériák használatáhozvar 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

Mediator24

Mediator25

var scoreboard = {// HTML element to be updatedelement: document.getElementById('results'),// update the score displayupdate: 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();

};

Mediator26

var mediator = {// all the playersplayers: {},// initializationsetup: function() {

var players = this.players;players.home = new Player('Home');players.guest = new Player('Guest');

},// someone plays, update the scoreplayed: function() {

var players = this.players,score = {

Home: players.home.points,Guest: players.guest.points

};scoreboard.update(score);

},// handle user interactionskeypress: function(e) {

e = e || window.event; // IEif (e.which === 49) { // key "1"

mediator.players.home.play();return;

}if (e.which === 48) { // key "0"

mediator.players.guest.play();return;

}}

};

// go!mediator.setup();window.onkeypress = mediator.keypress;// game over in 30 secondssetTimeout(function() {

window.onkeypress = null;alert('Game over!');

}, 30000);

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

Observer28

Ld. eseménykezelés a böngészőben

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)

observable.addEventListener('esemény', observer, false);

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);

}}

};

30

//observablevar weatherService = extendDeep({

state: 'sunny',changeState: function (newState, hours) {this.state = newState;this.notify(newState, hours);

}}, observable);

//observervar 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 weatherweatherService.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);

Pubsub32 //observable

var weatherService = {state: 'sunny',changeState: function (newState, hours) {this.state = newState;pubsub.notify(newState, hours);

}};

//observervar smartPhone = {warnForRain: /*...*/,prepareForSun: /*...*/

};//registering at observablepubsub.addObserver('rainy', smartPhone.warnForRain);pubsub.addObserver('sunny', smartPhone.prepareForSun);

//notify observers about changes in the weatherweatherService.changeState('rainy', 3);weatherService.changeState('rainy', 2);weatherService.changeState('rainy', 1);weatherService.changeState('sunny', 1);

Pubsub példa33

<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>

34

// Pre-compile template and "cache" it using closurevar resultTemplate = _.template($( "#resultTemplate" ).html());

// Subscribe to the new search tags topic$.subscribe( "/search/tags" , function( e, tags ) {

$( "#lastQuery" ).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 /search/tags topic$( "#flickrSearch" ).submit( function( e ) {

e.preventDefault();var tags = $(this).find( "#query").val();if ( !tags ){

return;}$.publish( "/search/tags" , [ $.trim(tags) ]);

});

$.subscribe("/search/tags", function( e, tags ) {

$.getJSON( "url" ,{tags: tags,tagmode: "any",format: "json"

},

function( data ){if( !data.items.length ) {

return;}$.publish( "/search/resultSet" , {

items: data.items } );}

);});

Minták a gyakorlatban – példák35

36

var jQuery = (function() {

var jQuery = function( selector, context ) {

return new jQuery.fn.init( selector, context, rootjQuery );

},

...

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;

})();

jQuery37

jQuery()

prototype

jQuery.fn=jQuery.prototype

size

init()

length

each()

eq()

selector

constructor

jQ obj = $(‘.adat’) =

new jQuery.fn.init()

context

length

__proto__

[ ]

selector

init()

prototype

jQuery plugin készítés38

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és39

Plugin helye: $.fn névtérbe

(function( $ ){$.fn.myPlugin = function() {

// Plugin kódja};

})( jQuery );

jQuery plugin készítés40

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és41

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és42

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és43

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','background-color' : 'blue'

};return this.each(function() {

if ( options ) { $.extend( settings, options );

}// Tooltip plugin code here//...

});};

})( jQuery );

jQuery plugin készítés44

Csak egy metódussal bővítse az $.fn névteret

események kötése bind-dal, névterezve

top related