



<html>
<head>
  <title>javabog.dk -  - Skabende designm&oslash;nstre</title>
  <link rev="stylesheet" type="text/css" href="../typografi.css">
  <meta name="description" content="Lærebog i Java. Af Jacob Nordfalk. Udkommet hos Forlaget Globe">
  <meta name="keywords" content="designmønster, programmering, OOP, objekter, klasser, objektorienteret programmering, Java, JSP, lærebog, UML, IT">
</head>
<body bgcolor="#ffffff">



<a href='http://javabog.dk/'>javabog.dk</a>&nbsp;&nbsp;|&nbsp;&nbsp;<a href='kapitel15.jsp'>&lt;&lt; forrige</a>&nbsp;&nbsp;|&nbsp;&nbsp;<a href='indhold.jsp'>indhold</a>&nbsp;&nbsp;|&nbsp;&nbsp;<a href='kapitel17.jsp'>n&aelig;ste &gt;&gt;</a>&nbsp;&nbsp;|&nbsp;&nbsp;<a href='kode/'>programeksempler</a>&nbsp;&nbsp;|&nbsp;&nbsp;<a href='../index_VP.html'>om bogen</a>

<H1 CLASS="western" STYLE="">16 <a name='afsn16'></a>Skabende
designm&oslash;nstre</H1>
<DIV ID="Indholdsfortegnelse2">
  <P STYLE="margin-top: 0.3cm; margin-bottom: 0cm"><BR>
  </P>
  <P STYLE="margin-left: 0.3cm; margin-top: 0.15cm; margin-bottom: 0cm">
  <FONT FACE="Helvetica, sans-serif"><FONT SIZE=2 STYLE="font-size: 11pt"><B>16.1
  Overordnet id&eacute;  248</B></FONT></FONT></P>
  <P STYLE="margin-left: 0.6cm; margin-top: 0.08cm; margin-bottom: 0cm">
  <FONT FACE="Helvetica, sans-serif"><FONT SIZE=2 STYLE="font-size: 9pt">16.1.1
  Fabrikeringsmetoder  248</FONT></FONT></P>
  <P STYLE="margin-left: 0.3cm; margin-top: 0.15cm; margin-bottom: 0cm">
  <FONT FACE="Helvetica, sans-serif"><FONT SIZE=2 STYLE="font-size: 11pt"><B>16.2
  Fabrik  249</B></FONT></FONT></P>
  <P STYLE="margin-left: 0.6cm; margin-top: 0.08cm; margin-bottom: 0cm">
  <FONT FACE="Helvetica, sans-serif"><FONT SIZE=2 STYLE="font-size: 9pt">16.2.1
  Eksempel: Image  249</FONT></FONT></P>
  <P STYLE="margin-left: 0.3cm; margin-top: 0.15cm; margin-bottom: 0cm">
  <FONT FACE="Helvetica, sans-serif"><FONT SIZE=2 STYLE="font-size: 11pt"><B>16.3
  Singleton  250</B></FONT></FONT></P>
  <P STYLE="margin-left: 0.6cm; margin-top: 0.08cm; margin-bottom: 0cm">
  <FONT FACE="Helvetica, sans-serif"><FONT SIZE=2 STYLE="font-size: 9pt">16.3.1
  Eksempel: java.lang.Runtime  250</FONT></FONT></P>
  <P STYLE="margin-left: 0.6cm; margin-top: 0.08cm; margin-bottom: 0cm">
  <FONT FACE="Helvetica, sans-serif"><FONT SIZE=2 STYLE="font-size: 9pt">16.3.2
  Eksempel: java.awt.Toolkit  250</FONT></FONT></P>
  <P STYLE="margin-left: 0.6cm; margin-top: 0.08cm; margin-bottom: 0cm">
  <FONT FACE="Helvetica, sans-serif"><FONT SIZE=2 STYLE="font-size: 9pt">16.3.3
  Eksempel: Dataforbindelse  251</FONT></FONT></P>
  <P STYLE="margin-left: 0.6cm; margin-top: 0.08cm; margin-bottom: 0cm">
  <FONT FACE="Helvetica, sans-serif"><FONT SIZE=2 STYLE="font-size: 9pt">16.3.4
  Singleton med sen initialisering  252</FONT></FONT></P>
  <P STYLE="margin-left: 0.6cm; margin-top: 0.08cm; margin-bottom: 0cm">
  <FONT FACE="Helvetica, sans-serif"><FONT SIZE=2 STYLE="font-size: 9pt">16.3.5
  Singleton uden privat konstrukt&oslash;r  253</FONT></FONT></P>
  <P STYLE="margin-left: 0.3cm; margin-top: 0.15cm; margin-bottom: 0cm">
  <FONT FACE="Helvetica, sans-serif"><FONT SIZE=2 STYLE="font-size: 11pt"><B>16.4
  Abstrakt Fabrik  254</B></FONT></FONT></P>
  <P STYLE="margin-left: 0.6cm; margin-top: 0.08cm; margin-bottom: 0cm">
  <FONT FACE="Helvetica, sans-serif"><FONT SIZE=2 STYLE="font-size: 9pt">16.4.1
  Eksempel: AdresseFabrik  254</FONT></FONT></P>
  <P STYLE="margin-left: 0.6cm; margin-top: 0.08cm; margin-bottom: 0cm">
  <FONT FACE="Helvetica, sans-serif"><FONT SIZE=2 STYLE="font-size: 9pt">16.4.2
  Eksempel fra standardklasserne: AWT  256</FONT></FONT></P>
  <P STYLE="margin-left: 0.3cm; margin-top: 0.15cm; margin-bottom: 0cm">
  <FONT FACE="Helvetica, sans-serif"><FONT SIZE=2 STYLE="font-size: 11pt"><B>16.5
  Bygmester  257</B></FONT></FONT></P>
  <P STYLE="margin-left: 0.6cm; margin-top: 0.08cm; margin-bottom: 0cm">
  <FONT FACE="Helvetica, sans-serif"><FONT SIZE=2 STYLE="font-size: 9pt">16.5.1
  Trinvis konstruktion  257</FONT></FONT></P>
  <P STYLE="margin-left: 0.6cm; margin-top: 0.08cm; margin-bottom: 0cm">
  <FONT FACE="Helvetica, sans-serif"><FONT SIZE=2 STYLE="font-size: 9pt">16.5.2
  Eksempel p&aring; trinvis konstruktion: E-post  258</FONT></FONT></P>
  <P STYLE="margin-left: 0.6cm; margin-top: 0.08cm; margin-bottom: 0cm">
  <FONT FACE="Helvetica, sans-serif"><FONT SIZE=2 STYLE="font-size: 9pt">16.5.3
  Eksempel p&aring; netv&aelig;rk af objekter: Funktioner  258</FONT></FONT></P>
  <P STYLE="margin-left: 0.3cm; margin-top: 0.15cm; margin-bottom: 0cm">
  <FONT FACE="Helvetica, sans-serif"><FONT SIZE=2 STYLE="font-size: 11pt"><B>16.6
  Prototype  259</B></FONT></FONT></P>
  <P STYLE="margin-left: 0.6cm; margin-top: 0.08cm; margin-bottom: 0cm">
  <FONT FACE="Helvetica, sans-serif"><FONT SIZE=2 STYLE="font-size: 9pt">16.6.1
  Interfacet Cloneable  259</FONT></FONT></P>
  <P STYLE="margin-left: 0.6cm; margin-top: 0.08cm; margin-bottom: 0cm">
  <FONT FACE="Helvetica, sans-serif"><FONT SIZE=2 STYLE="font-size: 9pt">16.6.2
  Overfladisk og dyb kopiering  259</FONT></FONT></P>
  <P STYLE="margin-left: 0.6cm; margin-top: 0.08cm; margin-bottom: 0cm">
  <FONT FACE="Helvetica, sans-serif"><FONT SIZE=2 STYLE="font-size: 9pt">16.6.3
  Eksempel: Et tegneprogram  260</FONT></FONT></P>
  <P STYLE="margin-left: 0.3cm; margin-top: 0.15cm; margin-bottom: 0cm">
  <FONT FACE="Helvetica, sans-serif"><FONT SIZE=2 STYLE="font-size: 11pt"><B>16.7
  Objektpulje  262</B></FONT></FONT></P>
  <P STYLE="margin-left: 0.6cm; margin-top: 0.08cm; margin-bottom: 0cm">
  <FONT FACE="Helvetica, sans-serif"><FONT SIZE=2 STYLE="font-size: 9pt">16.7.1
  Eksempel: Begr&aelig;nsede resurser  262</FONT></FONT></P>
  <P STYLE="margin-left: 0.6cm; margin-top: 0.08cm; margin-bottom: 0cm">
  <FONT FACE="Helvetica, sans-serif"><FONT SIZE=2 STYLE="font-size: 9pt">16.7.2
  Variation: Klient 'h&aelig;nger', hvis puljen l&oslash;ber t&oslash;r  263</FONT></FONT></P>
  <P STYLE="margin-left: 0.6cm; margin-top: 0.08cm; margin-bottom: 0cm">
  <FONT FACE="Helvetica, sans-serif"><FONT SIZE=2 STYLE="font-size: 9pt">16.7.3
  Genbrug af tr&aring;de  264</FONT></FONT></P>
  <P STYLE="margin-left: 0.6cm; margin-top: 0.08cm; margin-bottom: 0cm">
  <FONT FACE="Helvetica, sans-serif"><FONT SIZE=2 STYLE="font-size: 9pt">16.7.4
  Opgave: Objektpulje med objektfabrik  265</FONT></FONT></P>
  <P STYLE="margin-left: 0.3cm; margin-top: 0.15cm; margin-bottom: 0cm">
  <FONT FACE="Helvetica, sans-serif"><FONT SIZE=2 STYLE="font-size: 11pt"><B>16.8
  St&oslash;rre eksempel: Dataforbindelse  266</B></FONT></FONT></P>
  <P STYLE="margin-left: 0.6cm; margin-top: 0.08cm; margin-bottom: 0cm">
  <FONT FACE="Helvetica, sans-serif"><FONT SIZE=2 STYLE="font-size: 9pt">16.8.1
  Specialiseringer af Dataforbindelse  267</FONT></FONT></P>
  <P STYLE="margin-left: 0.6cm; margin-top: 0.08cm; margin-bottom: 0cm">
  <FONT FACE="Helvetica, sans-serif"><FONT SIZE=2 STYLE="font-size: 9pt">16.8.2
  Dataforbindelse over netv&aelig;rk  269</FONT></FONT></P>
  <P STYLE="margin-left: 0.6cm; margin-top: 0.08cm; margin-bottom: 0cm">
  <FONT FACE="Helvetica, sans-serif"><FONT SIZE=2 STYLE="font-size: 9pt">16.8.3
  Dataforbindelse, der cacher foresp&oslash;rgsler  271</FONT></FONT></P>
  <P STYLE="margin-left: 0.6cm; margin-top: 0.08cm; margin-bottom: 0cm">
  <FONT FACE="Helvetica, sans-serif"><FONT SIZE=2 STYLE="font-size: 9pt">16.8.4
  Endelig udgave af Dataforbindelse  272</FONT></FONT></P>
</DIV>

<P CLASS="kapiteloversigt-western">Det er en god id&eacute; at kigge
i <a href='kapitel1.jsp#afsn1.1'>afsnit 1.1</a>, Lister og m&aelig;ngder, <a href='kapitel8.jsp'>kapitel 8</a>, Databaser (JDBC) og
<a href='kapitel15.jsp#afsn15.9'>afsnit 15.9</a>, Introduktion til designm&oslash;nstre, f&oslash;r man
l&aelig;ser kapitlet.</P>
<H2 CLASS="western" STYLE="">16.1 <a name='afsn16.1'></a>Overordnet
id&eacute;</SPAN></H2>
<P CLASS="western"><SPAN LANG="da-DK">Den overordnede id&eacute; i de
skabende designm&oslash;nstre er at give id&eacute;er til, hvordan
man afkobler (dvs. mindsker graden af bindinger) mellem den del af
programmet, som <I>bruger</I> nogle objekter (kaldet klienten), og
den del af programmet, der bestemmer hvordan disse objekter bliver
<I>oprettet</I>. </SPAN>
</P>
<P CLASS="western">Kig p&aring; f&oslash;lgende eksempel, hvor
klienten b&aring;de opretter og bruger objektet af type Hj&aelig;lp:</P>
<PRE CLASS="kode-western">  Hj&aelig;lp h = <B>new Hj&aelig;lp()</B>;        <I>// h&oslash;j kobling - klient opretter et Hj&aelig;lp-objekt</I>

<SPAN LANG="da-DK">  ...</SPAN>

<SPAN LANG="da-DK">  h.metode1();</SPAN>
<SPAN LANG="da-DK">  h.metode2();</SPAN></PRE><P CLASS="western">
Ovenst&aring;ende kan <I>ikke</I> udvides til, at klienten (uden at
vide det) f.eks. benytter flere forskellige typer (nedarvinger) af
Hj&aelig;lp-objekter, eller at Hj&aelig;lp-objektet oprettes med
nogle andre parametre i konstrukt&oslash;ren. At oprette et objekt
kr&aelig;ver jo, at man angiver pr&aelig;cist, hvilken klasse og
hvilken konstrukt&oslash;r der skal anvendes.</P>
<P CLASS="western">Den eneste m&aring;de at g&oslash;re det p&aring;
er rent faktisk at &aelig;ndre i klientens kode - dvs. der er h&oslash;j
kobling (binding).</P>
<H3 CLASS="western">16.1.1 <a name='afsn16.1.1'></a>Fabrikeringsmetoder</H3>
<P CLASS="western">En fabrikeringsmetode (eng.: Factory Method) er en
metode, der opretter et objekt for klienten. Med en
fabrikeringsmetode har man fjernet opgaven med at oprette objektet
fra klienten, s&aring;dan at klienten i stedet kalder
fabrikeringsmetoden.</P>
<P CLASS="western">Eksempelvis kunne Hj&aelig;lp-klassen have
defineret metoden opretHj&aelig;lp():</P>
<PRE CLASS="kode-western">  Hj&aelig;lp h = <B>Hj&aelig;lp.opretHj&aelig;lp()</B>; <I>// fabrikeringsmetode leverer objekt til klient</I>en

<SPAN LANG="da-DK">  ...</SPAN>

<SPAN LANG="da-DK">  h.metode1();</SPAN>
<SPAN LANG="da-DK">  h.metode2();</SPAN></PRE><P CLASS="western">
Fabrikeringsmetoder kan v&aelig;re en m&aring;de at opn&aring;, at
nogle objekter arver fra en f&aelig;lles superklasse og anvendes
ensartet udefra, men internt fungerer forskelligt (polymorfi) uden
klientens vidende. N&aelig;sten alle de f&oslash;lgende designm&oslash;nstre
g&oslash;r derfor brug af fabrikeringsmetoder.</P>
<P CLASS="western">Inde i metoden opretHj&aelig;lp() oprettes
objekterne muligvis n&oslash;jagtigt lige som f&oslash;r (med new
Hj&aelig;lp()), men det kunne ogs&aring; v&aelig;re at:</P>
<UL>
  <LI><P CLASS="western">det var en nedarving af Hj&aelig;lp, der blev
  oprettet (polymorfi)</P>
  <LI><P CLASS="western">det blev oprettet med nogle bestemte
  parametre i konstrukt&oslash;ren</P>
  <LI><P CLASS="western">det samme objekt blev brugt af alle klienter
  (en Singleton, se <a href='kapitel16.jsp#afsn16.3'>afsnit 16.3</a>)</P>
  <LI><P CLASS="western">et eksisterende Hj&aelig;lp-objekt blev
  genbrugt (en Objektpulje, se <a href='kapitel16.jsp#afsn16.7'>afsnit 16.7</a>)</P>
