JavaScript Wzorce

background image
background image

Tytuł oryginału: JavaScript Patterns

Tłumaczenie: Rafał Jońca

ISBN: 978-83-246-3821-5

© Helion S.A. 2012.

Authorized Polish translation of the English edition of JavaScript Patterns ISBN 9780596806750 © 2010,
Yahoo!, Inc. All rights reserved.
This translation is published and sold by permission of O’Reilly Media, Inc., the owner of all rights to
publish and sell the same.

All rights reserved. No part of this book may be reproduced or transmitted in any form or by any means,
electronic or mechanical, including photocopying, recording or by any information storage retrieval system,
without permission from the Publisher.

Wszelkie prawa zastrzeżone. Nieautoryzowane rozpowszechnianie całości lub fragmentu niniejszej
publikacji w jakiejkolwiek postaci jest zabronione. Wykonywanie kopii metodą kserograficzną,
fotograficzną, a także kopiowanie książki na nośniku filmowym, magnetycznym lub innym powoduje
naruszenie praw autorskich niniejszej publikacji.

Wszystkie znaki występujące w tekście są zastrzeżonymi znakami firmowymi bądź towarowymi ich
właścicieli.

Autor oraz Wydawnictwo HELION dołożyli wszelkich starań, by zawarte w tej książce informacje były
kompletne i rzetelne. Nie biorą jednak żadnej odpowiedzialności ani za ich wykorzystanie, ani za związane
z tym ewentualne naruszenie praw patentowych lub autorskich. Autor oraz Wydawnictwo HELION nie
ponoszą również żadnej odpowiedzialności za ewentualne szkody wynikłe z wykorzystania informacji
zawartych w książce.

Wydawnictwo HELION
ul. Kościuszki 1c, 44-100 GLIWICE
tel. 32 231 22 19, 32 230 98 63
e-mail: helion@helion.pl
WWW: http://helion.pl (księgarnia internetowa, katalog książek)

Drogi Czytelniku!
Jeżeli chcesz ocenić tę książkę, zajrzyj pod adres
http://helion.pl/user/opinie/jascwz
Możesz tam wpisać swoje uwagi, spostrzeżenia, recenzję.

Printed in Poland.

Kup książkę

Poleć książkę

Oceń książkę

Księgarnia internetowa

Lubię to! » Nasza społeczność

background image

5

Spis treci

Wstp

............................................................................................................................11

1. Wprowadzenie

............................................................................................................ 15

Wzorce

15

JavaScript — podstawowe cechy

16

Zorientowany obiektowo

16

Brak klas

17

Prototypy 18
rodowisko 18

ECMAScript 5

18

Narzdzie JSLint

19

Konsola

20

2. Podstawy

..................................................................................................................... 21

Tworzenie kodu atwego w konserwacji

21

Minimalizacja liczby zmiennych globalnych

22

Problem ze zmiennymi globalnymi

22

Efekty uboczne pominicia var

24

Dostp do obiektu globalnego

25

Wzorzec pojedynczego var

25

Przenoszenie deklaracji — problem rozrzuconych deklaracji var

26

Ptle for

27

Ptle for-in

29

Modyfikacja wbudowanych prototypów

31

Wzorzec konstrukcji switch

31

Unikanie niejawnego rzutowania

32

Unikanie eval()

32

Konwertowanie liczb funkcj parseInt()

34

Kup książkę

Poleć książkę

background image

6

_ Spis

treci

Konwencje dotyczce kodu

34

Wcicia

35

Nawiasy klamrowe

35

Pooenie nawiasu otwierajcego

36

Biae spacje

37

Konwencje nazewnictwa

38

Konstruktory pisane od wielkiej litery

38

Oddzielanie wyrazów

39

Inne wzorce nazewnictwa

39

Pisanie komentarzy

40

Pisanie dokumentacji interfejsów programistycznych

41

Przykad dokumentacji YUIDoc

42

Pisanie w sposób uatwiajcy czytanie

44

Ocenianie kodu przez innych czonków zespou

45

Minifikowanie kodu tylko w systemie produkcyjnym

46

Uruchamiaj narzdzie JSLint

47

Podsumowanie 47

3. Literay

i konstruktory .................................................................................................49

Litera obiektu

49

Skadnia literau obiektowego

50

Obiekty z konstruktora

51

Puapka konstruktora Object

51

Wasne funkcje konstruujce

52

Warto zwracana przez konstruktor

53

Wzorce wymuszania uycia new

54

Konwencja nazewnictwa

54

Uycie that

54

Samowywoujcy si konstruktor

55

Litera tablicy

56

Skadnia literau tablicy

56

Puapka konstruktora Array

56

Sprawdzanie, czy obiekt jest tablic

57

JSON

58

Korzystanie z formatu JSON

58

Litera wyraenia regularnego

59

Skadnia literaowego wyraenia regularnego

60

Otoczki typów prostych

61

Obiekty bdów

62

Podsumowanie 63

Kup książkę

Poleć książkę

background image

Spis treci

_

7

4. Funkcje

.........................................................................................................................65

Informacje ogólne

65

Stosowana terminologia

66

Deklaracje kontra wyraenia — nazwy i przenoszenie na pocztek

67

Waciwo name funkcji

68

Przenoszenie deklaracji funkcji

68

Wzorzec wywoania zwrotnego

70

Przykad wywoania zwrotnego

70

Wywoania zwrotne a zakres zmiennych

72

Funkcje obsugi zdarze asynchronicznych

73

Funkcje czasowe

73

Wywoania zwrotne w bibliotekach

74

Zwracanie funkcji

74

Samodefiniujce si funkcje

75

Funkcje natychmiastowe

76

Parametry funkcji natychmiastowych

77

Wartoci zwracane przez funkcje natychmiastowe

77

Zalety i zastosowanie

79

Natychmiastowa inicjalizacja obiektu

79

Usuwanie warunkowych wersji kodu

80

Waciwoci funkcji — wzorzec zapamitywania

82

Obiekty konfiguracyjne

83

Rozwijanie funkcji

84

Aplikacja funkcji

84

Aplikacja czciowa

85

Rozwijanie funkcji

87

Kiedy uywa aplikacji czciowej

89

Podsumowanie 89

5. Wzorce

tworzenia obiektów ...................................................................................... 91

Wzorzec przestrzeni nazw

91

Funkcja przestrzeni nazw ogólnego stosowania

92

Deklarowanie zalenoci

94

Metody i waciwoci prywatne

95

Skadowe prywatne

96

Metody uprzywilejowane

96

Problemy z prywatnoci

96

Literay obiektów a prywatno

98

Prototypy a prywatno

98

Udostpnianie funkcji prywatnych jako metod publicznych

99

Kup książkę

Poleć książkę

background image

8

_ Spis

treci

Wzorzec moduu

100

Odkrywczy wzorzec moduu

102

Moduy, które tworz konstruktory

102

Import zmiennych globalnych do moduu

103

Wzorzec piaskownicy

103

Globalny konstruktor

104

Dodawanie moduów

105

Implementacja konstruktora

106

Skadowe statyczne

107

Publiczne skadowe statyczne

107

Prywatne skadowe statyczne

109

Stae obiektów

110

Wzorzec acucha wywoa

112

Wady i zalety wzorca acucha wywoa

112

Metoda method()

113

Podsumowanie 114

6. Wzorce

wielokrotnego uycia kodu ..........................................................................115

Klasyczne i nowoczesne wzorce dziedziczenia

115

Oczekiwane wyniki w przypadku stosowania wzorca klasycznego

116

Pierwszy wzorzec klasyczny — wzorzec domylny

117

Podanie wzdu acucha prototypów

117

Wady wzorca numer jeden

119

Drugi wzorzec klasyczny — poyczanie konstruktora

119

acuch prototypów

120

Dziedziczenie wielobazowe przy uyciu poyczania konstruktorów

121

Zalety i wady wzorca poyczania konstruktora

122

Trzeci wzorzec klasyczny — poyczanie i ustawianie prototypu

122

Czwarty wzorzec klasyczny — wspódzielenie prototypu

123

Pity wzorzec klasyczny — konstruktor tymczasowy

124

Zapamitywanie klasy nadrzdnej

125

Czyszczenie referencji na konstruktor

125

Podejcie klasowe

126

Dziedziczenie prototypowe

129

Dyskusja 129
Dodatki do standardu ECMAScript 5

130

Dziedziczenie przez kopiowanie waciwoci

131

Wzorzec wmieszania

132

Poyczanie metod

133

Przykad — poyczenie metody od obiektu Array

134

Poyczenie i przypisanie

134

Metoda Function.prototype.bind()

135

Podsumowanie 136

Kup książkę

Poleć książkę

background image

Spis treci

_

9

7. Wzorce

projektowe ....................................................................................................137

Singleton

137

Uycie sowa kluczowego new

138

Instancja we waciwoci statycznej

139

Instancja w domkniciu

139

Fabryka

141

Wbudowane fabryki obiektów

143

Iterator

143

Dekorator

145

Sposób uycia

145

Implementacja 146
Implementacja wykorzystujca list

148

Strategia

149

Przykad walidacji danych

150

Fasada

152

Porednik

153

Przykad 153
Porednik jako pami podrczna

159

Mediator

160

Przykad mediatora

160

Obserwator 163

Pierwszy przykad — subskrypcja magazynu

163

Drugi przykad — gra w naciskanie klawiszy

166

Podsumowanie 169

8. DOM i wzorce dotyczce przegldarek ..................................................................... 171

Podzia zada

171

Skrypty wykorzystujce DOM

172

Dostp do DOM

173

Modyfikacja DOM

174

Zdarzenia

175

Obsuga zdarze

175

Delegacja zdarze

177

Dugo dziaajce skrypty

178

Funkcja setTimeout()

178

Skrypty obliczeniowe

179

Komunikacja z serwerem

179

Obiekt XMLHttpRequest

180

JSONP

181

Ramki i wywoania jako obrazy

184

Kup książkę

Poleć książkę

background image

10

_ Spis

treci

Serwowanie kodu JavaScript klientom

184

czenie skryptów

184

Minifikacja i kompresja

185

Nagówek Expires

185

Wykorzystanie CDN

186

Strategie wczytywania skryptów

186

Lokalizacja elementu <script>

187

Wysyanie pliku HTML fragmentami

188

Dynamiczne elementy <script> zapewniajce nieblokujce pobieranie

189

Wczytywanie leniwe

190

Wczytywanie na danie

191

Wstpne wczytywanie kodu JavaScript

192

Podsumowanie 194

Skorowidz

.................................................................................................................. 195

Kup książkę

Poleć książkę

background image

137

ROZDZIA 7.

Wzorce projektowe

Wzorce projektowe opisane w ksice tak zwanego gangu czworga oferuj rozwizania ty-
powych problemów zwizanych z projektowaniem oprogramowania zorientowanego obiektowo.
S dostpne ju od jakiego czasu i sprawdziy si w wielu rónych sytuacjach, warto wic
si z nimi zapozna i powici im nieco czasu.

Cho same te wzorce projektowe nie s uzalenione od jzyka programowania i implementa-
cji, byy analizowane przez wiele lat gównie z perspektywy jzyków o silnym sprawdzaniu
typów i statycznych (niezmiennych) klasach takich jak Java lub C++.

JavaScript jest jzykiem o lunej kontroli typów i bazuje na prototypach (a nie klasach), wic nie-
które z tych wzorców okazuj si wyjtkowo proste, a czasem wrcz banalne w implementacji.

Zacznijmy od przykadu sytuacji, w której w jzyku JavaScript rozwizanie wyglda inaczej
ni w przypadku jzyków statycznych bazujcych na klasach, czyli od wzorca singletonu.

Singleton

Wzorzec singletonu ma w zaoeniu zapewni tylko jedn instancj danej klasy. Oznacza to,
e próba utworzenia obiektu danej klasy po raz drugi powinna zwróci dokadnie ten sam
obiekt, który zosta zwrócony za pierwszym razem.

Jak zastosowa ten wzorzec w jzyku JavaScript? Nie mamy przecie klas, a jedynie obiekty.
Gdy powstaje nowy obiekt, nie ma w zasadzie drugiego identycznego, wic jest on automa-
tycznie singletonem. Utworzenie prostego obiektu za pomoc literau to doskonay przykad
utworzenia singletonu.

var obj = {
myprop: 'warto'
};

W JavaScripcie obiekty nie s sobie równe, jeli nie s dokadnie tym samym obiektem,
wic nawet jeli utworzy si dwa identyczne obiekty z takimi samymi wartociami, nie
bd równowane.

var obj2 = {
myprop: 'warto'
};
obj === obj2; // false
obj == obj2; // false

Kup książkę

Poleć książkę

background image

138

_

Rozdzia 7. Wzorce projektowe

Mona wic stwierdzi, e za kadym razem, gdy powstaje nowy obiekt tworzony za pomoc
literau, powstaje nowy singleton, i to bez uycia dodatkowej skadni.

Czasem gdy ludzie mówi „singleton” w kontekcie jzyka JavaScript, maj na myli
wzorzec moduu opisany w rozdziale 5.

Uycie sowa kluczowego new

JavaScript jest jzykiem niestosujcym klas, wic dosowna definicja singletonu nie ma tu za-
stosowania. Z drugiej strony jzyk posiada sowo kluczowe

new

, które tworzy obiekty na

podstawie funkcji konstruujcych. Czasem tworzenie ich w ten sposób jako singletonów mo-
e by ciekawym podejciem. Ogólny pomys jest nastpujcy: kilkukrotne wywoanie funkcji
konstruujcej z uyciem

new

powinno spowodowa kadorazowo zwrócenie dokadnie tego

samego obiektu.

Przedstawiony poniej opis nie jest uyteczny w praktyce. Stanowi raczej teoretyczne
wyjanienie powodów powstania wzorca w jzykach statycznych o cisej kontroli
typów, w których to funkcje nie s penoprawnymi obiektami.

Poniszy przykad ilustruje oczekiwane zachowanie (pod warunkiem e nie wierzy si
w wiaty równolege i akceptuje si tylko jeden).

var uni = new Universe();
var uni2 = new Universe();
uni === uni2; // true

W tym przykadzie

uni

tworzone jest tylko przy pierwszym wywoaniu konstruktora. Drugie

i kolejne wywoania zwracaj ten sam obiekt. Dziki temu

uni === uni2

(to dokadnie ten

sam obiekt). Jak osign taki efekt w jzyku JavaScript?

Konstruktor

Universe

musi zapamita instancj obiektu (

this

), gdy zostanie utworzona po

raz pierwszy, a nastpnie zwraca j przy kolejnych wywoaniach. Istnieje kilka sposobów,
by to uzyska.

x

Wykorzystanie zmiennej globalnej do zapamitania instancji. Nie jest to zalecane podej-
cie, bo zmienne globalne naley tworzy tylko wtedy, gdy jest to naprawd niezbdne.
Co wicej, kady moe nadpisa tak zmienn, take przez przypadek. Na tym zakoczmy
rozwaania dotyczce tej wersji.

x

Wykorzystanie waciwoci statycznej konstruktora. Funkcje w jzyku JavaScript s
obiektami, wic maj waciwoci. Mona by utworzy waciwo

Universe.instance

i to w niej przechowywa obiekt. To eleganckie rozwizanie, ale ma jedn wad: waci-
wo

instance

byaby dostpna publicznie i inny kod mógby j zmieni.

x

Zamknicie instancji w domkniciu. W ten sposób instancja staje si elementem prywatnym
i nie moe zosta zmieniona z zewntrz. Cen tego rozwizania jest dodatkowe domknicie.

Przyjrzyjmy si przykadowym implementacjom drugiej i trzeciej opcji.

Kup książkę

Poleć książkę

background image

Singleton

_ 139

Instancja we waciwoci statycznej

Poniszy kod zapamituje pojedyncz instancj we waciwoci statycznej konstruktora

Universe

.

function Universe() {

// czy istnieje ju instancja?
if (typeof Universe.instance === "object") {
return Universe.instance;
}

// standardowe dziaania
this.start_time = 0;
this.bang = "Wielki";

// zapamitanie instancji
Universe.instance = this;

// niejawna instrukcja return:
// return this;
}

// test
var uni = new Universe();
var uni2 = new Universe();
uni === uni2; // true

To bardzo proste rozwizanie z jedn wad, któr jest publiczne udostpnienie

instance

.

Cho prawdopodobiestwo zmiany takiej waciwoci przez kod jest niewielkie (i na pewno
znaczco mniejsze ni w przypadku zmiennej globalnej), to jednak jest to moliwe.

Instancja w domkniciu

Innym sposobem uzyskania singletonu podobnego do rozwiza klasowych jest uycie
domknicia w celu ochrony instancji. W implementacji mona wykorzysta wzorzec prywat-
nej skadowej statycznej omówiony w rozdziale 5. Tajnym skadnikiem jest nadpisanie kon-
struktora.

function Universe() {

// zapamitanie instancji
var instance = this;

// standardowe dziaania
this.start_time = 0;
this.bang = "Wielki";

// nadpisanie konstruktora
Universe = function () {
return instance;
};
}

// testy
var uni = new Universe();
var uni2 = new Universe();
uni === uni2; // true

Kup książkę

Poleć książkę

background image

140

_

Rozdzia 7. Wzorce projektowe

Za pierwszym razem zostaje wywoany oryginalny konstruktor, który zwraca

this

w sposób

standardowy. Drugie i nastpne wywoania wykonuj ju zmieniony konstruktor, który ma
dostp do zmiennej prywatnej

instance

dziki domkniciu i po prostu j zwraca.

Przedstawiona implementacja jest w zasadzie przykadem wzorca samomodyfikujcej si
funkcji z rozdziau 4. Wad tego rozwizania opisan we wspomnianym rozdziale jest to, e
nadpisana funkcja (w tym przypadku konstruktor

Universe()

) utraci wszystkie waciwoci

dodane midzy jej zdefiniowaniem i nadpisaniem. W tej konkretnej sytuacji nic z tego, co zo-
stanie dodane do prototypu

Universe()

po pierwszym obiekcie, nie bdzie mogo posiada

referencji do instancji utworzonej przez oryginaln implementacj.

Dla uwidocznienia problemu wykonajmy krótki test. Najpierw kilka wierszy przygotowujcych:

// dodanie waciwoci do prototypu
Universe.prototype.nothing = true;

var uni = new Universe();

// ponowne dodanie waciwoci do prototypu
// po utworzeniu pierwszego obiektu
Universe.prototype.everything = true;

var uni2 = new Universe();

Oto waciwy test:

// tylko oryginalny prototyp jest powizany z obiektami
uni.nothing; // true
uni2.nothing; // true
uni.everything; // undefined
uni2.everything; // undefined

// wyglda prawidowo:
uni.constructor.name; // "Universe"

// ale to jest dziwne:
uni.constructor === Universe; // false

Powodem, dla którego waciwo

uni.constructor

nie jest ju taka sama jak konstruktor

Universe()

, jest fakt, i

uni.constructor

nadal wskazuje na oryginalny konstruktor zamiast

przedefiniowanego.

Jeli prototyp i referencja wskazujca na konstruktor musz dziaa prawidowo, do wcze-
niejszej implementacji trzeba wprowadzi kilka poprawek.

function Universe() {

// zapamitanie instancji
var instance;

// nadpisanie konstruktora
Universe = function Universe() {
return instance;
};

// przeniesienie waciwoci prototypu
Universe.prototype = this;

// instancja
instance = new Universe();

// zmiana referencji wskazujcej na konstruktor
instance.constructor = Universe;

Kup książkę

Poleć książkę

background image

Fabryka

_ 141

// waciwa funkcjonalno
instance.start_time = 0;
instance.bang = "Wielki";

return instance;
}

Teraz wszystkie testy powinny dziaa zgodnie z oczekiwaniami.

// aktualizacja prototypu i utworzenie instancji
Universe.prototype.nothing = true; // true
var uni = new Universe();
Universe.prototype.everything = true; // true
var uni2 = new Universe();

// to ta sama pojedyncza instancja
uni === uni2; // true

// wszystkie waciwoci prototypu dziaaj prawidowo
// niezalenie od momentu ich zdefiniowania
uni.nothing && uni.everything && uni2.nothing && uni2.everything; // true
// standardowe waciwoci równie dziaaj prawidowo
uni.bang; // "Wielki"
// referencja wskazujca na konstruktor równie jest prawidowa
uni.constructor === Universe; // true

Alternatywne rozwizanie mogoby polega na otoczeniu konstruktora oraz instancji funkcj
natychmiastow. Pierwsze wywoanie konstruktora tworzy obiekt i zapamituje go w pry-
watnej zmiennej

instance

. Drugie i kolejne wywoania jedynie zwracaj zawarto zmiennej.

Wszystkie poprzednie testy bd dziaay równie dla implementacji przedstawionej poniej.

var Universe;

(function () {

var instance;

Universe = function Universe() {

if (instance) {
return instance;
}

instance = this;

// waciwa funkcjonalno
this.start_time = 0;
this.bang = "Wielki";

};

}());

Fabryka

Celem wzorca fabryki jest tworzenie obiektów. Najczciej fabryk jest klasa lub metoda sta-
tyczna klasy, której celem jest:

x

wykonanie powtarzajcych si operacji przy tworzeniu podobnych obiektów;

x

zapewnienie uytkownikom moliwoci tworzenia obiektów bez potrzeby znania kon-
kretnego typu (klasy) na etapie kompilacji.

Kup książkę

Poleć książkę

background image

142

_

Rozdzia 7. Wzorce projektowe

Drugi punkt ma wiksze znaczenie w przypadku jzyków ze statyczn analiz typów, w któ-
rych to utworzenie instancji klas nieznanych na etapie kompilacji nie jest zadaniem atwym.
Na szczcie w jzyku JavaScript nie trzeba gowi si nad tym zagadnieniem.

Obiekty tworzone przez metod fabryczn z reguy dziedzicz po tym samym przodku, ale
z drugiej strony s wyspecjalizowanymi wersjami z pewnymi dodatkowymi rozwizaniami.
Czasem wspólny przodek to klasa zawierajca metod fabryczn.

Przyjrzyjmy si przykadowej implementacji, która ma:

x

wspólny konstruktor przodka

CarMaker

;

x

metod statyczn

CarMaker

o nazwie

factory()

, która tworzy obiekty samochodów;

x

wyspecjalizowane konstruktory

CarMaker.Compact

,

CarMaker.SUV

i

CarMaker.Convertible

,

które dziedzicz po

CarMaker

i wszystkie s statycznymi waciwociami przodka, dziki

czemu globalna przestrze nazw pozostaje czysta i atwo je w razie potrzeby odnale.

Implementacja bdzie moga by wykorzystywana w nastpujcy sposób:

var corolla = CarMaker.factory('Compact');
var solstice = CarMaker.factory('Convertible');
var cherokee = CarMaker.factory('SUV');
corolla.drive(); // "Brum, mam 4 drzwi"
solstice.drive(); // "Brum, mam 2 drzwi"
cherokee.drive(); // "Brum, mam 17 drzwi"

Fragment

var corolla = CarMaker.factory('Compact');

to prawdopodobnie najbardziej rozpoznawalna cz wzorca fabryki. Metoda przyjmuje typ
jako tekst i na jego podstawie tworzy i zwraca obiekty danego typu. Nie pojawiaj si kon-
struktory wykorzystujce

new

lub literay obiektów — uytkownik stosuje funkcj, która two-

rzy obiekty na podstawie typu wskazanego jako tekst.

Oto przykadowa implementacja wzorca fabryki, która odpowiada wczeniejszemu przyka-
dowi jego uycia:

// konstruktor przodka
function CarMaker() {}

// metoda przodka
CarMaker.prototype.drive = function () {
return "Brum, mam " + this.doors + " drzwi";
};