</UL>

<H2 CLASS="western" STYLE="">16.2 <a name='afsn16.2'></a>Fabrik</SPAN></H2>
<P CLASS="designm&oslash;nsteroverskrift-western">Problem: Klienten
kan/skal ikke bestemme pr&aelig;cist, hvordan nogle objekter
oprettes.</P>
<P CLASS="designm&oslash;nsteroverskrift-western">L&oslash;sning: Lad
en Fabrik (eng.: Factory) med en fabrikeringsmetode varetage
oprettelsen.</P>
<BLOCKQUOTE CLASS="definition-western">En Fabrik er et objekt, der
opretter objekter for klienten</BLOCKQUOTE>
<P CLASS="western">En Fabrik er et objekt, der &quot;fabrikerer&quot;
objekter for kalderen (klienten), og alts&aring; et objekt, der har
en fabrikeringsmetode. Den kan lade klienten angive indhold og type.
Klienten kender ikke til detaljerne omkring oprettelsen.</P>
<H3 CLASS="western">16.2.1 <a name='afsn16.2.1'></a>Eksempel: Image</H3>
<P CLASS="western">N&aring;r man vil oprette et billede (af typen
Image), sker det ikke med</P>
<PRE CLASS="kode-western">  Image i = new Image(&quot;billede.gif&quot;); // forkert!!</PRE><P CLASS="western">
Image-objekter kan v&aelig;re forskelligt repr&aelig;senteret
afh&aelig;ngig af typen (f.eks. GIF, JPG eller PNG) og derudover
bruges Image-objekter ogs&aring; til andre ting, bl.a. filtrerede
billeder. Derudover kan et sort/hvid-billede med fordel repr&aelig;senteres
med en bit per punkt, mens et GIF-billede kan have op til 256 farver
og derfor med fordel repr&aelig;senteres med en byte per punkt.
JPG-billeder kan have op til 16.7 millioner farver. Endelig kan et
GIF-billede indeholde gennemsigtige punkter, der ikke tegnes p&aring;
sk&aelig;rmen, mens et JPG-billede ikke kan v&aelig;re gennemsigtigt.</P>
<P CLASS="western">Det er bedre at lade systemet afg&oslash;re
pr&aelig;cist, hvilken nedarving af Image der skal oprettes og
hvordan. Man kalder derfor i stedet fabrikeringsmetoden getImage() p&aring;
en grafisk komponent (arving fra java.awt.Component, f.eks. en applet
eller et panel):</P>
<PRE CLASS="kode-western">  Image i = this.getImage(&quot;billede.gif&quot;); <I>// i en applet eller panel</I></PRE><P CLASS="western">
Man siger, at komponenten fungerer som en <I>fabrik</I>, og at den
<I>fabrikerer</I> Image-objekter.</P>
<P CLASS="western">Image er en abstrakt klasse, der repr&aelig;senterer
et billede, der kan tegnes p&aring; sk&aelig;rmen. Ved hj&aelig;lp af
parameteren afg&oslash;r metoden getImage(), hvordan billedet skal
oprettes: Er parameteren et JPG-billede, vil getImage() v&aelig;lge
en nedarving af Image, der er velegnet til at repr&aelig;sentere
JPG-billeder. Er  parameteren et GIF-billede, vil en nedarving
velegnet til at repr&aelig;sentere GIF-billeder blive valgt. 
</P>
<H2 CLASS="western" STYLE="">16.3 <a name='afsn16.3'></a>Singleton</SPAN></H2>
<P CLASS="designm&oslash;nsteroverskrift-western">Problem: Klienten
m&aring; ikke have flere objekter af en bestemt type, men skal altid
bruge det samme objekt.</P>
<P CLASS="designm&oslash;nsteroverskrift-western">L&oslash;sning:
Programm&eacute;r s&aring;dan, at der aldrig kan oprettes mere end &eacute;t
eksemplar af det p&aring;g&aelig;ldende objekt.</P>
<BLOCKQUOTE CLASS="definition-western">En Singleton er en klasse, som
der m&aring; v&aelig;re &eacute;n og kun &eacute;n instans (objekt)
af</BLOCKQUOTE>
<P CLASS="western">Med en Singleton s&oslash;rger man alts&aring;
for, at der eksisterer h&oslash;jst &eacute;t objekt. Dette udf&oslash;res
som regel i koden ved at g&oslash;re konstrukt&oslash;ren privat og
lade klassen selv h&aring;ndtere oprettelsen af objektet (med en
klassemetode, der kan fabrikere objektet). 
</P>
<P CLASS="western">En Singleton bruges ofte til at repr&aelig;sentere
resurser som i deres natur kun skal eksistere &eacute;n gang. Det kan
for eksempel v&aelig;re en forbindelse til en database, en resurse
med begr&aelig;nset adgang, en systemresurse (f.eks. afspilning af
lyd), ...</P>
<H3 CLASS="western">16.3.1 <a name='afsn16.3.1'></a>Eksempel: java.lang.Runtime</H3>
<P CLASS="western">Runtime er et eksempel p&aring; en Singleton. 
</P>
<P CLASS="western">Ethvert k&oslash;rende javaprogram har et objekt
af type Runtime, der giver programmet adgang til de omgivelser, det
k&oslash;rer i. Man kan f.eks. kalde eksterne programmer, stoppe den
virtuelle maskine, unders&oslash;ge ledig hukommelse, k&oslash;re
garbage collector, indl&aelig;se flere klasser og et par andre ting.</P>
<P CLASS="western">Man f&aring;r fat i Runtime-objektet ved at kalde
metoden Runtime.getRuntime().</P>
<PRE CLASS="kode-western">  Runtime <B>rt = Runtime.getRuntime()</B>;

<SPAN LANG="da-DK">  <I>// eksempler p&aring; brug at Runtime-objektet</I></SPAN>
<SPAN LANG="da-DK">  System.out.println(&quot;Hukommelse reserveret til Java: &quot;+<B>rt.totalMemory()</B>);</SPAN>
<SPAN LANG="da-DK">  System.out.println(&quot;Heraf ledigt: &quot;+<B>rt.freeMemory()</B>);</SPAN>
<SPAN LANG="da-DK">  <B>rt.gc()</B>; <I>// k&oslash;r garbage collector</I></SPAN>
<SPAN LANG="da-DK">  System.out.println(&quot;Nu ledigt: &quot;+<B>rt.freeMemory()</B>);</SPAN></PRE><H3 CLASS="western">
16.3.2 <a name='afsn16.3.2'></a>Eksempel: java.awt.Toolkit</H3>
<P CLASS="western"><SPAN LANG="da-DK">Toolkit-klassen, der
repr&aelig;senterer de konkrete implementationer af AWT (Abstract
Window Toolkit), er et andet eksempel p&aring; en singleton. Det er
klart at der skal eksistere &eacute;t og kun &eacute;t grafisk system
samtidigt. </SPAN>
</P>
<P CLASS="western">Man f&aring;r fat i Toolkit-objektet med metoden
Toolkit.getDefaultToolkit():</P>
<PRE CLASS="kode-western">  Toolkit tk = Toolkit.getDefaultToolkit();</PRE><P CLASS="western">
Fabrikeringsmetoden getDefaultToolkit() returnerer altid det samme
objekt (ellers var det ikke en Singleton).</P>
<PRE CLASS="kode-western">  System.out.println(&quot;Sk&aelig;rmst&oslash;rrelse (punkter): &quot; + <B>tk.getScreenSize()</B>);
<SPAN LANG="da-DK">  <B>tk.beep()</B>; <I>// computeren siger bip</I></SPAN>
<SPAN LANG="da-DK">  Image i = tk.getImage(&quot;billede.gif&quot;);</SPAN></PRE><P CLASS="western">
I den sidste linje fungerer Toolkit-objektet ogs&aring; som fabrik
(der fabrikerer billeder).</P>


<H3 CLASS="western">16.3.3 <a name='afsn16.3.3'></a>Eksempel: Dataforbindelse</H3>
<P CLASS="western">En forbindelse til en database kan med fordel
implementeres som en Singleton, hvis man &oslash;nsker, at alle
foresp&oslash;rgsler skal g&aring; gennem det samme objekt. Det kunne
v&aelig;re for at cache foresp&oslash;rgslerne eller for at sikre
konsistens i data.</P>
<PRE CLASS="ikke-javakode-western">import java.util.*;

public class Dataforbindelse1
{
  /**
<SPAN LANG="da-DK"><I>   * Instansen (forekomsten, objektet) af dataforbindelsen.</I></SPAN>
<SPAN LANG="da-DK"><I>   * Dette er en klassevariabel, s&aring; den oprettes n&aring;r klassen indl&aelig;ses.</I></SPAN>
<SPAN LANG="da-DK"><I>   */</I></SPAN>
<SPAN LANG="da-DK">  <B>private static Dataforbindelse1 instans = new Dataforbindelse1()</B>;</SPAN>

<SPAN LANG="da-DK">  <I>/**</I></SPAN>
<SPAN LANG="da-DK"><I>   * Giver instansen af Dataforbindelse.</I></SPAN>
<SPAN LANG="da-DK"><I>   */</I></SPAN>
<SPAN LANG="da-DK">  <B>public static Dataforbindelse1 hentForbindelse()</B></SPAN>
<SPAN LANG="da-DK"><B>  { </B></SPAN>
<SPAN LANG="da-DK"><B>    return instans;</B></SPAN>
<SPAN LANG="da-DK"><B>  }</B></SPAN>

<SPAN LANG="da-DK">  <I>/**</I></SPAN>
<SPAN LANG="da-DK"><I>   * Privat konstrukt&oslash;r sikrer at der ikke kan oprettes objekter udenfor klassen.</I></SPAN>
<SPAN LANG="da-DK"><I>   */</I></SPAN>
<SPAN LANG="da-DK">  private Dataforbindelse1()</SPAN>
<SPAN LANG="da-DK">  {</SPAN>
<SPAN LANG="da-DK">    alle = new ArrayList();</SPAN>
<SPAN LANG="da-DK">  }</SPAN>

<SPAN LANG="da-DK">  private List alle;</SPAN>

<SPAN LANG="da-DK">  public void sletAlleData() { alle.clear(); }</SPAN>
<SPAN LANG="da-DK">  public void inds&aelig;t(Kunde k) { alle.add(k); }</SPAN>
<SPAN LANG="da-DK">  public List hentAlle() { return alle; }</SPAN>
<SPAN LANG="da-DK">}</SPAN></PRE><P CLASS="western">
Her er det sikret, at der ikke oprettes flere objekter ved at g&oslash;re
konstrukt&oslash;ren privat.</P>
<P CLASS="western">Dataforbindelsen bruges fra ens program ved at
bede om instansen i stedet for at oprette et nyt objekt:</P>
<PRE CLASS="ikke-javakode-western">public class BenytDataforbindelse1
<SPAN LANG="da-DK">{</SPAN>
<SPAN LANG="da-DK">  public static void main(String[] args)</SPAN>
<SPAN LANG="da-DK">  {</SPAN>
<SPAN LANG="da-DK">    Dataforbindelse1 dbf = Dataforbindelse1.hentForbindelse();</SPAN>
<SPAN LANG="da-DK">    dbf.inds&aelig;t( new Kunde(&quot;Kurt&quot;,1000) );</SPAN>
<SPAN LANG="da-DK">    // ...</SPAN>
<SPAN LANG="da-DK">  }</SPAN>
<SPAN LANG="da-DK">}</SPAN></PRE>

<H3 CLASS="western">16.3.4 <a name='afsn16.3.4'></a>Singleton med sen initialisering</H3>
<P CLASS="western">I ovenst&aring;ende eksempel blev
Dataforbindelse-objektet oprettet ved opstart af programmet, n&aring;r
klassen blev indl&aelig;st. Har singletonen brug for, at andre ting
er initialiseret, f&oslash;r den oprettes, eller er det slet ikke
sikkert, at singletonen overhovedet bliver brugt, kan det v&aelig;re
hensigtsm&aelig;ssigt at udskyde oprettelsen af objektet, indtil
f&oslash;rste gang der er brug for det.</P>
<PRE CLASS="ikke-javakode-western">import java.util.*;

public class Dataforbindelse2
{
  /**
<SPAN LANG="da-DK"><I>   * Instansen (forekomsten, objektet) af dataforbindesen.</I></SPAN>
<SPAN LANG="da-DK"><I>   * Dette er en klassevariabel, s&aring; den oprettes n&aring;r klassen indl&aelig;ses.</I></SPAN>
<SPAN LANG="da-DK"><I>   */</I></SPAN>
<SPAN LANG="da-DK"><B>  private static Dataforbindelse2 instans = null;</B></SPAN>

<SPAN LANG="da-DK"><I>  /**</I></SPAN>
<SPAN LANG="da-DK"><I>   * Giver instansen af Dataforbindelse2.</I></SPAN>
<SPAN LANG="da-DK"><I>   * Instansen bliver oprettet f&oslash;rste gang denne metode kaldes.</I></SPAN>
<SPAN LANG="da-DK"><I>   */</I></SPAN>
<SPAN LANG="da-DK"><B>  public static synchronized Dataforbindelse2 hentForbindelse()</B></SPAN>
<SPAN LANG="da-DK"><B>  {</B></SPAN>
<SPAN LANG="da-DK"><B>    if (instans != null) return instans;</B></SPAN>
<SPAN LANG="da-DK"><B>    instans = new Dataforbindelse2();</B></SPAN>
<SPAN LANG="da-DK"><B>    return instans;</B></SPAN>
<SPAN LANG="da-DK"><B>  }</B></SPAN>

<SPAN LANG="da-DK"><I>  /**</I></SPAN>
<SPAN LANG="da-DK"><I>   * En privat konstrukt&oslash;r sikrer at der ikke kan oprettes objekter uden</I></SPAN>
<SPAN LANG="da-DK"><I>   * for klassen.</I></SPAN>
<SPAN LANG="da-DK"><I><B>   */</B></I></SPAN>
<SPAN LANG="da-DK">  private Dataforbindelse2()</SPAN>
<SPAN LANG="da-DK">  {</SPAN>
<SPAN LANG="da-DK">    alle = new ArrayList();</SPAN>
<SPAN LANG="da-DK">  }</SPAN>

<SPAN LANG="da-DK">  private List alle;</SPAN>

<SPAN LANG="da-DK">  public void sletAlleData() { alle.clear(); }</SPAN>
<SPAN LANG="da-DK">  public void inds&aelig;t(Kunde k) { alle.add(k); }</SPAN>
<SPAN LANG="da-DK">  public List hentAlle() { return alle; }</SPAN>
<SPAN LANG="da-DK">}</SPAN></PRE><P CLASS="western">
Bem&aelig;rk, at da man kunne v&aelig;re s&aring; uheldig, at to
tr&aring;de samtidig kalder hentForbindelse(), er det n&oslash;dvendigt,
at den er synkroniseret, s&aring; tr&aring;dene k&oslash;rer en ad
gangen i metoden.</P>
<P CLASS="western">At kalde en metode, der er synkroniseret, g&aring;r
lidt langsommere, da den virtuelle maskine skal lave noget arbejde
for at sikre enkeltradet genneml&oslash;b af metoden.</P>