// statyczna metoda fabryczna
CarMaker.factory = function (type) {
var constr = type,
newcar;

// bd, jeli konstruktor nie istnieje
if (typeof CarMaker[constr] !== "function") {
throw {
name: "Error",
message: constr + " nie istnieje"
};
}

// na tym etapie wiemy, e konstruktor istnieje
// niech odziedziczy przodka, ale tylko raz

Kup książkę

Poleć książkę

background image

Iterator

_ 143

if (typeof CarMaker[constr].prototype.drive !== "function") {
CarMaker[constr].prototype = new CarMaker();
}
// utworzenie nowej instancji
newcar = new CarMaker[constr]();
// opcjonalne wywoanie dodatkowych metod i zwrócenie obiektu...
return newcar;
};

// definicje konkretnych konstruktorów
CarMaker.Compact = function () {
this.doors = 4;
};
CarMaker.Convertible = function () {
this.doors = 2;
};
CarMaker.SUV = function () {
this.doors = 17;
};

W implementacji wzorca fabryki nie ma nic szczególnego. Wystarczy wyszuka odpowiedni
funkcj konstruujc, która utworzy obiekt wymaganego typu. W tym przypadku zastoso-
wano bardzo proste odwzorowanie nazw przekazywanych do fabryki na odpowiadajce im
obiekty. Przykadem powtarzajcych si zada, które warto byoby umieci w fabryce, za-
miast powtarza osobno dla kadego konstruktora, jest dziedziczenie.

Wbudowane fabryki obiektów

W zasadzie jzyk JavaScript posiada wbudowan fabryk, któr jest globalny konstruktor

Object()

. Zachowuje si on jak fabryka, poniewa zwraca róne typy obiektów w zalenoci

od parametru wejciowego. Przekazanie liczby spowoduje utworzenie obiektu konstruktorem

Number()

. Podobnie dzieje si dla tekstów i wartoci logicznych. Wszystkie inne wartoci lub

brak argumentu spowoduj utworzenie zwykego obiektu.

Oto kilka przykadów i testów tego sposobu dziaania. Co wicej,

Object

mona równie

wywoa z takim samym efektem bez uycia

new

.

var o = new Object(),
n = new Object(1),
s = Object('1'),
b = Object(true);

// testy
o.constructor === Object; // true
n.constructor === Number; // true
s.constructor === String; // true
b.constructor === Boolean; // true

To, e

Object()

jest równie fabryk, ma mae znaczenie praktyczne, ale warto o tym

wspomnie, by mie wiadomo, i wzorzec fabryki pojawia si niemal wszdzie.

Iterator

We wzorcu iteratora mamy do czynienia z pewnym obiektem zawierajcym zagregowane
dane. Dane te mog by przechowywane wewntrznie w bardzo zoonej strukturze, ale se-
kwencyjny dostp do nich zapewnia bardzo prosta funkcja. Kod korzystajcy z obiektu nie
musi zna caej zoonoci struktury danych — wystarczy, e wie, jak korzysta z poje-
dynczego elementu i pobra nastpny.

Kup książkę

Poleć książkę

background image

144

_

Rozdzia 7. Wzorce projektowe

We wzorcu iteratora kluczow rol odgrywa metoda

next()

. Kade jej wywoanie powinno

zwraca nastpny element w kolejce. To, jak uoona jest kolejka i jak posortowane s ele-
menty, zaley od zastosowanej struktury danych.

Przy zaoeniu, e obiekt znajduje si w zmiennej

agg

, dostp do wszystkich elementów da-

nych uzyska si dziki wywoywaniu

next()

w ptli:

var element;
while (element = agg.next()) {
// wykonanie dziaa na elemencie...
console.log(element);
}

We wzorcu iteratora bardzo czsto obiekt agregujcy zapewnia dodatkow metod pomocni-
cz

hasNext()

, która informuje uytkownika, czy zosta ju osignity koniec danych. Inny

sposób uzyskania sekwencyjnego dostpu do wszystkich elementów, tym razem z uyciem

hasNext()

, mógby wyglda nastpujco:

while (agg.hasNext()) {
// wykonanie dziaa na nastpnym elemencie...
console.log(agg.next());
}

Po przedstawieniu sposobów uycia wzorca czas na implementacj obiektu agregujcego.

Implementujc wzorzec iteratora, warto w zmiennej prywatnej przechowywa dane oraz
wskanik (indeks) do nastpnego elementu. W naszym przykadzie zaómy, e dane to ty-
powa tablica, a „specjalna” logika pobierania tak naprawd zwraca jej nastpny element.

var agg = (function () {

var index = 0,
data = [1, 2, 3, 4, 5],
length = data.length;

return {

next: function () {
var element;
if (!this.hasNext()) {
return null;
}
element = data[index];
index = index + 2;
return element;
},

hasNext: function () {
return index < length;
}

};
}());

Aby zapewni atwiejszy dostp do danych i moliwo kilkukrotnej iteracji, obiekt moe
oferowa dodatkowe metody:

x

rewind()

— ustawia wskanik na pocztek kolejki;

x

current()

— zwraca aktualny element, bo nie mona tego uczyni za pomoc

next()

bez jednoczesnej zmiany wskanika.

Kup książkę

Poleć książkę

background image

Dekorator

_ 145

Implementacja tych dodatkowych metod nie sprawi adnych trudnoci.

var agg = (function () {

// [jak wyej...]

return {

// [jak wyej...]

rewind: function () {
index = 0;
},
current: function () {
return data[index];
}
};
}());

Oto dodatkowy test iteratora:

// ptla wywietla wartoci 1, 3 i 5
while (agg.hasNext()) {
console.log(agg.next());
}

// powrót na pocztek
agg.rewind();
console.log(agg.current()); // 1

W konsoli pojawi si nastpujce wartoci: 1, 3 i 5 (z ptli), a na kocu ponownie 1 (po
przejciu na pocztek kolejki).

Dekorator

We wzorcu dekoratora dodatkow funkcjonalno mona dodawa do obiektu dynamicznie
w trakcie dziaania programu. W przypadku korzystania ze statycznych i niezmiennych klas
jest to faktycznie due wyzwanie. W jzyku JavaScript obiekty mona modyfikowa, wic
dodanie do nich nowej funkcjonalnoci nie stanowi wielkiego problemu.

Dodatkow cech wzorca dekoratora jest atwo dostosowania i konfiguracji jego oczekiwa-
nego zachowania. Zaczyna si od prostego obiektu z podstawow funkcjonalnoci. Nastp-
nie wybiera si kilka z zestawu dostpnych dekoratorów, po czym rozszerza si nimi pod-
stawowy obiekt. Czasem istotna jest kolejno tego rozszerzania.

Sposób uycia

Przyjrzyjmy si sposobom uycia tego wzorca. Przypumy, e opracowujemy aplikacj, która
co sprzedaje. Kada nowa sprzeda to nowy obiekt

sale

. Obiekt zna cen produktu i potrafi

j zwróci po wywoaniu metody

sale.getPrice()

. W zalenoci od aktualnych warunków

mona zacz „dekorowa” obiekt dodatkow funkcjonalnoci. Wyobramy sobie, e jako
amerykaski sklep sprzedajemy produkt klientowi z kanadyjskiej prowincji Québec. W takiej
sytuacji klient musi zapaci podatek federalny i dodatkowo podatek lokalny. We wzorcu
dekoratora bdziemy wic „dekorowali” obiekt dekoratorem podatku federalnego i dekora-
torem podatku lokalnego. Po wyliczeniu ceny kocowej mona równie doda dekorator do
jej formatowania. Scenariusz byby nastpujcy:

Kup książkę

Poleć książkę

background image

146

_

Rozdzia 7. Wzorce projektowe

var sale = new Sale(100); // cena wynosi 100 dolarów
sale = sale.decorate('fedtax'); // dodaj podatek federalny
sale = sale.decorate('quebec'); // dodaj podatek lokalny
sale = sale.decorate('money'); // formatowanie ceny
sale.getPrice(); // "USD 112.88"

W innym scenariuszu kupujcy moe mieszka w prowincji, która nie stosuje podatku lokal-
nego, i dodatkowo moemy chcie poda cen w dolarach kanadyjskich.

var sale = new Sale(100); // cena wynosi 100 dolarów
sale = sale.decorate('fedtax'); // dodaj podatek federalny
sale = sale.decorate('cdn'); // sformatuj jako dolary kanadyjskie
sale.getPrice(); // "CAD 105.00"

Nietrudno zauway, e jest to wygodny i elastyczny sposób dodawania lub modyfikowania
funkcjonalnoci utworzonych ju obiektów. Czas na implementacj wzorca.

Implementacja

Jednym ze sposobów implementacji wzorca dekoratora jest utworzenie dekoratorów jako
obiektów zawierajcych metody do nadpisania. Kady dekorator dziedziczy wówczas tak
naprawd po obiekcie rozszerzonym przez poprzedni dekorator. Kada dekorowana metoda
wywouje swoj poprzedniczk za pomoc

uber

(odziedziczony obiekt), pobiera warto

i przetwarza j, dodajc co nowego.

Efekt jest taki, e wywoanie metody

sale.getPrice()

z pierwszego z przedstawionych

przykadów powoduje tak naprawd wywoanie metody dekoratora

money

(patrz rysunek 7.1).

Poniewa jednak kady dekorator wywouje najpierw odpowiadajc mu metod ze swego
poprzednika,

getPrice()

z

money

wywouje

getPrice()

z

quebec

, a ta metod

getPrice()

z

fedtax

i tak dalej. acuch moe by duszy, ale koczy si oryginaln metod

getPrice()

zaimplementowan przez konstruktor

Sale()

.

Rysunek 7.1. Implementacja wzorca dekoratora

Kup książkę

Poleć książkę

background image

Dekorator

_ 147

Implementacja rozpoczyna si od konstruktora i metody prototypu.

function Sale(price) {
this.price = price || 100;
}
Sale.prototype.getPrice = function () {
return this.price;
};

Wszystkie obiekty dekoratorów znajd si we waciwoci konstruktora:

Sale.decorators = {};

Przyjrzyjmy si przykadowemu dekoratorowi. To obiekt implementujcy zmodyfikowan
wersj metody

getPrice()

. Metoda najpierw pobiera zwrócon przez metod przodka warto,

a nastpnie j modyfikuje.

Sale.decorators.fedtax = {
getPrice: function () {
var price = this.uber.getPrice();
price += price * 5 / 100;
return price;
}
};

W podobny sposób mona zaimplementowa dowoln liczb innych dekoratorów. Mog one
stanowi rozszerzenie podstawowej funkcjonalnoci

Sale()

, czyli dziaa jak dodatki. Co wicej,

nic nie stoi na przeszkodzie, by znajdoway si w dodatkowych plikach i byy implementowane
przez innych, niezalenych programistów.

Sale.decorators.quebec = {
getPrice: function () {
var price = this.uber.getPrice();
price += price * 7.5 / 100;
return price;
}
};

Sale.decorators.money = {
getPrice: function () {
return "USD " + this.uber.getPrice().toFixed(2);
}
};

Sale.decorators.cdn = {
getPrice: function () {
return "CAD " + this.uber.getPrice().toFixed(2);
}
};

Na koniec przyjrzyjmy si „magicznej” metodzie o nazwie

decorate()

, która czy ze sob

wszystkie elementy. Sposób jej uycia jest nastpujcy:

sale = sale.decorate('fedtax');

Tekst

'fedtax'

odpowiada obiektowi zaimplementowanemu w

Sale.decorators.fedtax

.

Nowy obiekt

newobj

dziedziczy obiekt aktualny (orygina lub ju udekorowan wersj), któ-

ry jest zawarty w

this

. Do zapewnienia dziedziczenia wykorzystajmy wzorzec konstruktora

tymczasowego z poprzedniego rozdziau. Dodatkowo ustawmy waciwo

uber

obiektu

newobj

, by potomek mia dostp do przodka. Nastpnie niech kod kopiuje wszystkie wa-

ciwoci z dekoratora do nowego obiektu i zwraca

newobj

jako wynik caej operacji, co spo-

woduje, e stanie si on nowym obiektem

sale

.

Kup książkę

Poleć książkę

background image

148

_

Rozdzia 7. Wzorce projektowe

Sale.prototype.decorate = function (decorator) {
var F = function () {},
overrides = this.constructor.decorators[decorator],
i, newobj;
F.prototype = this;
newobj = new F();
newobj.uber = F.prototype;
for (i in overrides) {
if (overrides.hasOwnProperty(i)) {
newobj[i] = overrides[i];
}
}
return newobj;
};

Implementacja wykorzystujca list

Przeanalizujmy inn implementacj, która korzysta z dynamicznej natury jzyka JavaScript
i w ogóle nie stosuje dziedziczenia. Dodatkowo, zamiast wymusza na kadej metodzie de-
korujcej, by wywoywaa swoj poprzedniczk, przekazujemy tu wynik poprzedniej metody
jako parametr nastpnej.

Taka implementacja znaczco uatwia wycofanie udekorowania, czyli usunicie jednego
z elementów z listy dekoratorów.

Sposób uycia nowej implementacji bdzie prostszy, bo nie wymaga ona przypisywania
wartoci zwróconej przez

decorate()

. W tym przypadku

decorate()

jedynie dodaje nowy

element do listy:

var sale = new Sale(100); // cena wynosi 100 dolarów
sale.decorate('fedtax'); // dodaj podatek federalny
sale.decorate('quebec'); // dodaj podatek lokalny
sale.decorate('money'); // formatowanie ceny
sale.getPrice(); // "USD 112.88"

Tym razem konstruktor

Sale()

zawiera list dekoratorów jako wasn waciwo.

function Sale(price) {
this.price = (price > 0) || 100;
this.decorators_list = [];
}

Dostpne dekoratory s ponownie implementowane jako waciwoci

Sale.decorators

.

S prostsze, bo nie musz ju wywoywa poprzedniej wersji metody

getPrice()

, by uzy-

ska warto poredni. Teraz trafia ona do systemu jako parametr.

Sale.decorators = {};

Sale.decorators.fedtax = {
getPrice: function (price) {
return price + price * 5 / 100;
}
};

Sale.decorators.quebec = {
getPrice: function (price) {
return price + price * 7.5 / 100;
}
};

Kup książkę

Poleć książkę

background image

Strategia

_ 149

Sale.decorators.money = {
getPrice: function (price) {
return "USD " + price.toFixed(2);
}
};

Interesujce konstrukcje pojawiaj si w metodach

decorate()

i

getPrice()

oryginalnego

obiektu. W poprzedniej implementacji metoda

decorate()

bya w miar zoona, a

getPrice()

niezwykle prosta. W nowej jest dokadnie odwrotnie —

decorate()

po prostu dodaje nowy

element do listy, a

getPrice()

wykonuje ca istotn prac. Prac t jest przejcie przez list

wszystkich dodanych dekoratorów i wywoanie dla kadego z nich metody

getPrice()

z po-

przedni wartoci podan jako argument metody.

Sale.prototype.decorate = function (decorator) {
this.decorators_list.push(decorator);
};

Sale.prototype.getPrice = function () {
var price = this.price,
i,
max = this.decorators_list.length,
name;
for (i = 0; i < max; i += 1) {
name = this.decorators_list[i];
price = Sale.decorators[name].getPrice(price);
}
return price;
};

Druga implementacja jest prostsza i nie korzysta z dziedziczenia. Prostsze s równie metody
dekorujce. Ca rzeczywist prac wykonuje metoda, która „zgadza” si na dekoracj. W tej
prostej implementacji dekoracj dopuszcza jedynie metoda

getPrice()

. Jeli dekoracja mia-

aby dotyczy wikszej liczby metod, kada z nich musiaaby przej przez list dekoratorów
i wywoa odpowiednie metody. Oczywicie taki kod stosunkowo atwo jest umieci
w osobnej metodzie pomocniczej i uogólni. Umoliwiaby on dodanie dekorowalnoci do
dowolnej metody. Co wicej, w takiej implementacji waciwo

decorators_list

byaby

obiektem z waciwociami o nazwach metod i z tablicami dekorowanych obiektów jako
wartociami.

Strategia

Wzorzec strategii umoliwia wybór odpowiedniego algorytmu na etapie dziaania aplikacji.
Uytkownicy kodu mog stosowa ten sam interfejs zewntrzny, ale wybiera sporód kilku
dostpnych algorytmów, by lepiej dopasowa implementacj do aktualnego kontekstu.

Przykadem wzorca strategii moe by rozwizywanie problemu walidacji formularzy. Mona
utworzy jeden obiekt sprawdzania z metod

validate()

. Metoda zostanie wywoana nie-

zalenie od rodzaju formularza i zawsze zwróci ten sam wynik — list danych, które nie s
poprawne, wraz z komunikatami o bdach.

W zalenoci od sprawdzanych danych i typu formularza uytkownik kodu moe wybra
róne rodzaje sprawdze. Walidator wybiera najlepsz strategi wykonania zadania i dele-
guje konkretne czynnoci sprawdze do odpowiednich algorytmów.

Kup książkę

Poleć książkę

background image

150

_

Rozdzia 7. Wzorce projektowe

Przykad walidacji danych

Przypumy, e mamy do czynienia z nastpujcym zestawem danych pochodzcym naj-
prawdopodobniej z formularza i e chcemy go sprawdzi pod ktem poprawnoci:

var data = {
first_name: "Super",
last_name: "Man",
age: "unknown",
username: "o_O"
};

Aby walidator zna najlepsz strategi do zastosowania w tym konkretnym przykadzie, trzeba
najpierw go skonfigurowa, okrelajc zestaw regu i wartoci uznawanych za prawidowe.

Przypumy, e nie wymagamy podania nazwiska i zaakceptujemy dowoln warto imienia,
ale wymagamy podania wieku jako liczby i nazwy uytkownika, która skada si tylko z liczb
i liter bez znaków specjalnych. Konfiguracja mogaby wyglda nastpujco:

validator.config = {
first_name: 'isNonEmpty',
age: 'isNumber',
username: 'isAlphaNum'
};

Po skonfigurowaniu obiektu

validator

jest on gotowy do przyjcia danych. Wywoujemy

jego metod

validate()

i wywietlamy bdy walidacji w konsoli.

validator.validate(data);
if (validator.hasErrors()) {
console.log(validator.messages.join("\n"));
}

Efektem wykonania kodu mógby by nastpujcy komunikat:

Niepoprawna warto *age*; warto musi by liczb, na przykad 1, 3.14 lub 2010
Niepoprawna warto *username*; warto musi zawiera jedynie litery i cyfry bez
adnych znaków specjalnych

Przyjrzyjmy si implementacji walidatora. Poszczególne algorytmy s obiektami o z góry
ustalonym interfejsie — zawieraj metod

validate()

i jednowierszow informacj wyko-

rzystywan jako komunikat o bdzie.

// sprawdzenie, czy podano jak warto
validator.types.isNonEmpty = {
validate: function (value) {
return value !== "";
},
instructions: "warto nie moe by pusta"
};

// sprawdzenie, czy warto jest liczb
validator.types.isNumber = {
validate: function (value) {
return !isNaN(value);
},
instructions: "warto musi by liczb, na przykad 1, 3.14 lub 2010"
};

// sprawdzenie, czy warto zawiera jedynie litery i cyfry
validator.types.isAlphaNum = {
validate: function (value) {

Kup książkę

Poleć książkę

background image

Strategia

_ 151

return !/[^a-z0-9]/i.test(value);
},
instructions: "warto musi zawiera jedynie litery i cyfry bez adnych znaków

´specjalnych"

};

Najwyszy czas na obiekt

validator

:

var validator = {

// wszystkie dostpne sprawdzenia
types: {},

// komunikaty o bdach
// z aktualnej sesji walidacyjnej
messages: [],

// aktualna konfiguracja walidacji
// nazwa => rodzaj testu
config: {},

// metoda interfejsu
// data to pary klucz-warto
validate: function (data) {

var i, msg, type, checker, result_ok;

// usunicie wszystkich komunikatów
this.messages = [];

for (i in data) {

if (data.hasOwnProperty(i)) {

type = this.config[i];
checker = this.types[type];

if (!type) {
continue; // nie trzeba sprawdza
}
if (!checker) { // ojej
throw {
name: "ValidationError",
message: "Brak obsugi dla klucza " + type
};
}

result_ok = checker.validate(data[i]);
if (!result_ok) {
msg = "Niepoprawna warto *" + i + "*; " + checker.instructions;
this.messages.push(msg);
}
}
}
return this.hasErrors();
},

// metoda pomocnicza
hasErrors: function () {
return this.messages.length !== 0;
}
};

Kup książkę

Poleć książkę

background image

152

_

Rozdzia 7. Wzorce projektowe

Obiekt

validator

jest uniwersalny i bdzie dziaa prawidowo dla rónych rodzajów spraw-

dze. Jednym z usprawnie mogoby by dodanie kilku nowych testów. Po wykonaniu kilku
rónych formularzy z walidacj Twoja lista dostpnych sprawdze z pewnoci si wyduy.
Kady kolejny formularz bdzie wymaga jedynie skonfigurowania walidatora i uruchomie-
nia metody

validate()

.

Fasada

Wzorzec fasady jest bardzo prosty i ma za zadanie zapewni alternatywny interfejs obiektu.
Dobr praktyk jest stosowanie krótkich metod, które nie wykonuj zbyt wielu zada. Stosujc
to podejcie, uzyskuje si znacznie wicej metod ni w przypadku tworzenia supermetod z wie-
loma parametrami. W wikszoci sytuacji dwie lub wicej metod wykonuje si jednoczenie.
Warto wtedy utworzy jeszcze jedn metod, która stanowi otoczk dla takich pocze.

W trakcie obsugi zdarze przegldarki bardzo czsto korzysta si z nastpujcych metod:

x

stopPropagation()

— zapobiega wykonywaniu obsugi zdarzenia w wzach nadrzdnych;

x

preventDefault()

— zapobiega wykonaniu przez przegldark domylnej akcji dla zda-

rzenia (na przykad kliknicia cza lub wysania formularza).

To dwie osobne metody wykonujce odmienne zadania, wic nie stanowi jednej caoci, ale z dru-
giej strony w zdecydowanej wikszoci sytuacji s one wykonywane jednoczenie. Zamiast wic
powiela wywoania obu metod w caej aplikacji, mona utworzy fasad, która je obie wykona.

var myevent = {
// ...
stop: function (e) {
e.preventDefault();
e.stopPropagation();
}
// ...
};

Wzorzec fasady przydaje si równie w sytuacjach, w których za fasad warto ukry rónice
pomidzy przegldarkami internetowymi. Nic nie stoi na przeszkodzie, by rozbudowa po-
przedni przykad o inny sposób obsugi anulowania zdarze przez przegldark IE.

var myevent = {
// ...
stop: function (e) {
// inne
if (typeof e.preventDefault === "function") {
e.preventDefault();
}
if (typeof e.stopPropagation === "function") {
e.stopPropagation();
}
// IE
if (typeof e.returnValue === "boolean") {
e.returnValue = false;
}
if (typeof e.cancelBubble === "boolean") {
e.cancelBubble = true;
}
}
// ...
};

Kup książkę

Poleć książkę

background image

Porednik

_ 153

Wzorzec fasady bywa pomocny w przypadku zmiany interfejsów zwizanej na przykad
z refaktoryzacj. Gdy chce si zamieni obiekt na inn implementacj, najczciej moe to
zaj sporo czasu (jeli jest on naprawd rozbudowany). Zaómy te, e ju powstaje kod
dla nowego interfejsu. W takiej sytuacji mona utworzy przed starym obiektem fasad, która
imituje nowy interfejs. W ten sposób po dokonaniu rzeczywistej zamiany i pozbyciu si starego
obiektu ilo zmian w najnowszym kodzie zostanie ograniczona do minimum.