<H3 CLASS="western">16.3.5 <a name='afsn16.3.5'></a>Singleton uden privat konstrukt&oslash;r</H3>
<P CLASS="western">Den mest almindelige m&aring;de at gennemtvinge en
Singleton p&aring; er at lave konstrukt&oslash;ren privat. Den har
dog det problem, at den forhindrer nedarvinger (f.eks.
specialiseringer i forskellige slags datalagre). Det kunne dog l&oslash;ses
ved at erkl&aelig;re konstrukt&oslash;ren protected og l&aelig;gge
klassen og dens nedarvinger i en separat pakke (se <a href='kapitel15.jsp#afsn15.7.4'>afsnit 15.7.4</a>, Indkapsling og pakker).</P>
<P CLASS="western">En anden mulighed for at sikre, at der kun er &eacute;t
objekt (der evt. er en nedarving), er at kaste en undtagelse fra
konstrukt&oslash;ren (alts&aring; p&aring; k&oslash;retidspunktet at
n&aelig;gte at oprette objektet), hvis objektet allerede eksisterer.</P>
<P CLASS="western">Nedenfor er vist en Dataforbindelse-klasse der,
f&oslash;rste gang hentForbindelse() bliver kaldt, beslutter om det
skal v&aelig;re en Dataforbindelse eller nedarvingen
SpecialiseretDataforbindelse, der skal bruges.</P>
<PRE CLASS="ikke-javakode-western">import java.util.*;

public class Dataforbindelse3
<SPAN LANG="da-DK">{</SPAN>
  /**
<SPAN LANG="da-DK"><I>   * Instansen (forekomsten, objektet) af dataforbindelsen.</I></SPAN>
<SPAN LANG="da-DK"><I>   * Dette er en klassevariabel, s&aring; den oprettes n&aring;r klassen indl&aelig;ses.</I></SPAN>
<SPAN LANG="da-DK"><I>   */</I></SPAN>
<SPAN LANG="da-DK">  <B>private static Dataforbindelse3 instans</B>;</SPAN>

<SPAN LANG="da-DK"><I>  /**</I></SPAN>
<SPAN LANG="da-DK"><I>   * Giver instansen af Dataforbindelse.</I></SPAN>
<SPAN LANG="da-DK"><I>   */</I></SPAN>
<SPAN LANG="da-DK">  <B>public static Dataforbindelse3 hentForbindelse()</B></SPAN>
<SPAN LANG="da-DK">  { </SPAN>
<SPAN LANG="da-DK">    if (instans != null) return instans;</SPAN>

<SPAN LANG="da-DK"><B>    if (<I> ...normal brug... </I>) return new Dataforbindelse3();</B></SPAN>
<SPAN LANG="da-DK">    else return new SpecialiseretDataforbindelse();</SPAN>
<SPAN LANG="da-DK">  }</SPAN>

<SPAN LANG="da-DK"><I>  /**</I></SPAN>
<SPAN LANG="da-DK"><I>   * Konstrukt&oslash;ren sikrer at der ikke kan oprettes flere instanser.</I></SPAN>
<SPAN LANG="da-DK"><I>   * Da den ikke er erkl&aelig;ret public er det under alle omst&aelig;ndigheder kun</I></SPAN>
<SPAN LANG="da-DK"><I>   * muligt at oprette objekter inden for samme pakke</I></SPAN>
<SPAN LANG="da-DK"><I>   * @throws IllegalAccessException hvis instansen allerede eksisterer.</I></SPAN>
<SPAN LANG="da-DK"><I>   */</I></SPAN>
<SPAN LANG="da-DK">  protected Dataforbindelse3()</SPAN>
<SPAN LANG="da-DK">  {</SPAN>
<SPAN LANG="da-DK">    if (instans != null) throw new IllegalAccessException(&quot;Objekt eksisterer&quot;);</SPAN>
<SPAN LANG="da-DK">    alle = new ArrayList();</SPAN>
<SPAN LANG="da-DK">    instans = this;</SPAN>
<SPAN LANG="da-DK">  }</SPAN>

<SPAN LANG="da-DK">  private List alle;</SPAN>

<SPAN LANG="da-DK">  public void sletAlleData() { alle.clear(); }</SPAN>
<SPAN LANG="da-DK">  public void inds&aelig;t(Kunde k) { alle.add(k); }</SPAN>
<SPAN LANG="da-DK">  public List hentAlle() { return alle; }</SPAN>
<SPAN LANG="da-DK">}</SPAN></PRE>
<H2 CLASS="western" STYLE="">16.4 <a name='afsn16.4'></a>Abstrakt
Fabrik</SPAN></H2>
<P CLASS="designm&oslash;nsteroverskrift-western">Problem: En Fabrik
bliver uforholdsm&aelig;ssigt kompliceret, fordi nogle omst&aelig;ndigheder
har stor indflydelse p&aring;, hvordan oprettelsen skal foreg&aring;.</P>
<P CLASS="designm&oslash;nsteroverskrift-western">L&oslash;sning: Lav
en Abstrakt Fabrik (eng.: Abstract Factory) med en nedarving (Fabrik)
for hver omst&aelig;ndighed.</P>
<BLOCKQUOTE CLASS="definition-western">En Abstrakt Fabrik er en
Fabrik med nedarvinger, og disse nedarvinger s&oslash;rger for den
egentlige objektoprettelse</BLOCKQUOTE>
<P CLASS="western">En Abstrakt Fabrik kaldes ogs&aring; undertiden
Kit eller Toolkit.</P>
<P CLASS="western">Forestil dig, at du har en Fabrik, men at du st&aring;r
i den situation, at der er nogle ydre omst&aelig;ndigheder, der g&oslash;r,
at der nogen gange skal oprettes en anden slags objekter.</P>
<P CLASS="western">Man kunne selvf&oslash;lgelig klare det med nogle
if-s&aelig;tninger i fabrikeringsmetod(erne), men det bliver b&oslash;vlet,
hvis der p&aring; denne m&aring;de kommer et stort antal if-s&aelig;tninger
i hver fabrikeringsmetode, eller hvis der er et stort antal
fabrikeringsmetoder.</P>
<P CLASS="western">En smartere l&oslash;sning er at lade
fabrik-klassen v&aelig;re abstrakt og lave en nedarving for hver
omst&aelig;ndighed (som man ellers klarede med en if-s&aelig;tning). 
</P>
<P CLASS="western">Som regel er det altid den samme nedarvede Fabrik,
der skal bruges hver gang. S&aring; bruger man ofte
Singleton-designm&oslash;nstret p&aring; superklassen ved at give den
en metode til at fremskaffe nedarvingen.</P>
<H3 CLASS="western">16.4.1 <a name='afsn16.4.1'></a>Eksempel: AdresseFabrik</H3>
<P CLASS="western">Forestil dig, at du har et program til at lagre
internationale telefonnumre og adresser. For hvert land er
udformningen af telefonnumre og adresser lidt forskellige. Man kunne
lave en fabrik, som indeholdt en masse if-s&aelig;tninger afh&aelig;ngigt
af, hvilket land der var tale om, men det ville give en hel del kode
i fabrik-klassen, og hver gang man skal tilf&oslash;je adresse og
telefonnummer for et nyt land, skal man ind og rette i fabrikken.</P>
<P CLASS="western">S&aring; er det bedre at lave Fabrik-klasser, der
arver fra en overordnet Fabrik, og som s&oslash;rger for den
specifikke oprettelse af adresse og telefonnummer klasser for hvert
land, og s&aring; evt. lade den kode, som er generel for alle
adresseklasserne, ligge i superklassen.</P>
<PRE CLASS="kode-western">public class BenytAdresseFabrik {
  public static void main(String[] args)
  {
     AbstraktFabrikIF adresseFabrikRef;

     adresseFabrikRef = new DKAdresseFabrik();
     adresseFabrikRef.opretAdresse();
     adresseFabrikRef.opretTelefonNr();

<SPAN LANG="da-DK"><I>     // i dette eksempel v&aelig;lger klienten selv hvilken nedarving den vil bruge.</I></SPAN>
<SPAN LANG="da-DK">     adresseFabrikRef = new GRBAdresseFabrik();</SPAN>
     adresseFabrikRef.opretAdresse();
     adresseFabrikRef.opretTelefonNr();
  }
<SPAN LANG="da-DK">}</SPAN></PRE><P CLASS="western">
Den overordnede Fabrik skal der normalt ikke laves instanser af (den
kunne alts&aring; erkl&aelig;res abstrakt - deraf navnet Abstrakt
Fabrik). I dette eksempel har vi derfor valgt at lade det v&aelig;re
et interface:</P>
<PRE CLASS="kode-western">public interface AbstraktFabrikIF
<SPAN LANG="da-DK">{</SPAN>
<SPAN LANG="da-DK">   public Adresse opretAdresse();</SPAN>
<SPAN LANG="da-DK">   public TelefonNr opretTelefonNr();</SPAN>
<SPAN LANG="da-DK">}</SPAN></PRE><P CLASS="western">
<IMG SRC="bog17_html_2aa44ddc.gif" NAME="Grafik12" ALIGN=BOTTOM BORDER=0></P>

<PRE CLASS="kode-western">public class DKAdresseFabrik implements AbstraktFabrikIF
<SPAN LANG="da-DK">{</SPAN>
<SPAN LANG="da-DK">   public Adresse opretAdresse() {</SPAN>
<SPAN LANG="da-DK">      return new DKAdresse();</SPAN>
<SPAN LANG="da-DK">   }</SPAN>

<SPAN LANG="da-DK">   public TelefonNr opretTelefonNr() {</SPAN>
<SPAN LANG="da-DK">      return new DKTelefonNr();</SPAN>
<SPAN LANG="da-DK">   }</SPAN>
<SPAN LANG="da-DK">}</SPAN></PRE>
<PRE CLASS="kode-western">public class GRBAdresseFabrik implements AbstraktFabrikIF
<SPAN LANG="da-DK">{</SPAN>
<SPAN LANG="da-DK">   public Adresse opretAdresse() {</SPAN>
<SPAN LANG="da-DK">      return new GRBAdresse();</SPAN>
<SPAN LANG="da-DK">   }</SPAN>
<SPAN LANG="da-DK">   public TelefonNr opretTelefonNr() {</SPAN>
<SPAN LANG="da-DK">      return new GRBTelefonNr();</SPAN>
<SPAN LANG="da-DK">   }</SPAN>
<SPAN LANG="da-DK">}</SPAN></PRE>

<P CLASS="western" STYLE="">Adresse-klassen
skulle have de metoder og variabler, der forventes at findes i alle
adresser:</P>
<PRE CLASS="kode-western">public abstract class Adresse 
<SPAN LANG="da-DK">{</SPAN>
<SPAN LANG="da-DK">  <I>// mangler: metoder/variabler der findes i alle adresser</I></SPAN>
<SPAN LANG="da-DK">}</SPAN></PRE>
<PRE CLASS="kode-western">public class DKAdresse extends Adresse 
<SPAN LANG="da-DK">{</SPAN>
<SPAN LANG="da-DK">  public DKAdresse() {</SPAN>
<SPAN LANG="da-DK">     System.out.println(&quot;DKAdresse oprettet&quot;);</SPAN>
<SPAN LANG="da-DK">  }</SPAN>
<SPAN LANG="da-DK">}</SPAN></PRE>
<PRE CLASS="kode-western">public class GRBAdresse extends Adresse
<SPAN LANG="da-DK">{</SPAN>
<SPAN LANG="da-DK">  public GRBAdresse() {</SPAN>
<SPAN LANG="da-DK">     System.out.println(&quot;GRBAdresse oprettet&quot;);</SPAN>
<SPAN LANG="da-DK">  }</SPAN>
<SPAN LANG="da-DK">}</SPAN></PRE>
<P CLASS="western">Ligeledes med telefonnumre</P>
<PRE CLASS="kode-western">public abstract class TelefonNr 
<SPAN LANG="da-DK">{</SPAN>
<SPAN LANG="da-DK">  <I>// mangler: metoder/variabler der findes i alle telefonnumre</I></SPAN>
<SPAN LANG="da-DK">}</SPAN></PRE>
<PRE CLASS="kode-western">public class GRBTelefonNr extends TelefonNr 
<SPAN LANG="da-DK">{</SPAN>
<SPAN LANG="da-DK">  public GRBTelefonNr() {</SPAN>
<SPAN LANG="da-DK">     System.out.println(&quot;GRBTelefonNr oprettet&quot;);</SPAN>
<SPAN LANG="da-DK">  }</SPAN>
<SPAN LANG="da-DK">}</SPAN></PRE>
<PRE CLASS="kode-western">public class DKTelefonNr extends TelefonNr
<SPAN LANG="da-DK">{</SPAN>
<SPAN LANG="da-DK">  public DKTelefonNr() {</SPAN>
<SPAN LANG="da-DK">     System.out.println(&quot;DKTelefonNr oprettet&quot;);</SPAN>
<SPAN LANG="da-DK">  }</SPAN>
<SPAN LANG="da-DK">}</SPAN></PRE><H3 CLASS="western">
16.4.2 <a name='afsn16.4.2'></a>Eksempel fra standardklasserne: AWT</H3>
<P CLASS="western">Klassen Toolkit er et eksempel p&aring; en
Abstrakt Fabrik.</P>
<P CLASS="western">Da AWT-komponenterne skal virke p&aring; flere
platforme (Windows, Linux, Solaris, MacOS), fungerer de s&aring;dan,
at de har en platformsspecifik del (et s&aring;kaldt <I>peer</I><SPAN STYLE="font-style: normal">)
tilknyttet, der s&oslash;rger for at tegne dem p&aring; sk&aelig;rmen.</SPAN></P>
<P CLASS="western">F.eks. har klassen Button et ButtonPeer-objekt
<SPAN STYLE="font-style: normal">(beskrevet i pakken java.awt.peer),
som det delegerer nogle opgaver ud til. Da disse peers er
implementeret forskelligt fra platform til platform, er der
tilsvarende nedarvinger af ButtonPeer for de forskellige platforme,
f.eks. WindowsButtonPeer, LinuxToolkit, SolarisToolkit og
MacOSToolkit.</SPAN></P>
<P CLASS="western" STYLE="font-style: normal">Disse peer-objekter
fabrikeres af klassen Toolkit, der har f.eks. metoden createButton
til at oprette et  ButtonPeer-objekt.</P>
<P CLASS="western" STYLE="font-style: normal">I stedet for, at
Toolkit direkte kender til de forskellige styresystemer, er klassen
erkl&aelig;ret abstrakt, og der er en nedarving af Toolkit for hvert
styresystem (f.eks. WindowsToolkit, LinuxToolkit, SolarisToolkit og
MacOSToolkit).</P>
<P CLASS="western" STYLE="font-style: normal">Metoden
Toolkit.getDefaultToolkit() giver den konkrete nedarving, der passer
til styresystemet.</P>
<H2 CLASS="western">16.5 <a name='afsn16.5'></a>Bygmester</SPAN></H2>
<P CLASS="designm&oslash;nsteroverskrift-western">Problem: Der skal
oprettes flere objekter, som h&aelig;nger sammen.</P>
<P CLASS="designm&oslash;nsteroverskrift-western">Eller problem: De
n&oslash;dvendige informationer til oprettelse af nogle objekter
kommer ikke alle p&aring; &eacute;n gang, eller det er uoverskueligt
at angive alle informationerne i &eacute;t kald.</P>
<P CLASS="designm&oslash;nsteroverskrift-western">L&oslash;sning:
Brug en Bygmester (eng.: Builder) til at specificere informationerne
skridt for skridt og til sidst fabrikere objekterne.</P>
<BLOCKQUOTE CLASS="definition-western">Simplific&eacute;r oprettelsen
af nogle relaterede objekter ved at lave en Bygmester-klasse, der
opretter og konfigurerer objekterne for klienten</BLOCKQUOTE>
<P CLASS="western">En Bygmester er en Fabrik, der, evt. trinvist,
opretter et netv&aelig;rk af objekter. En Bygmester minder ogs&aring;
om en Facade (se <a href='kapitel17.jsp#afsn17.4'>afsnit 17.4</a> om Facade), men i stedet for at v&aelig;re
en simplificering af <I>brug</I> af nogle objekter er det en
simplificering af <I>oprettelsen</I> af nogle objekter.</P>
<P CLASS="western">Ligesom en Fabrik kan en Bygmester lade klienter
oprette objekter ved at angive indhold og type. Klienten kender ikke
til detaljerne omkring oprettelsen.</P>