Porednik

We wzorcu projektowym porednika jeden obiekt stanowi interfejs dla innego obiektu. Róni
si to od wzorca fasady, w którym po prostu istniej pewne metody dodatkowe czce
w sobie wywoania kilku innych metod. Porednik znajduje si midzy uytkownikiem
a obiektem i broni dostpu do niego.

Cho wzorzec wyglda jak dodatkowy narzut, w rzeczywistoci czsto suy do poprawy
wydajnoci. Porednik staje si stranikiem rzeczywistego obiektu i stara si, by ten wykona
jak najmniej pracy.

Jednym z przykadów zastosowania porednika jest tak zwana leniwa inicjalizacja. Stosuje
si j w sytuacjach, w których inicjalizacja rzeczywistego obiektu jest kosztowna, a istnieje
spora szansa, e klient po jego zainicjalizowaniu tak naprawd nigdy go nie uyje. Porednik
moe wtedy stanowi interfejs dla rzeczywistego obiektu. Otrzymuje polecenie inicjalizacji,
ale nie przekazuje go dalej a do momentu, gdy rzeczywisty obiekt naprawd zostanie uyty.

Rysunek 7.2 ilustruje sytuacj, w której klient wysya polecenie inicjalizujce, a porednik
odpowiada, e wszystko jest w porzdku, cho tak naprawd nie przekazuje polecenia dalej.
Czeka z inicjalizacj waciwego obiektu do czasu, gdy klient rzeczywicie bdzie wykony-
wa na nim prac — wówczas przekazuje obydwa komunikaty.

Rysunek 7.2. Komunikacja midzy klientem i rzeczywistym obiektem z wykorzystaniem porednika

Przykad

Wzorzec porednika bywa przydatny, gdy rzeczywisty obiekt docelowy wykonuje kosztow-
ne zadanie. W aplikacjach internetowych jedn z kosztownych sytuacji jest danie sieciowe,
wic w miar moliwoci warto zebra kilka operacji i wykona je jednym daniem. Prze-
ledmy praktyczne zastosowanie wzorca wanie w takiej sytuacji.

Kup książkę

Poleć książkę

background image

154

_

Rozdzia 7. Wzorce projektowe

Aplikacja wideo

Zaómy istnienie prostej aplikacji odtwarzajcej materia wideo wybranego artysty (patrz
rysunek 7.3). W zasadzie moesz nawet przetestowa kod, wpisujc w przegldarce interne-
towej adres http://www.jspatterns.com/book/7/proxy.html.

Rysunek 7.3. Aplikacja wideo w akcji

Strona zawiera list tytuów materiaów wideo. Gdy uytkownik kliknie tytu, obszar poniej
rozszerzy si, by przedstawi dodatkowe informacje i umoliwi odtworzenie filmu. Szcze-
góy dotyczce materiaów oraz adres URL treci wideo nie stanowi czci strony — s po-
bierane poprzez osobne wywoania serwera. Serwer przyjmuje jako parametr kilka identyfi-
katorów materiaów wideo, wic aplikacj mona przyspieszy, wykonujc mniej da
HTTP i pobierajc za kadym razem dane kilku filmów.

Aplikacja umoliwia jednoczesne rozwinicie szczegóów kilku (a nawet wszystkich) mate-
riaów, co stanowi doskona okazj do poczenia kilku da w jedno.

Bez uycia porednika

Gównymi elementami aplikacji s dwa obiekty:

x

videos

— jest odpowiedzialny za rozwijanie i zwijanie obszarów informacyjnych (metoda

videos.getInfo()

) oraz za odtwarzanie materiaów wideo (metoda

videos.getPlayer()

).

x

http

— jest odpowiedzialny za komunikacj z serwerem za pomoc metody

http.make

´Request()

.

Kup książkę

Poleć książkę

background image

Porednik

_ 155

Bez stosowania porednika

videos.getInfo()

wywoa

http.makeRequest()

dla kadego

materiau wideo. Po dodaniu porednika pojawi si nowy obiekt o nazwie

proxy

znajdujcy

si midzy

videos

oraz

http

i delegujcy wszystkie wywoania

makeRequest()

, a take -

czcy je ze sob.

Najpierw pojawi si kod, w którym nie zastosowano wzorca porednika. Druga wersja, sto-
sujca obiekt porednika, poprawi ogóln pynno dziaania aplikacji.

Kod HTML

Kod HTML to po prostu zbiór czy.

<p><span id="toggle-all">Przecz zaznaczone</span></p>
<ol id="vids">
<li><input type="checkbox" checked><a
href="http://new.music.yahoo.com/videos/--2158073">Gravedigger</a></li>
<li><input type="checkbox" checked><a
href="http://new.music.yahoo.com/videos/--4472739">Save Me</a></li>
<li><input type="checkbox" checked><a
href="http://new.music.yahoo.com/videos/--45286339">Crush</a></li>
<li><input type="checkbox" checked><a
href="http://new.music.yahoo.com/videos/--2144530">Don't Drink The Water

´</a></li>

<li><input type="checkbox" checked><a
href="http://new.music.yahoo.com/videos/--217241800">Funny the Way It Is

´</a></li>

<li><input type="checkbox" checked><a
href="http://new.music.yahoo.com/videos/--2144532">What Would You Say</a></li>
</ol>

Obsuga zdarze

Zanim pojawi si waciwa obsuga zdarze, warto doda funkcj pomocnicz

$

do pobiera-

nia elementów DOM na podstawie ich identyfikatorów.

var $ = function (id) {
return document.getElementById(id);
};

Stosujc delegacj zdarze (wicej na ten temat w rozdziale 8.), mona obsuy wszystkie
kliknicia dotyczce listy uporzdkowanej

id="vids"

za pomoc jednej funkcji.

$('vids').onclick = function (e) {
var src, id;

e = e || window.event;
src = e.target || e.srcElement;

if (src.nodeName !== "A") {
return;
}

if (typeof e.preventDefault === "function") {
e.preventDefault();
}
e.returnValue = false;

id = src.href.split('--')[1];

if (src.className === "play") {

Kup książkę

Poleć książkę

background image

156

_

Rozdzia 7. Wzorce projektowe

src.parentNode.innerHTML = videos.getPlayer(id);
return;
}

src.parentNode.id = "v" + id;
videos.getInfo(id);
};

Obsuga kliknicia zainteresowana jest tak naprawd dwoma sytuacjami: pierwsz dotyczc
rozwinicia lub zamknicia czci informacyjnej (wywoanie

getInfo()

) i drug zwizan

z odtworzeniem materiau wideo (gdy kliknicie dotyczyo obiektu z klas

play

), co oznacza,

e rozwinicie ju nastpio i mona bezpiecznie wywoa metod

getPlayer()

. Identyfika-

tory materiaów wideo wydobywa si z atrybutów

href

czy.

Druga z funkcji obsugujcych kliknicia dotyczy sytuacji, w której uytkownik chce prze-
czy wszystkie czci informacyjne. W zasadzie sprowadza si ona do wywoywania w ptli
metody

getInfo()

.

$('toggle-all').onclick = function (e) {

var hrefs,
i,
max,
id;

hrefs = $('vids').getElementsByTagName('a');
for (i = 0, max = hrefs.length; i < max; i += 1) {
// pomi cza odtwarzania
if (hrefs[i].className === "play") {
continue;
}
// pomi niezaznaczone
if (!hrefs[i].parentNode.firstChild.checked) {
continue;
}

id = hrefs[i].href.split('--')[1];
hrefs[i].parentNode.id = "v" + id;
videos.getInfo(id);
}
};

Obiekt videos

Obiekt

videos

zawiera trzy metody:

x

getPlayer()

— zwraca kod HTML niezbdny do odtworzenia materiau wideo (nie-

istotny w rozwaaniach na temat obiektu porednika).

x

updateList()

— wywoanie zwrotne otrzymujce wszystkie dane z serwera i generujce

kod HTML do wykorzystania przy rozwijaniu szczegóów filmów (w tej metodzie rów-
nie nie dzieje si nic interesujcego).

x

getInfo()

— metoda przeczajca widoczno czci informacyjnych i wykonu-

jca metody obiektu

http

przez przekazanie

updateList()

jako funkcji wywoania

zwrotnego.

Kup książkę

Poleć książkę

background image

Porednik

_ 157

Oto istotny fragment obiektu

videos

:

var videos = {

getPlayer: function (id) {...},
updateList: function (data) {...},

getInfo: function (id) {

var info = $('info' + id);

if (!info) {
http.makeRequest([id], "videos.updateList");
return;
}

if (info.style.display === "none") {
info.style.display = '';
} else {
info.style.display = 'none';
}
}
};

Obiekt http

Obiekt

http

ma tylko jedn metod, która wykonuje danie JSONP do usugi YQL firmy Yahoo.

var http = {
makeRequest: function (ids, callback) {
var url = 'http://query.yahooapis.com/v1/public/yql?q=',
sql = 'select * from music.video.id where ids IN ("%ID%")',
format = "format=json",
handler = "callback=" + callback,
script = document.createElement('script');

sql = sql.replace('%ID%', ids.join('","'));
sql = encodeURIComponent(sql);

url += sql + '&' + format + '&' + handler;
script.src = url;

document.body.appendChild(script);
}
};

YQL (Yahoo! Query Language) to uogólniona usuga internetowa, która oferuje mo-
liwo korzystania ze skadni przypominajcej SQL do pobierania danych z innych
usug. W ten sposób nie trzeba poznawa szczegóów ich API.

Gdy jednoczenie przeczone zostan wszystkie materiay wideo, do serwera trafi sze
osobnych da; kade bdzie podobne do nastpujcego dania YQL:

select * from music.video.id where ids IN ("2158073")

Obiekt proxy

Zaprezentowany wczeniej kod dziaa prawidowo, ale mona go zoptymalizowa. Na scen
wkracza obiekt

proxy

, który przejmuje komunikacj midzy

http

i

videos

. Obiekt stara si

poczy ze sob kilka da, czekajc na ich zebranie 50 ms. Obiekt

videos

nie wywouje

Kup książkę

Poleć książkę

background image

158

_

Rozdzia 7. Wzorce projektowe

usugi HTTP bezporednio, ale przez porednika. Ten czeka krótk chwil z wysaniem -
dania. Jeli wywoania z

videos

bd przychodziy w odstpach krótszych ni 50 ms, zostan

poczone w jedno danie. Takie opónienie nie jest szczególnie widoczne, ale pomaga zna-
czco przyspieszy dziaanie aplikacji w przypadku jednoczesnego odsaniania wicej ni
jednego materiau wideo. Co wicej, jest równie przyjazne dla serwera, który nie musi ob-
sugiwa sporej liczby da.

Zapytanie YQL dla dwóch materiaów wideo moe mie posta:

select * from music.video.id where ids IN ("2158073", "123456")

W istniejcym kodzie zachodzi tylko jedna zmiana: metoda

videos.getInfo()

wywouje

metod

proxy.makeRequest()

zamiast metody

http.makeRequest()

.

proxy.makeRequest(id, videos.updateList, videos);

Obiekt porednika korzysta z kolejki, w której gromadzi identyfikatory materiaów wideo przeka-
zane w ostatnich 50 ms. Nastpnie przekazuje wszystkie identyfikatory, wywoujc metod obiektu

http

i przekazujc wasn funkcj wywoania zwrotnego, poniewa

videos.updateList()

potrafi

przetworzy tylko pojedynczy rekord danych.

Oto kod obiektu poredniczcego

proxy

:

var proxy = {
ids: [],
delay: 50,
timeout: null,
callback: null,
context: null,
makeRequest: function (id, callback, context) {

// dodanie do kolejki
this.ids.push(id);

this.callback = callback;
this.context = context;

// ustawienie funkcji czasowej
if (!this.timeout) {
this.timeout = setTimeout(function () {
proxy.flush();
}, this.delay);
}
},
flush: function () {

http.makeRequest(this.ids, "proxy.handler");

// wyczyszczenie kolejki i funkcji czasowej
this.timeout = null;
this.ids = [];
},
handler: function (data) {
var i, max;

// pojedynczy materia wideo
if (parseInt(data.query.count, 10) === 1) {
proxy.callback.call(proxy.context, data.query.results.Video);
return;
}

Kup książkę

Poleć książkę

background image

Porednik

_ 159

// kilka materiaów wideo
for (i = 0, max = data.query.results.Video.length; i < max; i += 1) {
proxy.callback.call(proxy.context, data.query.results.Video[i]);
}
}
};

Wprowadzenie porednika umoliwio poczenie kilku da pobrania danych w jedno po-
przez zmian tylko jednego wiersza oryginalnego kodu.

Rysunki 7.4 i 7.5 przedstawiaj scenariusze z trzema osobnymi daniami (bez porednika)
i z jednym poczonym daniem (po uyciu porednika).

Rysunek 7.4. Trzy osobne dania do serwera

Rysunek 7.5. Wykorzystanie porednika do zmniejszenia liczby da wysyanych do serwera

Porednik jako pami podrczna

W prezentowanym przykadzie obiekt

videos

dajcy danych jest na tyle inteligentny, e

nie da tych samych informacji dwukrotnie. Nie zawsze jednak musi tak by. Porednik
moe pój o krok dalej i chroni rzeczywisty obiekt

http

przed powielaniem tych samych

da, zapamitujc je w nowej waciwoci

cache

(patrz rysunek 7.6). Gdyby obiekt

videos

ponownie poprosi o informacje o tym samym materiale (ten sam identyfikator), porednik
wydobyby dane z pamici podrcznej i unikn komunikacji z serwerem.

Rysunek 7.6. Pami podrczna w obiekcie porednika

Kup książkę

Poleć książkę

background image

160

_

Rozdzia 7. Wzorce projektowe

Mediator

Aplikacje — due czy mae — skadaj si z wielu obiektów. Obiekty musz si ze sob ko-
munikowa w sposób, który nie uczyni przyszej konserwacji kodu prawdziw drog przez
mk i umoliwi bezpieczn zmian jednego fragmentu bez potrzeby przepisywania wszyst-
kich innych. Gdy aplikacja si rozrasta, pojawiaj si coraz to nowe obiekty. W trakcie refak-
toryzacji obiekty usuwa si lub przerabia. Gdy wiedz o sobie za duo i komunikuj si
bezporednio (wywouj si wzajemnie i modyfikuj waciwoci), powstaje midzy nimi
niepodany cisy zwizek. Jeli obiekty s ze sob powizane zbyt mocno, nieatwo zmie-
ni jeden z nich bez modyfikacji pozostaych. Wtedy nawet najprostsza zmiana w aplikacji
nie jest duej trywialna i bardzo trudno oszacowa, ile tak naprawd czasu trzeba bdzie
na ni powici.

Wzorzec mediatora ma za zadanie promowa lune powizania obiektów i wspomóc przy-
sz konserwacj kodu (patrz rysunek 7.7). W tym wzorcu niezalene obiekty (koledzy) nie
komunikuj si ze sob bezporednio, ale korzystaj z obiektu mediatora. Gdy jeden z kole-
gów zmieni stan, informuje o tym mediator, a ten przekazuje t informacj wszystkim innym
zainteresowanym kolegom.

Rysunek 7.7. Uczestnicy wzorca mediatora

Przykad mediatora

Przeledmy przykad uycia wzorca mediatora. Aplikacja bdzie gr, w której dwóch gra-
czy przez pó minuty stara si jak najczciej klika w przycisk. Pierwszy gracz naciska kla-
wisz nr 1, a drugi klawisz 0 (spory odstp midzy klawiszami zapewnia, e nie pobij si
o klawiatur). Tablica wyników pokazuje aktualny stan rywalizacji.

Obiektami uczestniczcymi w wymianie informacji s:

x

pierwszy gracz,

x

drugi gracz,

x

tablica,

x

mediator.

Kup książkę

Poleć książkę

background image

Mediator

_ 161

Mediator wie o wszystkich obiektach. Komunikuje si z urzdzeniem wejciowym (klawiatur),
obsuguje nacinicia klawiszy, okrela, który gracz jest aktywny, i informuje o zmianach
wyników (patrz rysunek 7.8). Gracz jedynie gra (czyli aktualizuje swój wasny wynik) i in-
formuje mediator o tym zdarzeniu. Mediator informuje tablic o zmianie wyniku, a ta aktu-
alizuje wywietlan warto.

Rysunek 7.8. Uczestnicy w grze na szybko naciskania klawiszy

Poza mediatorem aden inny obiekt nie wie nic o pozostaych. Dziki temu bardzo atwo
zaktualizowa gr, na przykad doda nowego gracza lub zmieni tablic wyników na wersj
wywietlajc pozostay czas.

Pena wersja gry wraz z kodem ródowym jest dostpna pod adresem http://www.jspatterns.com/
book/7/mediator.html
.

Obiekty graczy s tworzone przy uyciu konstruktora

Player()

i zawieraj wasne waci-

woci

points

i

name

. Metoda

play()

z prototypu zwiksza liczb punktów o jeden i infor-

muje o tym fakcie mediator.

function Player(name) {
this.points = 0;
this.name = name;
}
Player.prototype.play = function () {
this.points += 1;
mediator.played();
};

Obiekt

scoreboard

zawiera metod

update()

wywoywan przez mediator po zdobyciu

punktu przez jednego z graczy. Tablica nie wie nic o graczach i nie przechowuje wyniku —
po prostu wywietla informacje przekazane przez mediator.

var scoreboard = {

// aktualizowany element HTML
element: document.getElementById('results'),

// aktualizacja wywietlacza
update: function (score) {

var i, msg = '';
for (i in score) {
if (score.hasOwnProperty(i)) {

Kup książkę

Poleć książkę

background image

162

_

Rozdzia 7. Wzorce projektowe

msg += '<p><strong>' + i + '<\/strong>: ';
msg += score[i];
msg += '<\/p>';
}
}
this.element.innerHTML = msg;
}
};

Czas na obiekt mediatora. Odpowiada on za inicjalizacj gry oraz utworzenie obiektów graczy
w metodzie

setup()

i ledzenie ich poczyna dziki umieszczeniu ich we waciwoci

players

.

Metoda

played()

zostaje wywoana przez kadego z graczy po wykonaniu akcji. Aktualizuje ona

wynik (

score

) i przesya go do tablicy (

scoreboard

). Ostatnia metoda,

keypress()

, obsuguje

zdarzenia klawiatury, okrela, który gracz jest aktywny, i powiadamia go o wykonanej akcji.

var mediator = {

// wszyscy gracze
players: {},

// inicjalizacja
setup: function () {
var players = this.players;
players.home = new Player('Gospodarze');
players.guest = new Player('Gocie');
},

// kto zagra, uaktualnij wynik
played: function () {
var players = this.players,
score = {
"Gospodarze": players.home.points,
"Gocie": players.guest.points
};

scoreboard.update(score);
},

// obsuga interakcji z uytkownikiem
keypress: function (e) {
e = e || window.event; // IE
if (e.which === 49) { // klawisz "1"
mediator.players.home.play();
return;
}
if (e.which === 48) { // klawisz "0"
mediator.players.guest.play();
return;
}
}
};

Ostatni element to uruchomienie i zakoczenie gry.

// start!
mediator.setup();
window.onkeypress = mediator.keypress;

// gra koczy si po 30 sekundach
setTimeout(function () {
window.onkeypress = null;
alert('Koniec gry!');
}, 30000);

Kup książkę

Poleć książkę

background image

Obserwator

_ 163

Obserwator

Wzorzec obserwatora jest niezwykle czsto wykorzystywany w programowaniu po stronie
klienta w jzyku JavaScript. Wszystkie zdarzenia przegldarki (poruszenie mysz, nacinicie
klawisza itp.) to przykady jego uycia. Inn czsto pojawiajc si nazw tego wzorca s
zdarzenia wasne

, czyli zdarzenia tworzone przez programist, a nie przegldark. Jeszcze

inna nazwa to wzorzec subskrybenta-dostawcy.

Gównym celem uywania wzorca jest promowanie lunego powizania elementów. Zamiast
sytuacji, w której jeden obiekt wywouje metod drugiego, mamy sytuacj, w której drugi
z obiektów zgasza ch otrzymywania powiadomie o zmianie w pierwszym obiekcie. Sub-
skrybenta nazywa si czsto obserwatorem, a obiekt obserwowany obiektem publikujcym
lub ródem. Obiekt publikujcy wywouje subskrybentów po zajciu istotnego zdarzenia
i bardzo czsto przekazuje informacj o nim w postaci obiektu zdarzenia.

Pierwszy przykad — subskrypcja magazynu

Aby dowiedzie si, jak zaimplementowa wzorzec, posumy si konkretnym przykadem.
Przypumy, e mamy wydawc

paper

, który publikuje gazet codzienn i miesicznik. Sub-

skrybent

joe

zostanie powiadomiony o wydaniu nowego periodyku.

Obiekt

paper

musi zawiera waciwo

subscribers

, która jest tablic przechowujc

wszystkich subskrybentów. Zgoszenie si do subskrypcji polega jedynie na dodaniu nowego
wpisu do tablicy. Gdy zajdzie istotne zdarzenie, obiekt

paper

przejdzie w ptli przez wszyst-

kich subskrybentów, by ich o nim powiadomi. Notyfikacja polega na wywoaniu metody
obiektu subskrybenta. Oznacza to, e w momencie zgoszenia chci otrzymywania powia-
domie subskrybent musi przekaza obiektowi

paper

jedn ze swoich metod w wywoaniu

metody

subscribe()

.

Obiekt

paper

moe dodatkowo umoliwi anulowanie subskrypcji, czyli usunicie wpisu

z tablicy subskrybentów. Ostatni istotn metod obiektu

paper

jest

publish()

, która wy-

wouje metody subskrybentów. Podsumowujc, obiekt publikujcy musi zawiera nastpujce
skadowe:

x

subscribers

— tablica;

x

subscribe()

— dodaje wpis do tablicy;

x

unsubscribe()

— usuwa wpis z tablicy;

x

publish()

— przechodzi w ptli przez subskrybentów i wywouje przekazane przez

nich metody.

Wszystkie trzy metody potrzebuj parametru

type

, poniewa wydawca moe zgosi kilka

rónych zdarze (publikacj gazety lub magazynu), a subskrybenci mog zdecydowa si na
otrzymywanie powiadomie tylko o jednym z nich.

Poniewa powysze skadowe s bardzo ogólne i mog by stosowane przez dowolnego
wydawc, warto zaimplementowa je jako cz osobnego obiektu. W ten sposób bdzie je
mona w przyszoci skopiowa do dowolnego obiektu, zamieniajc go w wydawc (obiekt
publikujcy).

Kup książkę

Poleć książkę

background image

164

_

Rozdzia 7. Wzorce projektowe

Oto przykadowa implementacja ogólnej funkcjonalnoci obiektu publikujcego, która defi-
niuje wszystkie wymagane skadowe oraz metod pomocnicz

visitSubscribers()

:

var publisher = {
subscribers: {
any: [] // typ zdarzenia
},
subscribe: function (fn, type) {
type = type || 'any';
if (typeof this.subscribers[type] === "undefined") {
this.subscribers[type] = [];
}
this.subscribers[type].push(fn);
},
unsubscribe: function (fn, type) {
this.visitSubscribers('unsubscribe', fn, type);
},
publish: function (publication, type) {
this.visitSubscribers('publish', publication, type);
},
visitSubscribers: function (action, arg, type) {
var pubtype = type || 'any',
subscribers = this.subscribers[pubtype],
i,
max = subscribers.length;

for (i = 0; i < max; i += 1) {
if (action === 'publish') {
subscribers[i](arg);
} else {
if (subscribers[i] === arg) {
subscribers.splice(i, 1);
}
}
}
}
};