<H3 CLASS="western">16.5.1 <a name='afsn16.5.1'></a>Trinvis konstruktion</H3>
<P CLASS="western">Bygmesteren kan have metoder til at konfigurere
objektet skridt for skridt. Til sidst kaldes en fabrikeringsmetode p&aring;
bygmesteren, som returnerer det f&aelig;rdige objekt. 
</P>
<P CLASS="western">Dette er forskelligt fra en Fabrik, hvor
konstruktion sker med et enkelt metodekald.</P>
<H4 CLASS="western">Hvorn&aring;r bruge trinvis konstruktion?</H4>
<P CLASS="western">Trinvis oprettelse kan v&aelig;re berettiget i
forskellige situationer:</P>
<UL>
  <LI><P CLASS="western">For at opn&aring; st&oslash;rre l&aelig;sbarhed
  - i stedet for store indviklede konstrukt&oslash;rer</P>
  <LI><P CLASS="western">Oplysninger er ikke alle til stede n&aring;r
  oprettelsen p&aring;begyndes, f.eks.:</P>
  <LI><P CLASS="western">P&aring; grund af m&aring;den, et filformat
  er organiseret p&aring; (ved indl&aelig;sning af data)</P>
  <LI><P CLASS="western">Brugeren er ved at indtaste nogle data.
  Efterh&aring;nden som data indtastes gives de videre til
  Bygmesteren, der registrerer og validerer dem (f.eks. en aftale i en
  kalender).</P>
  <LI><P CLASS="western">Beslutningen om, pr&aelig;cis hvilken klasse
  der skal oprettes, kan f&oslash;rst tages, n&aring;r alle
  oplysningerne er til stede (dette er f.eks. ikke muligt ved f&oslash;rst
  at skabe objektet med en Fabrik og derefter l&aelig;gge data ind i
  det resulterende objekt).</P>
</UL>



<H3 CLASS="western">16.5.2 <a name='afsn16.5.2'></a>Eksempel p&aring; trinvis konstruktion:
E-post</H3>
<P CLASS="western">Forestil dig et system, der kan afsende e-post.</P>
<P CLASS="western">Uden en Bygmester skulle klienten kalde en
konstrukt&oslash;r med alle parametrene. Konstrukt&oslash;ren kunne
f.eks. se s&aring;ledes ud:</P>
<PRE CLASS="kode-western">  public Meddelelse(
<SPAN LANG="da-DK">    Adresse afsender, </SPAN>
<SPAN LANG="da-DK">    Adresse[] modtagere, </SPAN>
<SPAN LANG="da-DK">    Date tidspunkt,</SPAN>
<SPAN LANG="da-DK">    String[] supplerendeData,</SPAN>
<SPAN LANG="da-DK">    String hovedtekst,</SPAN>
<SPAN LANG="da-DK">    Vedhaeftning[] vedh&aelig;ftedeData</SPAN>
<SPAN LANG="da-DK">  )</SPAN></PRE>
<P CLASS="western">S&aring; ville oprettelsen f.eks. se s&aring;ledes
ud:</P>
<PRE CLASS="kode-western">  Meddelelse m = new Meddelelse(
<SPAN LANG="da-DK">    new Adresse(&quot;nordfalk@mobilixnet.dk&quot;), </SPAN>
<SPAN LANG="da-DK">    new Adresse[] { new Adresse(&quot;bo@hansen.dk&quot;), new Adresse(&quot;hans@hansen.dk&quot;) } </SPAN>
<SPAN LANG="da-DK">    new Date(),</SPAN>
<SPAN LANG="da-DK">    null,</SPAN>
<SPAN LANG="da-DK">    &quot;En hilsen fra Jacob&quot;,</SPAN>
<SPAN LANG="da-DK">    null</SPAN>
<SPAN LANG="da-DK">  );</SPAN></PRE>
<P CLASS="western">Med en Bygmester sker oprettelsen skridt for
skridt, og det er umiddelbart nemmere at forst&aring;, hvad der
foreg&aring;r:</P>
<PRE CLASS="kode-western">  Meddelelsesbygger mb = new Meddelelsesbygger(); <I>// eller en fabrikeringsmetode</I>

<SPAN LANG="da-DK">  mb.s&aelig;tAfsender(&quot;nordfalk@mobilixnet.dk&quot;);</SPAN>
<SPAN LANG="da-DK">  mb.tilf&oslash;jModtager(&quot;bo@hansen.dk&quot;);</SPAN>
<SPAN LANG="da-DK">  mb.tilf&oslash;jModtager(&quot;hans@hansen.dk&quot;) </SPAN>
<SPAN LANG="da-DK">  mb.s&aelig;tHovedtekst(&quot;En hilsen fra Jacob&quot;);</SPAN>

<SPAN LANG="da-DK">  Meddelelse m = mb.byg();</SPAN></PRE>

<H3 CLASS="western">16.5.3 <a name='afsn16.5.3'></a>Eksempel p&aring; netv&aelig;rk af
objekter: Funktioner</H3>
<P CLASS="western"><SPAN LANG="da-DK">I eksemplet <a href='kapitel4.jsp#afsn4.7.1'>afsnit 4.7.1</a>, En komponent til at tegne kurver,
har klassen Funktionsfortolker i <a href='kapitel4.jsp#afsn4.7.3'>afsnit 4.7.3</a> rollen som Bygmester,
idet den opretter og konfigurerer Funktion-objekterne  til at passe
med en bestemt formel.</SPAN></P>

<H2 CLASS="western" STYLE="">16.6 <a name='afsn16.6'></a>Prototype</SPAN></H2>
<P CLASS="designm&oslash;nsteroverskrift-western">Problem: Klienten
ved ikke, hvad der skal oprettes, men kan dog angive et andet objekt,
som ligner det, der skal oprettes.</P>
<P CLASS="designm&oslash;nsteroverskrift-western">L&oslash;sning:
Brug det andet objekt som Prototype, og opret objektet ud fra
prototypen.</P>
<BLOCKQUOTE CLASS="definition-western">Tag et eksisterende objekt
(prototypen), lav en kopi, og ret kopien til efter behov</BLOCKQUOTE>
<P CLASS="western">Med Prototype kan man lade et objekt oprette en
kopi af sig selv p&aring; bestilling udefra. Klienten, der bestiller
oprettelsen, kan s&aring; arbejde med objektet og kopien uden at vide
eksakt hvad objektet indeholder eller detaljerne i, hvordan objektet
oprettes.</P>
<P CLASS="western">Prototype-designm&oslash;nstret best&aring;r alts&aring;
i at oprette objekter ud fra et allerede eksisterende objekt,
hvorefter kopien tilrettes s&aring;dan, at den passer til form&aring;let.</P>
<P CLASS="western">Det kan ogs&aring; ske, at klienten beder et andet
objekt (en Fabrik eller Bygmester) om at oprette objektet. Klienten
angiver s&aring;, hvad der skal oprettes, og fabrikken finder s&aring;
den rette Prototype, kopierer den og retter den til.</P>
<P CLASS="western">Prototype-designm&oslash;nstret har flere fordele:</P>
<UL>
  <LI><P CLASS="western">Klienten beh&oslash;ver ikke at kende den
  pr&aelig;cise m&aring;de, objektet skal oprettes p&aring;.</P>
  <LI><P CLASS="western">Der kan v&aelig;re data i objektet, som
  klienten ikke kender til.</P>
  <LI><P CLASS="western">Klienten kan have en liste af prototyper at
  oprette objekter ud fra. Denne liste kan udvides, hvorved klienten
  uden videre f&aring;r et st&oslash;rre repertoire af objekter, den
  kan oprette. 
  </P>
</UL>
<H3 CLASS="western">16.6.1 <a name='afsn16.6.1'></a>Interfacet Cloneable</H3>
<P CLASS="western">I Java er Prototype-designm&oslash;nstret specielt
let at implementere, da ethvert objekt har metoden clone() (arvet fra
superklassen Object), der returnerer en kopi af objektet. Metoden kan
dog kun kaldes p&aring; objekter, der tillader det. En klasse kan
give tilladelse til at blive klonet ved at den (eller dens
superklasse) implementerer interfacet Cloneable.</P>
<P CLASS="western">Ligesom Serializable markerer, at et objekt godt
m&aring; serialiseres, markerer Cloneable, at et objekt godt m&aring;
klones (kopieres ved at kalde clone()). Kaldes clone() p&aring; et
objekt, der ikke implementerer Cloneable, opst&aring;r undtagelsen
CloneNotSupportedException.</P>
<H3 CLASS="western">16.6.2 <a name='afsn16.6.2'></a>Overfladisk og dyb kopiering</H3>
<P CLASS="western">Metoden clone() kopierer objektvariablerne, uanset
om det er simple typer eller referencer til andre objekter. Hvis
f.eks. et objekt husker en liste af strenge i et Vector-objekt, og vi
kalder clone() p&aring; objektet for at f&aring; en kopi, vil kopien
bruge det samme Vector-objekt (da det kun var objektreferencen der
blev kopieret), og hvis &quot;den enes&quot; liste &aelig;ndres, vil
&quot;den andens&quot; liste ogs&aring; blive &aelig;ndret. Dette
kaldes en <I>overfladisk kopi</I> (eng.: shallow copy), da kopien har
referencer til de samme objekter som originalen.</P>
<P CLASS="western">En <I>dyb kopi</I> (eng.: deep copy) er en
kopiering, hvor alle de refererede objekter ogs&aring; kopieres,
s&aring;dan at originalen og kopien er helt uafh&aelig;ngige og ikke
deler objekter. &Oslash;nsker man at lave en dyb kopi af et objekt,
m&aring; man selv skrive koden<A CLASS="sdfootnoteanc" NAME="sdfootnote1anc" HREF="#sdfootnote1sym"><SUP>1</SUP></A>.</P>
<H3 CLASS="western">16.6.3 <a name='afsn16.6.3'></a>Eksempel: Et tegneprogram</H3>
<P CLASS="western">Forestil dig et program, der kan tegne forskellige
former og figurer p&aring; sk&aelig;rmen. Programmet har en palette
af figurer. Brugeren v&aelig;lger en figur fra paletten og klikker
derefter p&aring; sk&aelig;rmen der, hvor den skal v&aelig;re.</P>
<P CLASS="western">Programmet skal alts&aring; registrere, hvilken
figur brugeren har valgt, og derefter oprette et figur-objekt af den
rette type og tegne det p&aring; sk&aelig;rmen med de rette
koordinater.</P>
<P CLASS="western">En (ikke s&aelig;rlig raffineret) m&aring;de at
l&oslash;se denne opgave p&aring; er med en r&aelig;kke if-s&aelig;tninger.
Hvis der blev klikket p&aring; figur 1, s&aring; opret et
Figur1-objekt, hvis der blev klikket p&aring; figur 2, s&aring; opret
et Figur2-objekt, osv.</P>
<P CLASS="western">I en mere raffineret l&oslash;sning ville der v&aelig;re
en liste af figur-prototyper, og paletten viser alle elementerne i
denne liste. N&aring;r der klikkes p&aring; paletten, findes frem til
det p&aring;g&aelig;ldende element, og det anvendes som Prototype til
at oprette objektet, der skal tegnes p&aring; sk&aelig;rmen. 
</P>
<P CLASS="western">Denne l&oslash;sning har den fordel, at paletten
l&oslash;bende kan udvides med nye figurer (og derfor er den mere
fleksibel end f.eks. en Abstrakt Fabrik). Brugeren kan ogs&aring;
tage en eksisterende figur og &aelig;ndre den (f.eks. farven og om
den er udfyldt) og l&aelig;gge den ind i paletten.</P>
<P CLASS="western">Brugeren kunne m&aring;ske endda kombinere nogle
af figurobjekterne, der tilsammen udgjorde f.eks. et hus eller en
mand, i et samlet Figur-objekt (se <a href='kapitel18.jsp#afsn18.5'>afsnit 18.5</a>, Komposit/Rekursiv komposition),
og udvide paletten med denne figur.</P>
<P CLASS="western">Her er et udkast til koden til eksemplet. 
</P>
<P CLASS="western">Superklassen Figur implementerer Cloneable og har
metoden kopi():</P>
<PRE CLASS="kode-western">public abstract class Figur implements Cloneable
<SPAN LANG="da-DK">{</SPAN>
<SPAN LANG="da-DK">  int x;</SPAN>
<SPAN LANG="da-DK">  int y;</SPAN>

<SPAN LANG="da-DK">  public Figur(int x1, int y1)</SPAN>
<SPAN LANG="da-DK">  {</SPAN>
<SPAN LANG="da-DK">    x=x1; y=y1;</SPAN>
<SPAN LANG="da-DK">  }</SPAN>

<SPAN LANG="da-DK"><I>  /** Tegner figuren p&aring; sk&aelig;rmen */</I></SPAN>
<SPAN LANG="da-DK">  public abstract void tegn(java.awt.Graphics g);</SPAN>

<SPAN LANG="da-DK"><I>  /** Giver en overfladisk kopi af dette objekt */</I></SPAN>
<SPAN LANG="da-DK">  public Figur kopi() </SPAN>
<SPAN LANG="da-DK">  {</SPAN>
<SPAN LANG="da-DK">    try {</SPAN>
<SPAN LANG="da-DK">      Figur kopien = (Figur) this.clone();</SPAN>
<SPAN LANG="da-DK">      return kopien;</SPAN>
<SPAN LANG="da-DK">    } catch (Exception e) { throw new InternalError(&quot;Kunne ikke klone&quot;); }</SPAN>
<SPAN LANG="da-DK">  }</SPAN>
<SPAN LANG="da-DK">}</SPAN></PRE>
<P CLASS="western">Cirkel har en radius:</P>
<PRE CLASS="kode-western">public class Cirkel extends Figur
<SPAN LANG="da-DK">{</SPAN>
<SPAN LANG="da-DK">  int radius;</SPAN>

<SPAN LANG="da-DK">  public Cirkel(int x1, int y1, int radius1)</SPAN>
<SPAN LANG="da-DK">  {</SPAN>
<SPAN LANG="da-DK">    super(x1,y1);</SPAN>
<SPAN LANG="da-DK">    radius = radius1;</SPAN>
<SPAN LANG="da-DK">  }</SPAN>

<SPAN LANG="da-DK"><I>  public void tegn(java.awt.Graphics g)</I></SPAN>
<SPAN LANG="da-DK"><I>  {</I></SPAN>
<SPAN LANG="da-DK">    g.drawOval(x,y,radius,radius);</SPAN>
<SPAN LANG="da-DK">  }</SPAN>
<SPAN LANG="da-DK">}</SPAN></PRE><P CLASS="western">
Firkant har en bredde og h&oslash;jde:</P>
<PRE CLASS="kode-western">public class Firkant extends Figur
<SPAN LANG="da-DK">{</SPAN>
<SPAN LANG="da-DK">  int bredde;</SPAN>
<SPAN LANG="da-DK">  int h&oslash;jde;</SPAN>

<SPAN LANG="da-DK">  public Firkant(int x1, int y1, int bredde1, int h&oslash;jde1)</SPAN>
<SPAN LANG="da-DK">  {</SPAN>
<SPAN LANG="da-DK">    super(x1,y1);</SPAN>
<SPAN LANG="da-DK">    bredde = bredde1;</SPAN>
<SPAN LANG="da-DK">    h&oslash;jde  = h&oslash;jde1;</SPAN>
<SPAN LANG="da-DK">  }</SPAN>

<SPAN LANG="da-DK">  public void tegn(java.awt.Graphics g) </SPAN>
<SPAN LANG="da-DK">  {</SPAN>
<SPAN LANG="da-DK">    g.drawRect(x,y,bredde,h&oslash;jde);</SPAN>
<SPAN LANG="da-DK">  }</SPAN>
<SPAN LANG="da-DK">}</SPAN></PRE>
<P CLASS="western">Paletten indeholder figurerne og deres navne (til
f.eks. at lade brugeren v&aelig;lge mellem dem i en valgliste).
Paletten fungerer som bygmester med metoden opretFigur(), der
opretter en figur ud fra prototype og derefter giver kopien de
rigtige koordinater.</P>
<P CLASS="western">Metoden tilf&oslash;j() kan bruges udefra til at
f&oslash;je nye figurer til paletten.</P>
<PRE CLASS="kode-western">import java.util.*;
public class Palette
{
  /**
   * @associates &lt;{Figur}&gt;
   * @supplierCardinality 0..*
   */
  private List figurer = new ArrayList();
<SPAN LANG="da-DK">  private List navne = new ArrayList();</SPAN>

<SPAN LANG="da-DK">  public Palette()</SPAN>
<SPAN LANG="da-DK">  {</SPAN>
<SPAN LANG="da-DK">    tilf&oslash;j(&quot;Cirkel&quot;, new Cirkel(10,10,10));</SPAN>
<SPAN LANG="da-DK">    tilf&oslash;j(&quot;Firkant&quot;, new Firkant(0,0,20,20));</SPAN>
<SPAN LANG="da-DK">  }</SPAN>

<SPAN LANG="da-DK">  public void tilf&oslash;j(String navn, Figur f)</SPAN>
<SPAN LANG="da-DK">  {</SPAN>
<SPAN LANG="da-DK">    navne.add(navn);</SPAN>
<SPAN LANG="da-DK">    figurer.add(f);</SPAN>
<SPAN LANG="da-DK">  }</SPAN>

<SPAN LANG="da-DK">  public List navneliste()</SPAN>
<SPAN LANG="da-DK">  {</SPAN>
<SPAN LANG="da-DK">    return navne;</SPAN>
<SPAN LANG="da-DK">  }</SPAN>

<SPAN LANG="da-DK">  public Figur opretFigur(int indeks, int x, int y)</SPAN>
<SPAN LANG="da-DK">  {</SPAN>
<SPAN LANG="da-DK">    Figur f = (Figur) figurer.get(indeks);</SPAN>
<SPAN LANG="da-DK">    f = f.kopi();</SPAN>
<SPAN LANG="da-DK">    f.x = x;</SPAN>
<SPAN LANG="da-DK">    f.y = y;</SPAN>
<SPAN LANG="da-DK">    return f;</SPAN>
<SPAN LANG="da-DK">  }</SPAN>
<SPAN LANG="da-DK">}</SPAN></PRE>