Poniej znajduje si kod funkcji, która przyjmuje obiekt i zamienia go w obiekt publikujcy
przez proste skopiowanie wszystkich ogólnych metod dotyczcych publikacji.

function makePublisher(o) {
var i;
for (i in publisher) {
if (publisher.hasOwnProperty(i) && typeof publisher[i] === "function") {
o[i] = publisher[i];
}
}
o.subscribers = {any: []};
}

Czas na implementacj obiektu

paper

, który bdzie publikowa gazet i magazyn.

var paper = {
daily: function () {
this.publish("ciekawy news");
},
monthly: function () {
this.publish("interesujc analiz", "magazyn");
}
};

Trzeba jeszcze uczyni z obiektu wydawc.

makePublisher(paper);

Kup książkę

Poleć książkę

background image

Obserwator

_ 165

Po utworzeniu wydawcy moemy utworzy obiekt subskrybenta o nazwie

joe

, który ma

dwie metody.

var joe = {
drinkCoffee: function (paper) {
console.log('Wanie przeczytaem ' + paper);
},
sundayPreNap: function (monthly) {
console.log('Chyba zasn, czytajc ' + monthly);
}
};

Nastpny krok to obiekt

paper

subskrybujcy

joe

(tak naprawd to

joe

zgasza si jako sub-

skrybent do

paper

).

paper.subscribe(joe.drinkCoffee);
paper.subscribe(joe.sundayPreNap, 'magazyn');

Obiekt

joe

udostpni dwie metody. Pierwsza z nich powinna by wywoywana dla domylnego

zdarzenia „wszystko”, a druga jedynie dla zdarze „magazyn”. Oto kilka zgosze zdarze:

paper.daily();
paper.daily();
paper.daily();
paper.monthly();

Wszystkie metody publikujce wywoay odpowiednie metody z obiektu

joe

, co spowodo-

wao wywietlenie w konsoli nastpujcego wyniku:

Wanie przeczytaem ciekawy news
Wanie przeczytaem ciekawy news
Wanie przeczytaem ciekawy news
Chyba zasn, czytajc interesujc analiz

Bardzo wanym elementem caego systemu jest to, e

paper

nie zawiera w sobie informacji

o

joe

i odwrotnie. Co wicej, nie istnieje obiekt mediatora, który wiedziaby o wszystkich

obiektach. Obiekty uczestniczce w interakcjach s ze sob powizane bardzo luno i bez ja-
kichkolwiek modyfikacji mona doda jeszcze kilku subskrybentów. Co wane,

joe

moe

w dowolnym momencie anulowa subskrypcj.

Nic te nie stoi na przeszkodzie, by

joe

równie zosta wydawc (przecie to nic trudnego

dziki systemom blogowym i mikroblogowym). Jako wydawca

joe

wysya aktualizacj swojego

statusu do serwisu Twitter:

makePublisher(joe);
joe.tweet = function (msg) {
this.publish(msg);
};

Wyobramy sobie, e dzia relacji z klientami wydawcy gazety decyduje si czyta, co o ga-
zecie sdzi jej subskrybent

joe

, i dodaje w tym celu metod

readTweets()

.

paper.readTweets = function (tweet) {
alert('Zwoajmy due zebranie! Kto napisa: ' + tweet);
};
joe.subscribe(paper.readTweets);

Gdy

joe

zamieci swój wpis, wydawca (

paper

) go otrzyma.

joe.tweet("nie lubi tej gazety");

Wykonanie kodu spowoduje wywietlenie w konsoli tekstu „Zwoajmy due zebranie! Kto
napisa: nie lubi tej gazety”.

Kup książkę

Poleć książkę

background image

166

_

Rozdzia 7. Wzorce projektowe

Peny kod ródowy przykadu oraz moliwo sprawdzenia wyników jego dziaania w konsoli
zapewnia strona HTML dostpna pod adresem http://www.jspatterns.com/book/7/observer.html.

Drugi przykad — gra w naciskanie klawiszy

Przyjrzymy si jeszcze jednemu przykadowi. Zaimplementujemy t sam gr w naciskanie
klawiszy co przy wzorcu mediatora, ale tym razem uyjemy wzorca obserwatora. Aby nieco
utrudni zadanie, zapewnijmy obsug dowolnej liczby graczy, a nie tylko dwóch. Ponownie
skorzystamy z konstruktora

Player()

, który tworzy obiekty graczy, i z obiektu

scoreboard

.

Jedynie obiekt

mediator

zamieni si w obiekt

game

.

We wzorcu mediatora obiekt

mediator

wiedzia o wszystkich uczestnikach gry i wywoywa

ich metody. Obiekt

game

ze wzorca obserwatora nie bdzie tego robi — to same obiekty bd

zgaszay ch otrzymywania informacji o zajciu wybranych zdarze. Przykadowo, obiekt

scoreboard

zgosi ch bycia informowanym o zajciu zdarzenia

scorechange

w obiekcie

game

.

Oryginalny obiekt

publisher

naley nieco zmieni, by upodobni go do rozwiza znanych

z przegldarek internetowych.

x

Zamiast metod

publish()

,

subscribe()

i

unsubscribe()

pojawi si metody

fire()

,

on()

i

remove()

.

x

Typ zdarzenia (

type

) bdzie uywany cay czas, wic stanie si pierwszym parametrem

wszystkich trzech funkcji.

x

Dodatkowy parametr

context

przekazywany wraz z funkcj powiadomienia umoliwi

wywoanie funkcji zwrotnej z odpowiednio ustawion wartoci

this

.

Nowy obiekt

publisher

ma nastpujc posta:

var publisher = {
subscribers: {
any: []
},
on: function (type, fn, context) {
type = type || 'any';
fn = typeof fn === "function" ? fn : context[fn];

if (typeof this.subscribers[type] === "undefined") {
this.subscribers[type] = [];
}
this.subscribers[type].push({fn: fn, context: context || this});
},
remove: function (type, fn, context) {
this.visitSubscribers('unsubscribe', type, fn, context);
},
fire: function (type, publication) {
this.visitSubscribers('publish', type, publication);
},
visitSubscribers: function (action, type, arg, context) {
var pubtype = type || 'any',
subscribers = this.subscribers[pubtype],
i,
max = subscribers ? subscribers.length : 0;

for (i = 0; i < max; i += 1) {
if (action === 'publish') {
subscribers[i].fn.call(subscribers[i].context, arg);
} else {

Kup książkę

Poleć książkę

background image

Obserwator

_ 167

if (subscribers[i].fn === arg && subscribers[i].context === context) {
subscribers.splice(i, 1);
}
}
}
}
};

Nowy konstruktor

Player()

wyglda nastpujco:

function Player(name, key) {
this.points = 0;
this.name = name;
this.key = key;
this.fire('newplayer', this);
}

Player.prototype.play = function () {
this.points += 1;
this.fire('play', this);
};

Nowym parametrem przyjmowanym przez konstruktor jest

key

— okrela on klawisz na

klawiaturze, który gracz bdzie naciska, by uzyskiwa punkty (we wczeniejszej wersji kodu
klawisze byy zapisane na sztywno). Dodatkowo utworzenie nowego obiektu gracza powo-
duje zgoszenie zdarzenia

newplayer

, a kade nacinicie klawisza przez gracza skutkuje

zgoszeniem zdarzenia

play

.

Obiekt

scoreboard

pozostaje bez zmian — nadal aktualizuje tablic wyników, korzystajc

z biecych wartoci.

Nowy obiekt

game

potrafi ledzi poczynania wszystkich graczy, by móg zlicza wyniki

i zgasza zdarzenie

scorechange

. Dodatkowo zgasza si on jako subskrybent wszystkich

zdarze

keypress

przegldarki, by wiedzie o wszystkich klawiszach przypisanych poszcze-

gólnym graczom.

var game = {

keys: {},

addPlayer: function (player) {
var key = player.key.toString().charCodeAt(0);
this.keys[key] = player;
},

handleKeypress: function (e) {
e = e || window.event; // IE
if (game.keys[e.which]) {
game.keys[e.which].play();
}
},

handlePlay: function (player) {
var i,
players = this.keys,
score = {};

for (i in players) {
if (players.hasOwnProperty(i)) {
score[players[i].name] = players[i].points;
}

Kup książkę

Poleć książkę

background image

168

_

Rozdzia 7. Wzorce projektowe

}
this.fire('scorechange', score);
}
};

Funkcja

makePublisher()

, która zamieniaa dowolny obiekt w obiekt publikujcy zdarzenia,

jest identyczna jak w przykadzie z wydawc i gazet. Obiekt

game

bdzie zgasza zdarzenia

takie jak

scorechange

. Obiektem publikujcym stanie si równie

Player.prototype

, by moli-

we byo zgaszanie zdarze

play

i

newplayer

wszystkim zainteresowanym.

makePublisher(Player.prototype);
makePublisher(game);

Obiekt

game

zgasza si jako subskrybent zdarze

play

i

newplayer

(a take zdarzenia

keypress

przegldarki), natomiast obiekt

scoreboard

chce by powiadamiany o zdarzeniach

scorechange

.

Player.prototype.on("newplayer", "addPlayer", game);
Player.prototype.on("play", "handlePlay", game);
game.on("scorechange", scoreboard.update, scoreboard);
window.onkeypress = game.handleKeypress;

Metoda

on()

umoliwia subskrybentom okrelenie funkcji zwrotnej jako referencji (

score

´board.update

) lub jako tekstu (

"addPlayer"

). Wersja tekstowa dziaa prawidowo tylko

w przypadku przekazania jako trzeciego parametru kontekstu (na przykad

game

).

Ostatni element to dynamiczne tworzenie tylu obiektów graczy (po naciniciu klawiszy), ile
zostanie zadanych przez grajcych.

var playername, key;
while (1) {
playername = prompt("Dodaj gracza (imi)");
if (!playername) {
break;
}
while (1) {
key = prompt("Klawisz dla gracza " + playername + "?");
if (key) {
break;
}
}
new Player(playername, key);
}

To ju wszystko w temacie gry. Peny kod ródowy wraz z moliwoci zagrania znajduje
si pod adresem http://www.jspatterns.com/book/7/observer-game.html.

W implementacji wzorca mediatora obiekt

mediator

musia wiedzie o wszystkich obiektach,

by móc w odpowiednim czasie wywoywa waciwe metody i koordynowa ca gr. W nowej
implementacji obiekt

game

jest nieco gupszy i wykorzystuje fakt, i obiekty zgaszaj zdarzenia

i obserwuj si nawzajem (na przykad obiekt

scoreboard

nasuchuje zdarzenia

scorechange

).

Zapewnia to jeszcze luniejsze powizanie obiektów (im mniej z nich wie o innych, tym lepiej),
cho za cen utrudnionej analizy, kto tak naprawd nasuchuje kogo. W przykadowej grze
wszystkie subskrypcje s na razie w jednym miejscu, ale gdyby staa si ona bardziej rozbu-
dowana, wywoania

on()

mogyby si znale w wielu rónych miejscach (niekoniecznie

w kodzie inicjalizujcym). Taki kod trudniej jest testowa, gdy trudno od razu zrozumie,
co tak naprawd si w nim dzieje. Wzorzec obserwatora zrywa ze standardowym, proce-
duralnym wykonywaniem kodu od pocztku do koca.

Kup książkę

Poleć książkę

background image

Podsumowanie

_ 169

Podsumowanie

W rozdziale pojawiy si opisy kilku popularnych wzorców projektowych i przykady ich
implementacji w jzyku JavaScript. Omawiane byy nastpujce wzorce:

x

Singleton

— tworzenie tylko jednego obiektu danej „klasy”. Pojawio si kilka rozwi-

za, w tym takie, które staray si zachowa skadni znan z jzyka Java przez zastoso-
wanie funkcji konstruujcych. Trzeba jednak pamita, e z technicznego punktu widze-
nia w jzyku JavaScript wszystkie obiekty s singletonami. Nie naley te zapomina, e
czasem programici stosuj sowo „singleton”, a maj na myli obiekty utworzone przy
uyciu wzorca moduu.

x

Fabryka

— metoda tworzca obiekty o typie przekazanym jako warto tekstowa.

x

Iterator

— interfejs umoliwiajcy atwe przetwarzanie elementów umieszczonych w zo-

onej strukturze danych.

x

Dekorator

— modyfikacja obiektów w trakcie dziaania programu przez dodawanie do

nich funkcjonalnoci zdefiniowanych w dekoratorach.

x

Strategia

— zachowanie tego samego interfejsu przy jednoczesnym wyborze najlepszej

strategii jego implementacji (uzalenionej od kontekstu).

x

Fasada

— zapewnienie bardziej przyjaznego API przez otoczenie typowych (lub le za-

projektowanych) metod ich nowymi wersjami.

x

Porednik

— otoczenie obiektu zapewniajce kontrol dostpu do niego, gdy celem jest

uniknicie wykonywania kosztownych operacji przez ich grupowanie lub opónianie do
momentu, gdy bd naprawd konieczne.

x

Mediator

— promowanie lunego powizania obiektów przez unikanie bezporedniej

komunikacji midzy nimi i zastpienie jej komunikacj poprzez obiekt mediatora.

x

Obserwator

— lune powizanie midzy obiektami osigane przez tworzenie obiektów,

których zmiany mona obserwowa, jawnie zgaszajc si jako subskrybent (czsto mówi
si równie o wasnych zdarzeniach lub wzorcu subskrybenta-dostawcy).

Kup książkę

Poleć książkę

background image

170

_

Rozdzia 7. Wzorce projektowe

Kup książkę

Poleć książkę

background image

195

Skorowidz

.htaccess, 185
@class, 43
@method, 43
@namespace, 43
@param, 41, 43
@return, 41, 43
<script>, 186, 187

dodawanie elementu, 190
dynamiczne elementy, 189
lokalizacja, 187

A

addEventListener(), 175
alert(), 20
antywzorce, 16
Apache, .htaccess, 185
aplikacja

czciowa, 85, 86, 89
funkcji, 84, 85
internetowa, 171

apply(), 85, 133, 134
arguments.callee, 55, 83
Array, 56, 57
asynchroniczne, zdarzenia, 73
atrybut, 17
attachEvent(), 175

B

bbelkowanie zdarze, 177
bind(), 135, 136
break, 32

C

call(), 133, 134
case, 32
CDN, 186
Closure Compiler, 46, 80
console, obiekt, 20

constructor, waciwo, 18, 126
Content Delivery Network, Patrz CDN
Crockford, Douglas, 19, 113
Curry, Haskell, 87
currying, Patrz funkcje, rozwijanie

D

default, 32
dekoratora, wzorzec, 145, 169

implementacja, 146, 147, 148, 149

delegacje zdarze, wzorzec, 177
delete, operator, 24
dir(), 20
Document Object Model, Patrz DOM
dodatki syntaktyczne, 113
dokumentacja, 41

JSDoc, 41
YUIDoc, 41, 42, 44

DOM, 172

dostp, 173
modyfikacja, 174

dorozumiane zmienne globalne, 23, 24
dziedziczenie, 18, 136

klasyczne, 115, 116, 126
nowoczesne, 115, 116
prototypowe, 129, 130
przez kopiowanie waciwoci, 131, 132
wielobazowe, 121

E

ECMAScript 5, 18, 19

dodatki, 130

Error(), 62
ES5, Patrz ECMAScript 5
eval(), 19

unikanie, 32, 33

Expires, nagówek, 185
extend(), 97, 132
extendDeep(), 97

Kup książkę

Poleć książkę

background image

196

_ Skorowidz

F

fabryki, wzorzec, 141, 142, 143, 169
fasady, wzorzec, 152, 153, 169
Firebug, 132
for, ptla, 27, 28
for-in, ptla, 29
Function(), 33, 66
Function.prototype.apply(), 84
funkcje, 17, 65, 66

anonimowe, 66, 68
czasowe, 73
deklaracje, 67, 68
konstruujce, 51, 52
name, waciwo, 68
natychmiastowe, 76, 77, 78, 79, 89
obsugi zdarze asynchronicznych, 73
poredniczce, 126
prywatne, 99
rozwijanie, 84, 86, 87, 89
samodefiniujce si, 75, 76, 90
samowywoujce si, 79
terminologia, 66
waciwoci, 82
wywoania zwrotnego, 70
wywoanie, 85
zwracanie, 74, 89

G

globalne zmienne, 22, 23, 24

dorozumiane, 23, 24

gospodarza, obiekty, 17, 18

H

hasOwnProperty(), 29, 30
hoisting, Patrz przenoszenie deklaracji
HTML, wysyanie pliku fragmentami, 188, 189
HTMLCollection, 27, 28

I

inicjalizacja, 25

leniwa, 153

init(), 79, 80
instanceof, operator, 108
instancja, 115
isArray(), 57
iteratora, wzorzec, 143, 144, 169

J

JavaScript, 15

biblioteki, 94
jako jzyk obiektowy, 16
sprawdzanie jakoci kodu, 19
rodowisko uruchomieniowe, 18

JavaScript Object Notation, Patrz JSON
jQuery, biblioteka, 59, 132
JSDoc, 41
JSLint, 19, 47
JSON, 58
JSON with Padding, Patrz JSONP
JSON.parse(), 58, 59
JSON.stringify(), 59
JSONP, 181, 182, 183

K

klasy, 17, 126

emulacja, 126, 127

kod

konwencje, 34, 35, 36, 37, 38
atwy w konserwacji, 21, 22
minifikowanie, 46
ocenianie przez innych, 45, 46
usuwanie warunkowych wersji, 80, 81, 90
wielokrotne uycie, 115

kodowania, wzorce, 16
komentarze, 40, 41
kompresja, 185
konsola, 20
konstruktory, 54, 119

czyszczenie referencji, 125
poredniczce, 126
poyczanie, 119, 121, 122
samowywoujce, 55
tymczasowe, 124, 126
warto zwracana, 53

konwencje kodu, 34, 35

biae spacje, 37, 38
nawias otwierajcy, 36, 37
nawiasy klamrowe, 35, 36
nazewnictwo, 38, 39, 40, 54
redniki, 37
wcicia, 35

konwersja liczb, 34
kopia

gboka, 131
pytka, 131

Kup książkę

Poleć książkę

background image

Skorowidz

_ 197

L

leniwa inicjalizacja, 153
leniwe wczytywanie, 190, 191
liczby, konwersja, 34
literay

funkcji, 67
obiektów, 49, 50, 51, 98
tablicy, 56
wyraenia regularnego, 59, 60

log(), 20
lokalne zmienne, 22

acuchy wywoa, 112

M

Martin, Robert, 112
mediator, 160
mediatora, wzorzec, 160, 169

przykad, 160, 161, 162
uczestnicy, 160

method(), 113
metody, 17, 49

poyczanie, 133, 134
prywatne, 95, 96
publiczne, 99
statyczne, 107, 108
uprzywilejowane, 96

minifikacja, 46, 185
moduy, 100, 101, 102

import zmiennych globalnych, 103
tworzce konstruktory, 102

N

najmniejszego przywileju, zasada, 97
name, waciwo, 68
natychmiastowa inicjalizacja obiektu, 79, 90
nazewnictwo, konwencje, 38, 39, 40, 54
nazwane wyraenie funkcyjne, 66, 67
new, sowo kluczowe, 54, 138
nienazwane wyraenie funkcyjne, 66, 68
notacja literau obiektu, 49, 50, 51

O

obiekty, 17, 51

bdów, 62
globalne, 22, 25
gospodarza, 17, 18

konfiguracyjne, 83, 84, 89
natychmiastowa inicjalizacja, 79, 90
rdzenne, 17
tworzenie, 51, 91

Object(), 18, 51, 143
Object.create(), 130
Object.prototype.toString(), 58
obserwator, 163
obserwatora, wzorzec, 163, 166, 169
obsuga zdarze, 175, 176

asynchronicznych, 73

onclick, atrybut, 175
open(), 180

P

parseInt(), 34
parseJSON(), 59
ptle

for, 27, 28
for-in, 29

piaskownicy, wzorzec, 103, 104, 105, 114

dodawanie moduów, 105
globalny konstruktor, 104
implementacja konstruktora, 106

porednika, wzorzec, 153, 155, 158, 159, 169
preventDefault(), 152
projektowe, wzorce, 16
prototype, waciwo, 18, 98

modyfikacja, 31

prototypy, 18

acuch, 117, 118, 120, 121
modyfikacja, 31
prywatno, 98
wspódzielenie, 123, 124

prywatno, problemy, 96
przegldarki, wykrywanie, 194
przenoszenie deklaracji, 26, 27
przestrzenie nazw, 22, 91, 92, 114

Firebug, 94

R

ramki, 184
rdzenne obiekty, 17
RegExp(), 59, 60
rzutowanie niejawne, 32

S

Schönfinkel, Moses, 87
schönfinkelizacja, 87
send(), 180

Kup książkę

Poleć książkę

background image

198

_ Skorowidz

serializacja, 82, 83
serwer, komunikacja, 179
setInterval(), 33, 73
setTimeout(), 33, 73, 178
singleton, 137, 138, 169
skadowe

prywatne, 96
statyczne, 107, 109

skrypty

czenie, 184, 185
obliczeniowe, 179
strategie wczytywania, 186

stae, 110, 111
stopPropagation(), 152
strategii, wzorzec, 149, 169
strict mode, Patrz tryb cisy
String.prototype.replace(), 60
styl wielbdzi, 39
subskrybenta-dostawcy, wzorzec, 163, 169
supermetody, 152
switch, 31, 32
SyntaxError(), 62

rodowisko uruchomieniowe, 18

T

that, 54, 55
this, 22, 53
throw, instrukcja, 62
tryb cisy, 19
TypeError(), 62
typeof, 32, 57
typy proste, otoczki, 61, 62

V

var, 23

efekty uboczne pominicia, 24
problem rozrzuconych deklaracji, 26
wzorzec pojedynczego uycia, 25

W

walidacja danych, 150
wtki, symulacja, 178
wczytywanie

leniwe, 190, 191
na danie, 191
wstpne, 192, 193, 194

wielbdzi, styl, 39
window, waciwo, 22, 25
with, polecenie, 19
waciwoci, 17, 49

prywatne, 95, 96
statyczne, 107, 110

wydajno, 184
wyliczenie, 29
wyraenia regularne, 59
wyraenie funkcyjne, 66, 67

nazwane, 66, 67
nienazwane, 66

wywoanie funkcji, 85
wywoanie jako obraz, 184
wywoanie zwrotne, 70, 71, 89

w bibliotekach, 74
zakres zmiennych, 72

wzorce, 11, 15

antywzorce, 16
API, 89
inicjalizacyjne, 89
kodowania, 16
optymalizacyjne, 90
projektowe, 16

X

XHR, Patrz XMLHttpRequest
XMLHttpRequest, 180, 181

Y

Y.clone(), 132
Y.delegate(), 178
Yahoo! Query Language, Patrz YQL
YQL, 157
YUI3, 132, 178
YUIDoc, 41, 42, 44

przykad dokumentacji, 42, 44

Z

zdarzenia, 175

asynchroniczne, 73
delegacje, 177
obsuga, 175, 176
wasne, 163

zmienne, 17

globalne, 22, 23, 24, 103
lokalne, 22

Kup książkę

Poleć książkę

background image
background image

Wyszukiwarka

Podobne podstrony:
JavaScript Wzorce
informatyka javascript wzorce stoyan stefanov ebook
JavaScript Wzorce Stoyan Stefanov
JavaScript Wzorce 2
JavaScript
05 Wzorce długości i kąta
JavaScript Rozmowki jscroz
Inzynieria oprogramowania w ujeciu obiektowym UML wzorce projektowe i Java iowuje
zasady grupy, java, javascript, oprogramowanie biurowe, programowanie, programowanie 2, UTK, systemy
polak,miernictwo,Wzorce
Formularze javascript
Wzorce projektowe ściaga

więcej podobnych podstron