<P CLASS="western"><IMG SRC="bog17_html_587d571f.gif" NAME="Grafik4" ALIGN=BOTTOM BORDER=0></P>

<P CLASS="western">xxx Figurtegning fra</P>
<P CLASS="western">
/home/j/dokumenter/vp/dm_eks_prototype_komposit_kommando/Figurtegning.java</P>
<P CLASS="western">ind</P>
<H3 CLASS="western">16.6.4 <a name='afsn16.6.4'></a>Opgave</H3>
<P CLASS="western">Defin&eacute;r endnu en figur kaldet xxx</P>

<H2 CLASS="western" STYLE="">16.7 <a name='afsn16.7'></a>Objektpulje</SPAN></H2>
<P CLASS="designm&oslash;nsteroverskrift-western">Problem: Der er et
begr&aelig;nset antal resurser, som skal fordeles.</P>
<P CLASS="designm&oslash;nsteroverskrift-western">Problem: Der
oprettes for mange objekter. Programmet er langsomt eller k&oslash;rer
uj&aelig;vnt, fordi der oprettes s&aring; mange objekter, der l&oslash;bende
smides v&aelig;k igen. Objekterne kunne egentligt godt genbruges i
stedet for at blive smidt v&aelig;k, men oprettelserne sker spredt
rundt i programmet, s&aring; det er sv&aelig;rt at koordinere.</P>
<P CLASS="designm&oslash;nsteroverskrift-western">L&oslash;sning: Lad
en Objektpulje (eng.: Object Pool) varetage resurserne/objekterne, og
lad klienterne reservere og frigive objekter fra objektpuljen.</P>
<P CLASS="western">Id&eacute;en er at genbruge allerede eksisterende
objekter, ved at have en pulje med ledige objekter, og p&aring; den
m&aring;de undg&aring; eller begr&aelig;nse oprettelsen af nye
objekter.</P>
<BLOCKQUOTE CLASS="definition-western">Genbrug de samme objekter igen
og igen ved at lave en Objektpulje, der husker objekterne</BLOCKQUOTE>
<P CLASS="western">Objektpuljen har en fabrikeringsmetode, der
fjerner et objekt fra puljen af ledige objekter og returnerer den.
Hvis puljen er tom, oprettes et nyt objekt. Der skal ogs&aring; v&aelig;re
en metode til at l&aelig;gge et objekt, der ikke mere er i brug,
tilbage i puljen.</P>
<P CLASS="western">En Objektpulje kan ogs&aring; bruges til at
repr&aelig;sentere begr&aelig;nsede resurser. I dette tilf&aelig;lde
vil fabrikeringsmetoden, i stedet for at oprette nye objekter, vente,
indtil et objekt bliver ledigt, f&oslash;r metoden returnerer (den
kaldende tr&aring;d bliver alts&aring; stoppet).</P>
<H3 CLASS="western">16.7.1 <a name='afsn16.7.1'></a>Eksempel: Begr&aelig;nsede resurser</H3>
<P CLASS="western">F&oslash;lgende klasse er en meget simpel
objektpulje til begr&aelig;nsede resurser. Hvis puljen l&oslash;ber
t&oslash;r for objekter, kastes en undtagelse.</P>
<PRE CLASS="kode-western">import java.util.*;
<SPAN LANG="da-DK"><I>/**<BR> * En pulje af objekter.<BR> * Objekterne skal f&oslash;rst f&oslash;jes til puljen udefra med kald til s&aelig;tInd() <BR> */<BR></I>public class Objektpulje<BR>{<BR>  private ArrayList ledige = new ArrayList();<BR><BR><I>  /**<BR>   * L&aelig;g et ledigt objekt ind i puljen.<BR>   * Bruges b&aring;de til at initialisere puljen lige efter dens oprettelse,<BR>   * og l&oslash;bende, n&aring;r et fjernet objekt bliver ledigt igen.<BR>   */<BR></I><B>  public synchronized void s&aelig;tInd(Object obj)<BR></B>  {<BR>    ledige.add(obj);<BR>  }<BR></SPAN>
<SPAN LANG="da-DK"><I>  /**<BR>   * Tag et ledigt objekt ud af puljen.</I></SPAN>
<SPAN LANG="da-DK"><I>   * @throws RuntimeException hvis puljen er l&oslash;bet t&oslash;r for objekter.<BR>   */<BR></I><B>  public synchronized Object tagUd()<BR></B>  {<BR>    if (ledige.isEmpty()) throw new RuntimeException(&quot;Ikke flere objekter&quot;);<BR>    Object obj = ledige.remove(ledige.size()-1);<I> // tag objekt ud af puljen</I><BR>    return obj;<BR>  }<BR>}</SPAN></PRE>
<H3 CLASS="western">16.7.2 <a name='afsn16.7.2'></a>Variation: Klient 'h&aelig;nger', hvis
puljen l&oslash;ber t&oslash;r</H3>
<P CLASS="western">I nogle tilf&aelig;lde er det mere
hensigtsm&aelig;ssigt, at objektpuljen lader kalderen vente p&aring;,
at der bliver et objekt ledigt. Det f&oslash;lgende eksempel er en
objektpulje til begr&aelig;nsede resurser, hvor den kaldende tr&aring;d,
hvis puljen er tom,  'h&aelig;nger', indtil der kommer et ledigt
objekt.</P>
<PRE CLASS="kode-western">import java.util.*;
<SPAN LANG="da-DK"><I>/**<BR> * En pulje af objekter. <BR> * Objekterne skal tilf&oslash;jes til puljen udefra.<BR> */<BR></I>public class ObjektpuljeKlientHaenger<BR>{<BR>  private ArrayList ledige = new ArrayList();</SPAN>
<SPAN LANG="da-DK"><BR><I>  /**<BR>   * L&aelig;g et ledigt objekt ind i puljen.<BR>   * Bruges b&aring;de til at initialisere puljen lige efter dens oprettelse,<BR>   * og l&oslash;bende, n&aring;r et fjernet objekt bliver ledigt igen.<BR>   */<BR></I><B>  public synchronized void s&aelig;tInd(Object obj)<BR></B>  {<BR>    ledige.add(obj);<BR>    this.notify();<I> // v&aelig;k eventuelle ventende tr&aring;de</I><BR>  }<BR><BR><I>  /**<BR>   * Tag et ledigt objekt ud af puljen.<BR>   * Er der ikke flere objekter tilbage 'h&aelig;nger' kaldet, indtil<BR>   * et objekt bliver ledigt.<BR>   */<BR></I><B>  public synchronized Object tagUd()<BR></B>  {<BR>    try {<BR>      while (ledige.isEmpty())<I> // s&aring; l&aelig;nge der ikke er ledige objekter...</I><BR>      {<BR>        System.out.println(&quot;Ikke flere objekter i puljen, venter...&quot;);<BR>        this.wait();<I>           // .... vent p&aring; at blive v&aelig;kket</I><BR>      }<BR>      Object obj = ledige.remove(ledige.size()-1);<I> // tag objekt</I><BR>      return obj;<BR>    } catch (InterruptedException e) {<BR>      e.printStackTrace();<BR>      return null;<BR>    }<BR>  }<BR>}</SPAN></PRE>
<H3 CLASS="western" STYLE="">16.7.3 <a name='afsn16.7.3'></a>Genbrug
af tr&aring;de</H3>
<P CLASS="western">At oprette en tr&aring;d er dyrt i k&oslash;rselstid.
&Oslash;nsker man derfor et hurtigt system, b&oslash;r man genbruge
tr&aring;de i stedet for at oprette og nedl&aelig;gge dem alt for
ofte.</P>
<P CLASS="western">Det f&oslash;lgende eksempel viser, hvordan en
pulje af tr&aring;de kan implementeres. En tr&aring;d aktiveres ved
at kalde metoden startOpgave() med et objekt, der s&aring; vil f&aring;
kaldt sin run()-metode i en separat arbejds-tr&aring;d (er der ingen
ledige tr&aring;de, oprettes en ny).</P>
<PRE CLASS="kode-western">import java.util.*;

<SPAN LANG="da-DK">public class Traadpulje</SPAN>
<SPAN LANG="da-DK">{</SPAN>
<SPAN LANG="da-DK">  private List ledige = new ArrayList(); <I>// Listen over ledige arbejdstr&aring;de</I></SPAN>

<SPAN LANG="da-DK"><I>  /** L&aelig;g en opgave i k&oslash; til en arbejdstr&aring;d */</I></SPAN>
<SPAN LANG="da-DK">  public synchronized void startOpgave(Runnable opgave)</SPAN>
<SPAN LANG="da-DK">  {</SPAN>
<SPAN LANG="da-DK">    Arbejder a;</SPAN>

<SPAN LANG="da-DK">    if (ledige.isEmpty())</SPAN>
<SPAN LANG="da-DK">    {</SPAN>
<SPAN LANG="da-DK">      a = new Arbejder(); <I>// ingen arbejdstr&aring;de ledige, en ny oprettes</I></SPAN>
<SPAN LANG="da-DK">      a.setDaemon(true);<I>  // tillad systemet at lukke ned selvom tr&aring;den er aktiv</I></SPAN>
<SPAN LANG="da-DK">      System.out.println(&quot;Ny arbejdstr&aring;d oprettet.&quot;);</SPAN>
<SPAN LANG="da-DK">      a.start();</SPAN>
<SPAN LANG="da-DK">    } else synchronized(ledige) {</SPAN>
<SPAN LANG="da-DK">      a = (Arbejder) ledige.remove(ledige.size()-1);<I>// tag arbejdstr&aring;d fra liste</I></SPAN>
<SPAN LANG="da-DK">    }</SPAN>

<SPAN LANG="da-DK">    synchronized(a)</SPAN>
<SPAN LANG="da-DK">    {</SPAN>
<SPAN LANG="da-DK">      if (a.opgave != null) throw new InternalError(&quot;Tr&aring;den k&oslash;rer allerede&quot;);</SPAN>
<SPAN LANG="da-DK">      a.opgave = opgave;</SPAN>
<SPAN LANG="da-DK">      a.notify();         <I>// v&aelig;k arbejdstr&aring;den der venter i wait()</I></SPAN>
<SPAN LANG="da-DK">    }</SPAN>
<SPAN LANG="da-DK">  }</SPAN>

<SPAN LANG="da-DK"><I>  /**</I></SPAN>
<SPAN LANG="da-DK"><I>   * Arbejds-tr&aring;den.</I></SPAN>
<SPAN LANG="da-DK"><I>   * Den er st&aelig;rkt bundet til puljen s&aring; den er lagt som en privat indre klasse.</I></SPAN>
<SPAN LANG="da-DK"><I>   */</I></SPAN>
<SPAN LANG="da-DK">  private class Arbejder extends Thread</SPAN>
<SPAN LANG="da-DK">  {</SPAN>
<SPAN LANG="da-DK">    private Runnable opgave = null;</SPAN>

<SPAN LANG="da-DK">    public final synchronized void run()</SPAN>
<SPAN LANG="da-DK">    {</SPAN>
<SPAN LANG="da-DK">      while (true) try</SPAN>
<SPAN LANG="da-DK">      {</SPAN>
<SPAN LANG="da-DK">        if (opgave != null)</SPAN>
<SPAN LANG="da-DK">        {</SPAN>
<SPAN LANG="da-DK">          System.out.println(this+&quot; virker nu p&aring; &quot;+opgave);</SPAN>
<SPAN LANG="da-DK">          opgave.run();     <I> // udf&oslash;r opgaven</I></SPAN>
<SPAN LANG="da-DK">          opgave = null;    <I> // ... og glem den (!)</I></SPAN>
<SPAN LANG="da-DK">          synchronized(ledige) {ledige.add(this);} <I>// l&aelig;g tr&aring;d tilbage i listen </I></SPAN>
<SPAN LANG="da-DK">        }</SPAN>
<SPAN LANG="da-DK">        System.out.println(this+&quot; venter p&aring; opgave.&quot;);</SPAN>
<SPAN LANG="da-DK">        this.wait();        <I> // vent p&aring; at blive v&aelig;kket med notify()</I></SPAN>
<SPAN LANG="da-DK">      } catch (Exception e) {</SPAN>
<SPAN LANG="da-DK">        System.err.println(this+&quot;: Fejl opstod i opgave &quot;+opgave);</SPAN>
<SPAN LANG="da-DK">        e.printStackTrace();</SPAN>
<SPAN LANG="da-DK">      }</SPAN>
<SPAN LANG="da-DK">    } <I>// slut p&aring; run()</I></SPAN>
<SPAN LANG="da-DK">  }<I>  // slut p&aring; den indre klasse</I></SPAN>
<SPAN LANG="da-DK">}</SPAN></PRE>

<P CLASS="western" STYLE=""><SPAN LANG="da-DK">Tr&aring;dpuljen
kunne for eksempel udnyttes i en webserver (her er den anvendt p&aring;
eksemplet FlertraadetHjemmesidevaert fra <a href='kapitel17.jsp'>kapitel 17</a> i
</SPAN><SPAN STYLE="font-weight: medium"><A CLASS="western" HREF="http://javabog.dk/"><SPAN LANG="da-DK">http://javabog.dk</SPAN></A></SPAN><SPAN LANG="da-DK">):</SPAN></P>
<PRE CLASS="kode-western">import java.io.*;
<SPAN LANG="da-DK">import java.net.*;</SPAN>
<SPAN LANG="da-DK">import java.util.*;</SPAN>

<SPAN LANG="da-DK">public class FlertraadetHjemmesidevaertMedTraadpulje<BR>{<BR>  public static void main(String arg[])<BR>  {<BR>    try {<BR>      ServerSocket v&aelig;rtssokkel = new ServerSocket(8001);<BR><BR>      <B>Traadpulje tr&aring;dpulje = new Traadpulje()</B>;<I>          // nyt</I><BR>      while (true)<BR>      {<BR>        Socket forbindelse = v&aelig;rtssokkel.accept();<BR>        Anmodning a = new Anmodning(forbindelse);<BR><BR>        <B>tr&aring;dpulje.startOpgave(a)</B>;              <I>         // nyt</I><BR><I>        // f&oslash;r: new Thread(a).start();<BR></I>      }<BR>    } catch (Exception e) {<BR>      e.printStackTrace();<BR>    }<BR>  }</SPAN>
<SPAN LANG="da-DK">}</SPAN>

<SPAN LANG="da-DK">class Anmodning implements Runnable</SPAN>
<SPAN LANG="da-DK">{</SPAN>
<SPAN LANG="da-DK">  private Socket forbindelse;</SPAN>

<SPAN LANG="da-DK">  Anmodning(Socket forbindelse)</SPAN>
<SPAN LANG="da-DK">  {</SPAN>
<SPAN LANG="da-DK">    this.forbindelse = forbindelse;</SPAN>
<SPAN LANG="da-DK">  }</SPAN>

<SPAN LANG="da-DK">  public void run()</SPAN>
<SPAN LANG="da-DK">  {</SPAN>
<SPAN LANG="da-DK">    try {</SPAN>
<SPAN LANG="da-DK">      PrintWriter ud = new PrintWriter(forbindelse.getOutputStream());</SPAN>
<SPAN LANG="da-DK">      BufferedReader ind = new BufferedReader(</SPAN>
<SPAN LANG="da-DK">        new InputStreamReader(forbindelse.getInputStream()));</SPAN>

<SPAN LANG="da-DK">      String anmodning = ind.readLine();</SPAN>
<SPAN LANG="da-DK">      System.out.println(&quot;start &quot;+new Date()+&quot; &quot;+anmodning);</SPAN>

<SPAN LANG="da-DK">      ud.println(&quot;HTTP/0.9 200 OK&quot;);</SPAN>
<SPAN LANG="da-DK">      ud.println();</SPAN>
<SPAN LANG="da-DK">      ud.println(&quot;&lt;html&gt;&lt;head&gt;&lt;title&gt;Svar&lt;/title&gt;&lt;/head&gt;&quot;);</SPAN>
<SPAN LANG="da-DK">      ud.println(&quot;&lt;body&gt;&lt;h1&gt;Svar&lt;/h1&gt;&quot;);</SPAN>
<SPAN LANG="da-DK">      ud.println(&quot;T&aelig;nker over &quot;+anmodning+&quot;&lt;br&gt;&quot;);</SPAN>
<SPAN LANG="da-DK">      for (int i=0; i&lt;100; i++) </SPAN>
<SPAN LANG="da-DK">      {</SPAN>
<SPAN LANG="da-DK">        ud.print(&quot;.&lt;br&gt;&quot;);</SPAN>
<SPAN LANG="da-DK">        ud.flush();</SPAN>
<SPAN LANG="da-DK">        Thread.sleep(100);</SPAN>
<SPAN LANG="da-DK">      }</SPAN>
<SPAN LANG="da-DK">      ud.println(&quot;Nu har jeg t&aelig;nkt f&aelig;rdig!&lt;/body&gt;&lt;/html&gt;&quot;);</SPAN>
<SPAN LANG="da-DK">      ud.flush();</SPAN>
<SPAN LANG="da-DK">      forbindelse.close();</SPAN>
<SPAN LANG="da-DK">      System.out.println(&quot;slut  &quot;+new Date()+&quot; &quot;+anmodning);</SPAN>
<SPAN LANG="da-DK">    } catch (Exception e) {</SPAN>
<SPAN LANG="da-DK">      e.printStackTrace();</SPAN>
<SPAN LANG="da-DK">    }</SPAN>
<SPAN LANG="da-DK">  }</SPAN>
<SPAN LANG="da-DK">}</SPAN></PRE><H3 CLASS="western">
16.7.4 <a name='afsn16.7.4'></a>Opgave: Objektpulje med objektfabrik</H3>
<P CLASS="western">Udvid Objektpulje, s&aring;dan at den kan f&aring;
overf&oslash;rt en Objektfabrik i konstrukt&oslash;ren. Klassen
Objektfabrik har metoden opretObjekt(), der skaber nye objekter til
puljen. &AElig;ndr puljen til at bruge fabrikken, s&aring;dan at der
oprettes nye objekter, hvis puljen l&oslash;ber t&oslash;r.</P>
<H2 CLASS="western">16.8 <a name='afsn16.8'></a>St&oslash;rre eksempel: Dataforbindelse</SPAN></H2>
<P CLASS="western">En forbindelse til en database kan med fordel
implementeres som en Singleton, hvis man &oslash;nsker, at alle
foresp&oslash;rgsler skal g&aring; gennem det samme objekt. Det kunne
v&aelig;re for at cache foresp&oslash;rgslerne eller for at sikre
konsistens i data.</P>
<P CLASS="western">Det f&oslash;lgende er ogs&aring; et eksempel p&aring;
indkapsling og abstraktion - al SQL-specifik kode er pakket ind i en
klasse for sig, i et Dataforbindelse-objekt. 
</P>
<P CLASS="western"><SPAN LANG="da-DK">For at abstraktionen skal
holde, skal de data (her drejer det sig om kunder), der overf&oslash;res
mellem dataforbindelsen og klientprogrammet, ogs&aring; repr&aelig;senteres
p&aring; en m&aring;de, s&aring; de ikke er afh&aelig;ngige af, at
kommunikation</SPAN>en skal foreg&aring; med JDBC til en
SQL-database:</P>
<PRE CLASS="kode-western">import java.io.*;

public class Kunde implements Serializable
{
  public String navn;
  public double kredit;

  public Kunde(String n, double k)
  {
    navn = n;
    kredit = k;
  }

  public String toString() { return navn+&quot;: &quot;+kredit+&quot; kr.&quot;; }
}</PRE>
<P CLASS="western">Kl<SPAN LANG="da-DK">ient-programmet kan s&aring;
abstrahere fra, hvordan data er lagret:</SPAN></P>
<PRE CLASS="kode-western">import java.util.*;

<SPAN LANG="da-DK">public class BenytDataforbindelse</SPAN>
<SPAN LANG="da-DK">{</SPAN>
<SPAN LANG="da-DK">  public static void main(String arg[])</SPAN>
<SPAN LANG="da-DK">  {</SPAN>
<SPAN LANG="da-DK">    try {</SPAN>
<SPAN LANG="da-DK">      Dataforbindelse dbf = Dataforbindelse.hentForbindelse();</SPAN>

<SPAN LANG="da-DK">      List liste = dbf.hentAlle();</SPAN>
<SPAN LANG="da-DK">      System.out.println(&quot;Alle data: &quot;+ liste);</SPAN>
<SPAN LANG="da-DK">      dbf.sletAlleData();</SPAN>

<SPAN LANG="da-DK">      System.out.println(&quot;Alle data nu: &quot;+dbf.hentAlle());</SPAN>

<SPAN LANG="da-DK">      dbf.inds&aelig;t( new Kunde(&quot;Kurt&quot;,1000) );</SPAN>
<SPAN LANG="da-DK">      dbf.inds&aelig;t( new Kunde(&quot;kunde indsat fra BenytDataforbindelse&quot;, 2) );</SPAN>
<SPAN LANG="da-DK">      System.out.println(&quot;Alle data nu: &quot;+dbf.hentAlle());</SPAN>
<SPAN LANG="da-DK">    } catch(Exception e) {</SPAN>
<SPAN LANG="da-DK">      System.out.println(&quot;Problem med dataforbindelse: &quot;+e);</SPAN>
<SPAN LANG="da-DK">      e.printStackTrace();</SPAN>
<SPAN LANG="da-DK">    }</SPAN>
<SPAN LANG="da-DK">  }</SPAN>
<SPAN LANG="da-DK">}</SPAN></PRE>
<HR>
<PRE CLASS="kode-western">Alle data: [Kurt: 1000.0 kr., kunde indsat fra BenytDataforbindelse: 2.0 kr.]
<SPAN LANG="da-DK">Alle data nu: []</SPAN>
<SPAN LANG="da-DK">Alle data nu: [Kurt: 1000.0 kr., kunde indsat fra BenytDataforbindelse: 2.0 kr.]</SPAN></PRE>
<P CLASS="western">Herunder er Dataforbindelse programmeret som en
Singleton (noget af koden er sk&aring;ret v&aelig;k). 
</P>
<PRE CLASS="ikke-javakode-western">import java.util.*;

<SPAN LANG="da-DK">public abstract class Dataforbindelse</SPAN>
<SPAN LANG="da-DK">{</SPAN>
<SPAN LANG="da-DK">  abstract <B>public void sletAlleData()</B> throws Exception;</SPAN>
<SPAN LANG="da-DK">  abstract <B>public void inds&aelig;t(Kunde k)</B> throws Exception;</SPAN>
<SPAN LANG="da-DK">  abstract <B>public List hentAlle()</B> throws Exception;</SPAN>

<SPAN LANG="da-DK">  // fabrikeringsmetode</SPAN>
<SPAN LANG="da-DK">  synchronized <B>public static Dataforbindelse hentForbindelse()</B> throws Exception</SPAN>
<SPAN LANG="da-DK">  {</SPAN>
<SPAN LANG="da-DK">    // - her implementeret som en Singleton</SPAN>

<SPAN LANG="da-DK">    if (forb != null) return forb;</SPAN>
<SPAN LANG="da-DK">    // ... her skal kode ind der skaffer en forbindelse</SPAN>

<SPAN LANG="da-DK">    forb = new DataforbindelseDummy();    // lige nu</SPAN>
<SPAN LANG="da-DK">    //forb = new DataforbindelseOracle(); // senere</SPAN>
<SPAN LANG="da-DK">    //forb = new DataforbindelseOverNetvaerketTilEnServlet(); // endnu senere!!</SPAN>

<SPAN LANG="da-DK">    return forb;</SPAN>
<SPAN LANG="da-DK">  }</SPAN>
<SPAN LANG="da-DK">  private static Dataforbindelse forb;</SPAN>
<SPAN LANG="da-DK">}</SPAN></PRE>
<P CLASS="western">Fabrikeringsmetoden
Dataforbindelse.hentForbindelse() kan senere &aelig;ndres til at
skaffe andre slags forbindelser, uden at der skal rettes i
klient-programmet.</P>
<H3 CLASS="western">16.8.1 <a name='afsn16.8.1'></a>Specialiseringer af Dataforbindelse</H3>
<P CLASS="western">Der kan v&aelig;re forskellige specialiseringer af
Dataforbindelse til forskellige situationer:</P>
<P CLASS="western"><IMG SRC="bog17_html_3e3d2cab.gif" NAME="Grafik8" ALIGN=BOTTOM BORDER=0></P>

<P CLASS="western">I starten af programmeringen udvikles nok en
DataforbindelseDummy (eller DataforbindelseStub), der kun er beregnet
til test. Den lader, som om den er en forbindelse, men g&oslash;r
reelt ingen verdens ting:</P>
<PRE CLASS="kode-western">import java.util.*;

<SPAN LANG="da-DK">public class DataforbindelseDummy extends Dataforbindelse</SPAN>
<SPAN LANG="da-DK">{</SPAN>
<SPAN LANG="da-DK">  public void sletAlleData()  { System.out.println(&quot;sletAlleData() kaldt&quot;); }</SPAN>
<SPAN LANG="da-DK">  public void inds&aelig;t(Kunde k) { System.out.println(&quot;inds&aelig;t(&quot;+k+&quot;) kaldt&quot;); }</SPAN>

<SPAN LANG="da-DK">  public List hentAlle()</SPAN>
<SPAN LANG="da-DK">  {</SPAN>
<SPAN LANG="da-DK">    List alle = new ArrayList();</SPAN>
<SPAN LANG="da-DK">    Kunde k = new Kunde( &quot;Jacob&quot;, -1722);</SPAN>
<SPAN LANG="da-DK">    alle.add(k);</SPAN>
<SPAN LANG="da-DK">    return alle;</SPAN>
<SPAN LANG="da-DK">  }</SPAN>
<SPAN LANG="da-DK">}</SPAN></PRE><P CLASS="western">
I denne udgave har vi ikke en gang gidet at huske data (selvom det
ville v&aelig;re nemt nok i dette tilf&aelig;lde).</P>
<P CLASS="western" STYLE="">Senere laves en,
der husker data, men serialiseret i en fil:</P>
<PRE CLASS="kode-western">import java.util.*;
<SPAN LANG="da-DK">import java.io.*;</SPAN>
<SPAN LANG="da-DK">public class DataforbindelseFil extends Dataforbindelse {</SPAN>
<SPAN LANG="da-DK">  private List alle;</SPAN>

<SPAN LANG="da-DK">  private void gem() {</SPAN>
<SPAN LANG="da-DK">    try {</SPAN>
<SPAN LANG="da-DK">      ObjectOutputStream p = new ObjectOutputStream(</SPAN>
<SPAN LANG="da-DK">        new FileOutputStream(&quot;data.ser&quot;));</SPAN>
<SPAN LANG="da-DK">      p.writeObject(alle);</SPAN>
<SPAN LANG="da-DK">      p.close();</SPAN>
<SPAN LANG="da-DK">    } catch (Exception e) { e.printStackTrace(); }</SPAN>
<SPAN LANG="da-DK">  }</SPAN>

<SPAN LANG="da-DK">  public DataforbindelseFil() {</SPAN>
<SPAN LANG="da-DK">    try {</SPAN>
<SPAN LANG="da-DK">      ObjectInputStream p = new ObjectInputStream(</SPAN>
<SPAN LANG="da-DK">        new FileInputStream(&quot;data.ser&quot;));</SPAN>
<SPAN LANG="da-DK">      alle = (List) p.readObject();</SPAN>
<SPAN LANG="da-DK">      p.close();</SPAN>
<SPAN LANG="da-DK">    } catch (Exception e) {</SPAN>
<SPAN LANG="da-DK">      alle = new ArrayList();</SPAN>
<SPAN LANG="da-DK">    }</SPAN>
<SPAN LANG="da-DK">  }</SPAN>

<SPAN LANG="da-DK">  public void sletAlleData() {</SPAN>
<SPAN LANG="da-DK">    alle = new ArrayList();</SPAN>
<SPAN LANG="da-DK">    gem();</SPAN>
<SPAN LANG="da-DK">  }</SPAN>

<SPAN LANG="da-DK">  public void inds&aelig;t(Kunde k) {</SPAN>
<SPAN LANG="da-DK">    alle.add(k);</SPAN>
<SPAN LANG="da-DK">    gem();</SPAN>
<SPAN LANG="da-DK">  }</SPAN>

<SPAN LANG="da-DK">  public List hentAlle() { return alle; }</SPAN>
<SPAN LANG="da-DK">}</SPAN></PRE>
<P CLASS="western">Senere, n&aring;r de rigtige tabeller osv. er
blevet oprettet i databasen, laves en rigtig forbindelse:</P>
<PRE CLASS="kode-western">import java.sql.*;
<SPAN LANG="da-DK">import java.util.*;</SPAN>
<SPAN LANG="da-DK">public class DataforbindelseOracle extends Dataforbindelse {</SPAN>
<SPAN LANG="da-DK">  private Connection forb;</SPAN>
<SPAN LANG="da-DK">  private Statement stmt;</SPAN>

<SPAN LANG="da-DK">  public DataforbindelseOracle() throws Exception {</SPAN>
<SPAN LANG="da-DK">    Class.forName(&quot;oracle.jdbc.driver.OracleDriver&quot;);</SPAN>
<SPAN LANG="da-DK">    Connection forb = DriverManager.getConnection(</SPAN>
<SPAN LANG="da-DK">      &quot;jdbc:oracle:thin:@ora.javabog.dk:1521:student&quot;,&quot;stuk1001&quot;,&quot;hemli'&quot;);</SPAN>
<SPAN LANG="da-DK">    stmt = forb.createStatement();</SPAN>
<SPAN LANG="da-DK">  }</SPAN>

<SPAN LANG="da-DK">  public void sletAlleData() throws SQLException {</SPAN>
<SPAN LANG="da-DK">    stmt.execute(&quot;truncate table kunder&quot;);</SPAN>
<SPAN LANG="da-DK">  }</SPAN>

<SPAN LANG="da-DK">  public void inds&aelig;t(Kunde k) throws SQLException {</SPAN>
<SPAN LANG="da-DK">    stmt.executeUpdate(</SPAN>
<SPAN LANG="da-DK">      &quot;INSERT INTO kunder VALUES('&quot; + k.navn + &quot;', &quot; + k.kredit + &quot;)&quot;);</SPAN>
<SPAN LANG="da-DK">  }</SPAN>

<SPAN LANG="da-DK">  public List hentAlle() throws SQLException {</SPAN>
<SPAN LANG="da-DK">    List alle = new ArrayList();</SPAN>
<SPAN LANG="da-DK">    ResultSet rs = stmt.executeQuery(&quot;SELECT navn, kredit FROM kunder&quot;);</SPAN>
<SPAN LANG="da-DK">    while (rs.next())</SPAN>
<SPAN LANG="da-DK">    {</SPAN>
<SPAN LANG="da-DK">      Kunde k = new Kunde( rs.getString(&quot;navn&quot;), rs.getDouble(&quot;kredit&quot;));</SPAN>
<SPAN LANG="da-DK">      alle.add(k);</SPAN>
<SPAN LANG="da-DK">    }</SPAN>
<SPAN LANG="da-DK">    return alle;</SPAN>
<SPAN LANG="da-DK">  }</SPAN>
<SPAN LANG="da-DK">}</SPAN></PRE><H3 CLASS="western">
16.8.2 <a name='afsn16.8.2'></a>Dataforbindelse over netv&aelig;rk</H3>
<P CLASS="western">Endnu senere kan det v&aelig;re, at programmet er
udvidet med en forbindelse, der kontakter en servlet p&aring; en
webserver et andet sted p&aring; netv&aelig;rket. Alts&aring; et
objekt, der 'lader som om', det er det rigtige objekt (en
Dataforbindelse), men i virkeligheden sender kaldene videre til det
rigtige Dataforbindelse-objekt (der i dette tilf&aelig;lde ligger p&aring;
en anden maskine). Dette kaldes ogs&aring; en Proxy - se <a href='kapitel17.jsp#afsn17.1'>afsnit 17.1</a>.</P>
<P CLASS="western">Herunder er s&aring;dan en forbindelse
implementeret. Den sender en GET-anmodning med HTTP-protokollen
v.hj.a. URL-klassen. Med anmodningen sendes parameteren kommando, der
beskriver, hvad der skal g&oslash;res. Eventuelle data indkodes med
URLEncoder-klassen (i metoden inds&aelig;t()). For eksempel kunne en
anmodning om at slette alle data se s&aring;ledes ud:</P>
<PRE CLASS="kode-western">http://localhost:8080/servlet/DataforbindelseServlet?kommando=sletAlleData</PRE>
<P CLASS="western">De fleste kommandoer forventer ikke noget svar.
Eneste undtagelse er kommando=hentAlle, der forventer, at servletten
sender bin&aelig;re data tilbage i form af serialiserede objekter.</P>
<PRE CLASS="kode-western">import java.util.*;
<SPAN LANG="da-DK">import java.net.*;</SPAN>
<SPAN LANG="da-DK">import java.io.*;</SPAN>

<SPAN LANG="da-DK"><I>/**</I></SPAN>
<SPAN LANG="da-DK"><I> * Dataforbindelse over netv&aelig;rket til en servlet.</I></SPAN>
<SPAN LANG="da-DK"><I> * @see DataforbindelseServlet</I></SPAN>
<SPAN LANG="da-DK"><I> */</I></SPAN>
<SPAN LANG="da-DK">public class DataforbindelseOverNetvaerketTilEnServlet extends Dataforbindelse</SPAN>
<SPAN LANG="da-DK">{</SPAN>
<SPAN LANG="da-DK">  private String basisUrl;</SPAN>

<SPAN LANG="da-DK">  private InputStream sp&oslash;rg(String spm) throws IOException</SPAN>
<SPAN LANG="da-DK">  {</SPAN>
<SPAN LANG="da-DK">    System.out.println(&quot;Sp&oslash;rger p&aring; &quot;+ basisUrl+&quot;?&quot;+spm);</SPAN>
<SPAN LANG="da-DK">    URL u = new URL(basisUrl+&quot;?&quot;+spm);<I>// opret URL</I></SPAN>
<SPAN LANG="da-DK">    u.openConnection().connect();<I>     // send foresp&oslash;rgslen</I></SPAN>
<SPAN LANG="da-DK">    return u.openStream();       <I>     // returner datastr&oslash;m med svaret</I></SPAN>
<SPAN LANG="da-DK">  }</SPAN>

<SPAN LANG="da-DK">  public DataforbindelseOverNetvaerketTilEnServlet(String urlP&aring;Servlet)</SPAN>
<SPAN LANG="da-DK">  {</SPAN>
<SPAN LANG="da-DK">    basisUrl = urlP&aring;Servlet;</SPAN>
<SPAN LANG="da-DK">  }</SPAN>

<SPAN LANG="da-DK">  public void sletAlleData() throws IOException</SPAN>
<SPAN LANG="da-DK">  {</SPAN>
<SPAN LANG="da-DK">    sp&oslash;rg(&quot;kommando=sletAlleData&quot;);</SPAN>
<SPAN LANG="da-DK">  }</SPAN>

<SPAN LANG="da-DK">  public void inds&aelig;t(Kunde k) throws IOException</SPAN>
<SPAN LANG="da-DK">  {</SPAN>
<SPAN LANG="da-DK">    sp&oslash;rg(&quot;kommando=inds&aelig;t&quot;</SPAN>
<SPAN LANG="da-DK">      +&quot;&amp;navn=&quot;   +URLEncoder.encode(k.navn)        <I>// indkod navn</I> </SPAN>
<SPAN LANG="da-DK">      +&quot;&amp;kredit=&quot; +URLEncoder.encode(&quot;&quot;+k.kredit)); <I>// indkode kredit</I></SPAN>
<SPAN LANG="da-DK">  }</SPAN>

<SPAN LANG="da-DK">  public List hentAlle() throws Exception</SPAN>
<SPAN LANG="da-DK">  {</SPAN>
<SPAN LANG="da-DK">    InputStream is = sp&oslash;rg(&quot;kommando=hentAlle&quot;);</SPAN>
<SPAN LANG="da-DK">    ObjectInputStream p = new ObjectInputStream(is);</SPAN>
<SPAN LANG="da-DK">    List alle = (List) p.readObject();              <I>// deserialis&eacute;r liste-objekt</I></SPAN>
<SPAN LANG="da-DK">    p.close();</SPAN>

<SPAN LANG="da-DK">    return alle;</SPAN>
<SPAN LANG="da-DK">  }</SPAN>
<SPAN LANG="da-DK">}</SPAN></PRE><P CLASS="western">
Herunder er den tilsvarende servlet. Den bruger et
dataforbindelses-objekt p&aring; <I>v&aelig;rten</I> til at afg&oslash;re,
hvad den skal svare. Her er det vigtigt at denne Dataforbindelse p&aring;
v&aelig;rtsmaskinen ikke selv fors&oslash;ger at f&aring; data fra en
servlet (data skal jo komme et sted fra i sidste ende). 
</P>
<P CLASS="western">Derfor giver den Dataforbindelse et vink (med
Dataforbindelse.s&aelig;tForbindelsesvink()) om hvilken
forbindelsestype den &oslash;nsker.</P>
<PRE CLASS="kode-western">import javax.servlet.*;
<SPAN LANG="da-DK">import javax.servlet.http.*;</SPAN>
<SPAN LANG="da-DK">import java.io.*;</SPAN>
<SPAN LANG="da-DK">import java.util.*;</SPAN>

<SPAN LANG="da-DK">public class DataforbindelseServlet extends HttpServlet {</SPAN>

<SPAN LANG="da-DK">  public void init(ServletConfig config) throws ServletException {</SPAN>
<SPAN LANG="da-DK">    super.init(config);</SPAN>

<SPAN LANG="da-DK">    // Det er vigtigt at dataforbindelsen p&aring; v&aelig;rten IKKE fors&oslash;ger</SPAN>
<SPAN LANG="da-DK">    // at f&aring; forbindelse med en servlet, s&aring; vi s&aelig;tter et vink om</SPAN>
<SPAN LANG="da-DK">    // hvilken forbindelse vi &oslash;nsker</SPAN>
<SPAN LANG="da-DK"><B>    try {</B></SPAN>
<SPAN LANG="da-DK"><B>      Dataforbindelse.s&aelig;tForbindelsesvink(&quot;fil&quot;);</B></SPAN>
<SPAN LANG="da-DK">    } catch (Exception e) { e.printStackTrace(); }</SPAN>
<SPAN LANG="da-DK">  }</SPAN>

<SPAN LANG="da-DK">  public void doGet(HttpServletRequest request, HttpServletResponse response)</SPAN>
<SPAN LANG="da-DK">    throws ServletException, IOException {</SPAN>
<SPAN LANG="da-DK">    try {</SPAN>
<SPAN LANG="da-DK">      String <B>kommando = request.getParameter(&quot;kommando&quot;)</B>;</SPAN>
<SPAN LANG="da-DK">      Dataforbindelse <B>dbforb = Dataforbindelse.hentForbindelse()</B>;</SPAN>

<SPAN LANG="da-DK">      System.out.println(&quot;kommando=&quot;+kommando);</SPAN>
<SPAN LANG="da-DK"><I>      // Test - for at sikre os at det rent faktisk kommer gennem servletten</I></SPAN>
<SPAN LANG="da-DK"><I>      // sender vi en ekstra &quot;kunde&quot; med</I></SPAN>
<SPAN LANG="da-DK"><I>      // dbf.inds&aelig;t( new Kunde( &quot;Servlet kommando = &quot;+kommando, 0) );</I></SPAN>

<SPAN LANG="da-DK"><B>      if (&quot;sletAlleData&quot;.equals(kommando)) dbforb.sletAlleData();</B></SPAN>
<SPAN LANG="da-DK"><B>      else if (&quot;hentAlle&quot;.equals(kommando))</B></SPAN>
<SPAN LANG="da-DK"><B>      {</B></SPAN>
<SPAN LANG="da-DK"><I>        // s&aelig;t indholdstypen til noget bin&aelig;rt (bare noget andet end text/html</I></SPAN>
<SPAN LANG="da-DK">        response.setContentType(&quot;application/x-serialiserede-kunder&quot;);</SPAN>

<SPAN LANG="da-DK">        // serialis&eacute;r svaret og send det til klienten</SPAN>
<SPAN LANG="da-DK"><B>        List alle = dbforb.hentAlle();</B></SPAN>
<SPAN LANG="da-DK"><B>        ObjectOutputStream p=new ObjectOutputStream(</B></SPAN>
<SPAN LANG="da-DK"><B>          response.getOutputStream());</B></SPAN>
<SPAN LANG="da-DK"><B>        p.writeObject(alle);</B></SPAN>
<SPAN LANG="da-DK"><B>        p.close();</B></SPAN>
<SPAN LANG="da-DK"><B>        return;</B></SPAN>
<SPAN LANG="da-DK"><B>      }</B></SPAN>
<SPAN LANG="da-DK"><B>      else if (&quot;inds&aelig;t&quot;.equals(kommando)) {</B></SPAN>
<SPAN LANG="da-DK"><B>        Kunde k = new Kunde (</B></SPAN>
<SPAN LANG="da-DK"><B>          request.getParameter(&quot;navn&quot;),</B></SPAN>
<SPAN LANG="da-DK"><B>          Double.parseDouble( request.getParameter(&quot;kredit&quot;) )</B></SPAN>
<SPAN LANG="da-DK"><B>        );</B></SPAN>
<SPAN LANG="da-DK"><B>        dbforb.inds&aelig;t(k);</B></SPAN>
<SPAN LANG="da-DK"><B>      }</B> else throw new IllegalArgumentException(&quot;ukendt kommando: &quot;+kommando);</SPAN>

<SPAN LANG="da-DK">    } catch (Exception e) {</SPAN>
<SPAN LANG="da-DK">      e.printStackTrace();</SPAN>
<SPAN LANG="da-DK">      response.setContentType(&quot;text/html&quot;);</SPAN>
<SPAN LANG="da-DK">      PrintWriter out = response.getWriter();</SPAN>
<SPAN LANG="da-DK">      System.out.println();</SPAN>
<SPAN LANG="da-DK">      out.println(&quot;&lt;html&gt;&quot;);</SPAN>
<SPAN LANG="da-DK">      out.println(&quot;&lt;head&gt;&lt;title&gt;DataforbindelseServlet - fejl&lt;/title&gt;&lt;/head&gt;&quot;);</SPAN>
<SPAN LANG="da-DK">      out.println(&quot;&lt;body&gt;&lt;h1&gt;Fejl: &quot;+e+&quot;&lt;/h1&gt;&quot;);</SPAN>
<SPAN LANG="da-DK">      out.println(&quot;&lt;p&gt;Der opstod en fejl i servletten:&lt;/p&gt;&quot;);</SPAN>
<SPAN LANG="da-DK">      e.printStackTrace(out);</SPAN>
<SPAN LANG="da-DK">      out.println(&quot;&lt;/body&gt;&lt;/html&gt;&quot;);</SPAN>
<SPAN LANG="da-DK">    }</SPAN>
<SPAN LANG="da-DK">  }</SPAN>
<SPAN LANG="da-DK">}</SPAN></PRE><P CLASS="western">
Teknikken med at serialisere objekter og sende dem over netv&aelig;rket
over HTTP anvendes ofte til at sende objekter til en applet.</P>

<H3 CLASS="western">16.8.3 <a name='afsn16.8.3'></a>Dataforbindelse, der cacher foresp&oslash;rgsler</H3>
<P CLASS="western">Efterh&aring;nden som programmet bliver mere og
mere udviklet, opdager vi m&aring;ske, at nogle dele af programmet,
der laver hyppige dataforesp&oslash;rgsler, k&oslash;rer for
langsomt.</P>
<P CLASS="western">De fleste af foresp&oslash;rgslerne er endda
overfl&oslash;dige, da data ikke har &aelig;ndret sig.</P>
<P CLASS="western">Nu kunne vi selvf&oslash;lgelig kode
DataforbindelseOverNetvaerketTilEnServlet om til ogs&aring; at cache
foresp&oslash;rgslerne, men det ville give en lavere koh&aelig;sion
(diskuteret i <a href='kapitel15.jsp#afsn15.7.2'>afsnit 15.7.2</a>, H&oslash;j koh&aelig;sion), da klassen
s&aring; ville have to ansvarsomr&aring;der (foresp&oslash;rgsler
over netv&aelig;rket og caching).</P>
<P CLASS="western">En mere elegant l&oslash;sning ville v&aelig;re at
lave en klasse, der kun tog sig af caching. Den kunne lade som om,
den var en Dataforbindelse, men i virkeligheden kalder den videre i
en anden Dataforbindelse, hvis den ikke har svaret (dette kaldes ogs&aring;
en Proxy - se <a href='kapitel17.jsp#afsn17.1'>afsnit 17.1</a>).</P>
<PRE CLASS="kode-western">import java.util.*;

<SPAN LANG="da-DK">public class DataforbindelseCache extends Dataforbindelse</SPAN>
<SPAN LANG="da-DK">{</SPAN>
<SPAN LANG="da-DK">  private Dataforbindelse df;</SPAN>
<SPAN LANG="da-DK">  private List cache;</SPAN>

<SPAN LANG="da-DK">  public DataforbindelseCache(Dataforbindelse forb) { </SPAN>
<SPAN LANG="da-DK">    df = forb;</SPAN>
<SPAN LANG="da-DK">  }</SPAN>

<SPAN LANG="da-DK">  public void sletAlleData() throws Exception</SPAN>
<SPAN LANG="da-DK">  {</SPAN>
<SPAN LANG="da-DK">    cache = null;            <I>// der er sket en &aelig;ndring - nulstil cache</I></SPAN>
<SPAN LANG="da-DK">    df.sletAlleData();</SPAN>
<SPAN LANG="da-DK">  }</SPAN>

<SPAN LANG="da-DK">  public void inds&aelig;t(Kunde k) throws Exception</SPAN>
<SPAN LANG="da-DK">  {</SPAN>
<SPAN LANG="da-DK">    cache = null;            <I>// der er sket en &aelig;ndring - nulstil cache</I></SPAN>
<SPAN LANG="da-DK">    df.inds&aelig;t(k);</SPAN>
<SPAN LANG="da-DK">  }</SPAN>

<SPAN LANG="da-DK">  public List hentAlle() throws Exception</SPAN>
<SPAN LANG="da-DK">  {</SPAN>
<SPAN LANG="da-DK">    if (cache != null)</SPAN>
<SPAN LANG="da-DK">    {</SPAN>
<SPAN LANG="da-DK">      <I>// Vi har listen i cachen - return&eacute;r den, uden at sp&oslash;rge videre</I></SPAN>
<SPAN LANG="da-DK">      return cache;</SPAN>
<SPAN LANG="da-DK">    } </SPAN>
<SPAN LANG="da-DK">    else </SPAN>
<SPAN LANG="da-DK">    {</SPAN>
<SPAN LANG="da-DK"><I>      // &Oslash;v - vi har ikke listen i cachen - vi er n&oslash;dt til at sp&oslash;rge videre</I></SPAN>
<SPAN LANG="da-DK">      cache = df.hentAlle(); <I>// husk listen til en anden gang</I></SPAN>
<SPAN LANG="da-DK">      return cache;</SPAN>
<SPAN LANG="da-DK">    }</SPAN>
<SPAN LANG="da-DK">  }</SPAN>
<SPAN LANG="da-DK">}</SPAN></PRE>
<H3 CLASS="western" STYLE="">16.8.4 <a name='afsn16.8.4'></a>Endelig
udgave af Dataforbindelse</H3>
<P CLASS="western">Her er Dataforbindelse.java, som den ser ud sidst
i programmeringsforl&oslash;bet:</P>
<PRE CLASS="kode-western">import java.util.*;

<SPAN LANG="da-DK">public abstract class Dataforbindelse</SPAN>
<SPAN LANG="da-DK">{</SPAN>
<SPAN LANG="da-DK"><I>  /**</I></SPAN>
<SPAN LANG="da-DK"><I>   * Sletter alle data</I></SPAN>
<SPAN LANG="da-DK"><I>   */</I></SPAN>
<SPAN LANG="da-DK">  abstract public void sletAlleData() throws Exception;</SPAN>

<SPAN LANG="da-DK"><I>  /**</I></SPAN>
<SPAN LANG="da-DK"><I>   * Inds&aelig;tter en kunde</I></SPAN>
<SPAN LANG="da-DK"><I>   * @param kunden</I></SPAN>
<SPAN LANG="da-DK"><I>   */</I></SPAN>
<SPAN LANG="da-DK">  abstract public void inds&aelig;t(Kunde k) throws Exception;</SPAN>

<SPAN LANG="da-DK"><I>  /**</I></SPAN>
<SPAN LANG="da-DK"><I>   * Henter alle kunderne</I></SPAN>
<SPAN LANG="da-DK"><I>   * @return en liste af Kunde-objekter</I></SPAN>
<SPAN LANG="da-DK"><I>   */</I></SPAN>
<SPAN LANG="da-DK">  abstract public List hentAlle() throws Exception;</SPAN>

<SPAN LANG="da-DK">  private static Dataforbindelse forb;</SPAN>
<SPAN LANG="da-DK"><I>  /**</I></SPAN>
<SPAN LANG="da-DK"><I>   * Fabrikeringsmetode til at skaffe en forbindelse</I></SPAN>
<SPAN LANG="da-DK"><I>   * @return forbindelsen</I></SPAN>
<SPAN LANG="da-DK"><I>   */</I></SPAN>
<SPAN LANG="da-DK">  synchronized public static Dataforbindelse hentForbindelse() throws Exception</SPAN>
<SPAN LANG="da-DK">  {</SPAN>
<SPAN LANG="da-DK"><I>    // implementeret som en Singleton, s&aring; der m&aring; kun eksistere &eacute;t objekt</I></SPAN>
<SPAN LANG="da-DK">    if (forb != null) return forb;</SPAN>

<SPAN LANG="da-DK"><I>    // ... herunder skal kode ind der skaffer en forbindelse</I></SPAN>
<SPAN LANG="da-DK"><I>    //forb = new DataforbindelseDummy();  // f&oslash;r</I></SPAN>
<SPAN LANG="da-DK"><I>    //forb = new DataforbindelseFil();    // senere</I></SPAN>
<SPAN LANG="da-DK">    forb = new DataforbindelseOracle();</SPAN>
<SPAN LANG="da-DK"><I>    // endnu senere !!!</I></SPAN>
<SPAN LANG="da-DK"><I>    // forb = new DataforbindelseOverNetvaerketTilEnServlet(</I></SPAN>
<SPAN LANG="da-DK"><I>    //   &quot;http://localhost:8080/servlet/DataforbindelseServlet&quot;);</I></SPAN>

<SPAN LANG="da-DK">    return forb;</SPAN>
<SPAN LANG="da-DK">  }</SPAN>

<SPAN LANG="da-DK"><I>  /**</I></SPAN>
<SPAN LANG="da-DK"><I>   * Lavet s&aring;dan at man kan angive hvilken slags forbindelse man foretr&aelig;kker.</I></SPAN>
<SPAN LANG="da-DK"><I>   * Dette er rart til afpr&oslash;vning og fejlfinding.</I></SPAN>
<SPAN LANG="da-DK"><I>   * Skal kaldes som &lt;b&gt;f&oslash;r&lt;/b&gt; f&oslash;rste kald til &lt;code&gt;hentForbindelse()&lt;/code&gt;.</I></SPAN>
<SPAN LANG="da-DK"><I>   * Eksempler p&aring; brug:&lt;br&gt;</I></SPAN>
<SPAN LANG="da-DK"><I>   * &lt;code&gt;Dataforbindelse.s&aelig;tForbindelsesvink(&quot;dummy&quot;);&lt;/code&gt;&lt;br&gt;</I></SPAN>
<SPAN LANG="da-DK"><I>   * &lt;code&gt;Dataforbindelse.s&aelig;tForbindelsesvink(&quot;fil&quot;);&lt;/code&gt;&lt;br&gt;</I></SPAN>
<SPAN LANG="da-DK"><I>   * &lt;code&gt;Dataforbindelse.s&aelig;tForbindelsesvink(</I></SPAN>
<SPAN LANG="da-DK"><I>   *   &quot;http://localhost:8080/servlet/DataforbindelseServlet&quot;);&lt;/code&gt;</I></SPAN>
<SPAN LANG="da-DK"><I>   *</I></SPAN>
<SPAN LANG="da-DK"><I>   * @param vink Vink til hvilken type forbindelse der foretr&aelig;kkes</I></SPAN>
<SPAN LANG="da-DK"><I>   * @see #hentForbindelse</I>()</SPAN>
<SPAN LANG="da-DK"><I>   */</I></SPAN>
<SPAN LANG="da-DK">  synchronized public static void s&aelig;tForbindelsesvink(String v) throws Exception</SPAN>
<SPAN LANG="da-DK">  {</SPAN>
<SPAN LANG="da-DK">    if (forb != null) return; <I>// ignorer vink n&aring;r forbindelse f&oslash;rst er oprettet.</I></SPAN>
<SPAN LANG="da-DK">    if (v.equals(&quot;dummy&quot;))         forb = new DataforbindelseDummy();</SPAN>
<SPAN LANG="da-DK">    else if (v.startsWith(&quot;fil&quot;))  forb = new DataforbindelseFil();</SPAN>
<SPAN LANG="da-DK">    else if (v.startsWith(&quot;http&quot;)) forb = new DataforbindelseCache(</SPAN>
<SPAN LANG="da-DK">              new DataforbindelseOverNetvaerketTilEnServlet(v));    <I>// brug cache</I></SPAN>
<SPAN LANG="da-DK">    else forb = new DataforbindelseOracle();</SPAN>
<SPAN LANG="da-DK">  }</SPAN>
<SPAN LANG="da-DK">}</SPAN></PRE><P CLASS="western">
Bem&aelig;rk, at vi <I>ikke</I> smider de tidlige udgaver, f.eks.
DataforbindelseDummy, ud. De kan m&aring;ske v&aelig;re nyttige
senere, f.eks. er det ikke ut&aelig;nkeligt, at man senere skal
videreudvikle en anden del af programmet, men af den ene eller anden
grund ikke kan/vil koble op til database.</P>
<DIV ID="sdfootnote1">
  <P CLASS="sdfootnote-western"><A CLASS="sdfootnotesym" NAME="sdfootnote1sym" HREF="#sdfootnote1anc">1</A>En
  mulighed er dog at bruge serialisering - serialisere objektet til en
  datastr&oslash;m (en fil eller et array af byte) og deserialisere
  det igen. Dette er dog ikke nogen s&aelig;rlig effektiv m&aring;de
  at lave kopier af objekter, s&aring; den anbefales ikke, hvis
  programmet skal k&oslash;re hurtigt, eller der skal kopieres mange
  objekter.</P>
</DIV>

<a href='http://javabog.dk/'>javabog.dk</a>&nbsp;&nbsp;|&nbsp;&nbsp;<a href='kapitel15.jsp'>&lt;&lt; forrige</a>&nbsp;&nbsp;|&nbsp;&nbsp;<a href='indhold.jsp'>indhold</a>&nbsp;&nbsp;|&nbsp;&nbsp;<a href='kapitel17.jsp'>n&aelig;ste &gt;&gt;</a>&nbsp;&nbsp;|&nbsp;&nbsp;<a href='kode/'>programeksempler</a>&nbsp;&nbsp;|&nbsp;&nbsp;<a href='../index_VP.html'>om bogen</a>
<hr>
<font size=-2>http://javabog.dk/ - <b></b> af Jacob Nordfalk.
<br>
  Licens og kopiering under <a href='http://www.linuxbog.dk/licens.html'>&Aring;ben Dokumentlicens</a> (&Aring;DL)
  hvor intet andet er nævnt (71% af værket).
</font>
<br>
Ønsker du at se de sidste 29% af dette værk (362838 tegn)
skal du købe bogen. Så får du pæne figurer og layout, stikordsregister og en trykt bog med i købet.
<!-- netlæser: Wget/1.10, autoHent: true  -->
     

</body>
</html>
