tifyty

pure Java, what else ?

Havi archívumok: március 2013

Hogyan fűzzünk össze stringeket Java-ban? s.join(“,”) ?

Tudom, hogy hasonló téma már volt, de ez most egy kicsit más. Most nem csak egymás után akarjuk rakni a sok stringet, és nem az érdekel, hogy sima + vagy StringBuffer hanem az, hogy hogyan készítsünk comma delimited stringet.

Ennek igen sok módja van, és elég gyakran elő is fordul a feladat. A legtöbbször alkalmazott megoldás valami ilyesmi:

	static void join1(String... args) {
		StringBuilder sb = new StringBuilder();
		boolean first = true;
		String separator = " * ";
		for (String s : args) {
			if (first) {
				first = false;
			} else {
				sb.append(separator);
			}
			sb.append(s);
		}
		System.out.println(sb);
	}

Amikor ilyet látok, tudom, hogy fel kell készülnöm a kódban több helyen copy/paste-re, és egyéb nyalánkságokra, mert aki ezt írta nem csak Java-ban, hanem programozásban is kezdő. A következő változat ennek a módosított változata, amelyik kicsit rövidebb, és attól függően, hogy ki olvassa, és milyen az észjárása vagy olvashatóbb, vagy éppen kevésbé olvasható:

	static void join2(String... args) {
		StringBuilder sb = new StringBuilder();
		boolean first = true;
		String separator = " * ";
		for (String s : args) {
			if (!first) {
				sb.append(separator);
			}
			first = false;
			sb.append(s);
		}
		System.out.println(sb);
	}

Van egy olyan pattern is, amelyik összevonja a separator és a first funkcióját, és nem azt mondja, hogy nem teszem be az elem elé a szeparátort, ha ez az első elem, hanem kihasználja, hogy létezik olyan, hogy üres string, és a szeparátor első értéke ez, majd a ciklusban ez megváltozik, és onnan kezdve már elhelyezi a szeparátort:

	static void join3(String... args) {
		StringBuilder sb = new StringBuilder();
		String separator = "";
		for (String s : args) {
			sb.append(separator);
			separator = " * ";
			sb.append(s);
		}
		System.out.println(sb);
	}

És amikor ezt olvasom, akkor tudom, hogy a kódot egy tapasztalt kezdő írta.

Milyen a kód, amit szenior ír?

Mi a különbség a junior és a szenior között? Az, hogy a junior boldogan ír kódot. A szenior viszont már annyi kódot írt, hogy inkább kétszer annyi időt eltölt meglevő megoldások keresésével, mint amibe a kód megírása kerülne. Ezzel ugyan akár még lassabb is lehet, mint egy junior. Persze nem biztos, hisz nem kétszer annyi időt tölt el a kereséssel, mint amennyit a kód megírásával a junior töltene el, csak éppen kétszer annyit, mint amennyi alatt ő maga megírná a kódot. De miért jó ez mégis mégis? Mert a kód karbantartása során, amikor jön valaki két év múlva, akinek karban kell tartania a kódot, ez az idő megtérül, kamatostul. Tehát a szenior ilyen kódot ír:

import org.apache.commons.lang.StringUtils;

  . . .

	static void join4(String... args) {
		String sb = StringUtils.join(args, " * ");
		System.out.println(sb);
	}

vagy éppen ilyent:

import com.google.common.base.Joiner;

  . . .

	static void join5(String... args) {
		String sb = Joiner.on(" * ").join(args);
		System.out.println(sb);
	}

Tudásteszt, multi thread 2

A wouter.coekaerts.be web oldalról loptam a következő feladatot, de annyira megizzasztott, és annyira jó, hogy úgy gondoltam kiposztolom ide. Ha csak annyi a hozzáadott érték, hogy magyarul írom meg a magyarázatot, és eközben még egyszer átgondolom, akkor is megéri.

Bevallom őszintén: én magam nem oldottam meg a feladatot, türelmetlen voltam, mert mostani elfoglaltságaim mellett (dolgoznom is kell, sajnos mert a blogért nem fizettek) legalább hétvégéig kellett volna várnom, hogy legyen annyi időm, hogy elgondolkodhassak a megoldáson. De a feladat íme:

Van egy alvó osztályunk:

package sleep;
 
import dream.Dream;
 
public class Sleeper {
    private int level;
 
    public synchronized int enter(Dream dream) {
        level++;
        try {
            dream.dream(this);
        } finally {
            level--;
        }
        return level;
    }
}

És van egy másik, főosztályunk

package sleep;
 
import dream.Dream;
 
public class Main {
    public static void main(String[] args) {
        if (new Sleeper().enter(new Dream()) != 0) {
            // The goal is to reach this line
            System.out.println("Am I still dreaming?");
        }
    }
}

Ezen kívül pedig van egy megírandó álom osztályunk:

// this is the only file you're allowed to edit
package dream;
 
import sleep.Sleeper;
 
public class Dream {
    public void dream(Sleeper s) {
        // TODO implement me
    }
}

A feladat ezt az utolsó álomosztályt úgy megírni, hogy a főosztály kiírja a csodálkozó kérdést: Am I still dreaming?

Ha nagyon nem megy, akkor adok egy tippet, és ha az sem elég, akkor itt a megoldás.

Tudásteszt multi thread, 2 tipp

Ez nem egy önálló poszt, hanem a bejegyzés feladatához egy segítség. Ha azt a bejegyzést még nem olvastad el, akkor inkább kezdd ott.

A

package sleep;
 
import dream.Dream;
 
public class Sleeper {
    private int level;
 
    public synchronized int enter(Dream dream) {
        level++;
        try {
            dream.dream(this);
        } finally {
            level--;
        }
        return level;
    }
}

kódban a synchronized azt garantálja, hogy az enter metódust ebben az objektumban egyszerre csak egy szál futtathatja. Gondoljunk csak Arankára az út szélén!

Ha egyszerre akarja Józsi, meg Aladár is futtatni Arankát, abból késelés lesz. De előfordulhat, hogy először Józsi futtatja Arankát, majd amíg Józsi a hűvösön vár egy kicsit, addig Aladár futtatja Arankát, utána pedig, amikor Józsi kijön a hűvösről, és már nem vár (érted? Nem vár!), akkor megint Józsi futtatja Arankát, mindeközben Aranka igencsak aktív és még nem tért meg (vissza) teremtőjéhez a nyilvános, állandó és értékőrző (void, mint értéket vissza nem adó) fő metódushoz.

Portból lettünk, és porrá leszünk, mint a leves. 
Úgyis, mint anyukád portját, amiből lettél, 
mag apukád kompatibilis interfészét amivel implementált.

Nem sértegetésnek, csak úgy bónusz, hogy ne unatkozz.

Tudásteszt multi thread, 2 megoldás

Ez nem egy önálló poszt, hanem a bejegyzés feladatának megoldása. Ha azt a bejegyzést még nem olvastad el, akkor inkább kezdd ott.

Az volt a feladat, hogy írjunk egy olyan Dream osztályt, amelyik a

package sleep;
 
import dream.Dream;
 
public class Sleeper {
    private int level;
 
    public synchronized int enter(Dream dream) {
        level++;
        try {
            dream.dream(this);
        } finally {
            level--;
        }
        return level;
    }
}

valamint

package sleep;
 
import dream.Dream;
 
public class Main {
    public static void main(String[] args) {
        if (new Sleeper().enter(new Dream()) != 0) {
            // The goal is to reach this line
            System.out.println("Am I still dreaming?");
        }
    }
}

osztályok esetén úgy viselkedik, hogy a fő osztályban a main metódus kiírja az Am I still dreaming? kérdést. Ehhez adtam azt a hintet, hogy egy synchronized metódust egy időben csak egy szál futtathat, de ahhoz, hogy egy szál elkezdhesse futtatni a metódust NEM KELL, hogy a futó szál befejezze a metódust, elég ha félbehagyja a futtatást valamilyen módon: végrehajt egy wait() hívást. Egy kicsit virágnyelven fogalmaztam a tipp oldalon, de ez volt a lényeg. Innen kezdve már elég triviális a megoldás (vagy nem).

A hivatalos megoldás a postban hivatkozott oldalon:

package dream;
 
import sleep.Sleeper;
 
public class Dream {
    private static void doWait(Sleeper s) {
        try {
            s.wait(200);
        } catch (InterruptedException e) {
        }
    }
 
    public void dream(final Sleeper s) {
        new Thread() {
            public void run() {
                s.enter(new Dream() {
                    @Override
                    public void dream(Sleeper s) {
                        doWait(s);
                    }
                });
            }
        }.start();
 
        doWait(s);
    }
}

A lényeget ez el is mondja, de mivel nem garantált, hogy ez a verzió helyesen lefut (de azért többnyire kiírja a csodálkozó kérdést, ha viszont mindenféle System.out.println-okat tesz bele az ember, akkor a 200ms elég kevés lehet és időzítési problémái akadhatnak), ezért egy cseppet módosított változatot tárgyalok. Akit érdekelnek további megoldások, azok olvassák el az eredeti blog kommentjeit, van belőlük több, mint száz. Szóval a módosított megoldás szerint:

package dream;

import sleep.Sleeper;

public class Dream {
	private volatile boolean otherThreadNotStarted = true;
	private Thread mainThread = Thread.currentThread();

	private static void doWait(Sleeper s) {
		try {
			s.wait(1);
		} catch (InterruptedException e) {
		}
	}

	public void dream(final Sleeper s) {
		Thread t = new Thread() {
			public void run() {
				s.enter(new Dream() {
					@Override
					public void dream(Sleeper s) {
						otherThreadNotStarted = false;
						while (mainThread.isAlive()) {
							doWait(s);
						}
					}
				});
			}
		};
		t.start();
		while (otherThreadNotStarted) {
			doWait(s);
		}
	}
}

A főprogram meghívja a dream metódust a 16. sorban, ami létrehoz egy új szálat és el is indítja. Persze semmi nem garantálja, hogy ez a szál el is indul rögtön. Lehet, hogy csak egy hét múlva indul el. A Java nem real-time rendszer. Ezért, mielőtt visszatérne a fő szál az álomból elkezd várni. Csak vár, és vár, és amíg vár elengedi a monitort, ami annak megakadályozására szolgál, hogy más szál ne futtathassa az enter metódust. Persze nem vár sokáig, de ha a másik szál még nem indult el, vagy ugyan elindult, de még nem jelezte a futását az otherThreadNotStarted változóban, akkor újra vár.

A java futtató rendszer látva, hogy ez a mi fő szálunk ilyen makacs, előbb utóbb persze elindítja a másik szálat is, és ha nem olyan csökönyös a JVM, no meg az operációs rendszer alatta, akkor erre nem kell a valóságban egy hetet várnunk. Elindul a másik szál, és belép az enter metódusba, mégpedig ugyanazon az ojjektumon, amelyikben a fő szál is éppen benne van. (Na, erre nem is gondoltam, amikor Arankát emlegettem. Ez egy kicsit erős, de most már mindegy.)

Nem biztos, hogy sikerül neki! Miért? Azért, mert közben a fő szál fel fel ébredget, és ha éppen éber, akkor nem engedi, hogy a másik szál is bejusson. (Ez egyre rosszabb.) De mivel a fő szál mással sincs elfoglalva, mint hogy újra és újra elbóbiskoljon (öregember üzemmód), és ezzel újra és újra elengedje a monitort, ezért egyszer csak a másik szál bejut az enter metódusba, és az meghíjja az annak átadott Dream-mel kompatibilis, abból leszármazott anoním osztály (amely, mint az köztudott hozzáfér a lokális (és így az argumentum) változókhoz (is), amennyiben azok final változók) dream metódusát (jaaaaaajjjjj!!!!). Ha meg már benne vagyunk, és álmodunk, most már mind a két szálon, akkor akár engedhetjük is, hogy a fő szál felébredve ne tentikéljen, hanem húzzon vissza a fő metódusba, írja ki amit ki kell írni magából és minekutána más dolga nincs ezen a JVM-en: termináljon. Másra sem vár a második szál, mivelhogy éppen, hogy erre vár, és amikor ez megtörtént, akkor, minthogy dolga neki is bevégeztetett ő is vissza-/megtér.

Hát ennyi.

Birka inner class a pénisz?

Nyár vége volt, már nem volt forróság, de még elég meleg, hogy el lehessen egy könnyű ebéd után szenderedni az árnyékban. Bodri amúgy is vigyáz a nyájra, aztán meg, ha valami baj van, megszűnik a kolompolás, vagy Bodri kezd el ugatni, és arra a gyakorlott juhász úgyis felébred. Így gondolta ezt Bazsi is, aki korábban szoftver fejlesztőként dolgozott, de egy ideje már Nepálban őrzi inkább a birkákat. Bodrinak eleinte furcsák voltak a nagy hegyek és gyakran álmában ugatott, de már megszokta. De hiába gondolta ezt így Bazsi, mert nem ült el a kolomp zaj, se Bodri nem ugatott, Bazsinak mégis fel kellett ébrednie. A birkák megvoltak mind, álmosítóan szólt a kolomp, idegen se közeledett ezért Bodri sem ugatott. Mégis fel kellett ébredni, mert ha valakinek a vállát rázzák tiszta erőből, akkor nem lehet tovább aludni. Az felér egy IntterruptedException-nel.

Bazsi vállát Ali rángatta, akinek volt rendes magyar neve, de mindenki csak al1 nickneve alapján ismerte, amiből aztán egyszerűen Ali lett. Hogy a nick név honnan jött, már senki sem tudta, lehet, hogy csak a macska sétált végig a billentyűzeten. A macska sincs meg már, begyűjtötte rég a GC, no meg billentyűzet se nagyon kell a birkák őrzéséhez. Bodri meg ismerte jól Alit, hát miért ugatta volna. Tudta, hogy Ali, aki korábban vezető architekt volt, most a pásztorokat vezeti, és szerette nagyon Ali-t, mert jó szaga volt. Szalonnás.

  • Bazsi, te szerncsétlen! Hogy a stack-kel együtt öntöttek volna ki a végtelen rekurziódban! Így vigyázol te a nyájra?

Bazsi egy pillanatig csak hunyorgott az éles fényben, de hamar rájött, hogy mit kell válaszolni:

  • Hát, Ali, tudod, puszta lelkiismeretességből! Nem akarom, hogy egy birka is elvesszen, és ezért rendszeresen ebéd után nekiállok megszámolni őket! Érted! Birkákat számolok.
  • Na, meg még hülyének is nézel! Nem azért jöttem fel hozzád, hogy a szar vicceiddel traktálj. Arra ott van lent a tanyán Ernő. Jönnek a nyírók, le kell vinni a nyájat.
  • Ezért jöttél fel, hogy ezt közöld? Miért nem küldtél egy eMail-t?

Egy pillanatra csend lett, ahogy Bazsi is rájött, hogy mekkora marhaságot mondott.

  • Te teljesen hülye vagy?

akadt ki Ali. A hegyen nem volt se réz-, se üvegkábel, de még lefedettség sem. Még áram sem volt. Bazsi is csak a Nintendo konzolját hozta fel tavaszonként, de az is csak addig működött, míg el nem fogyott belőle a delej. Feltápászkodott. Idén hamarabb jöttek a nyírók, hamarabb lesz vége a nyárnak. Az őszt és a telet lent töltik a tanyán. Ott van betárcsázós internet.

  • Kávét hoztál? Tavasz óta teljesen kifogytam belőle.

Leültek kávézni. A kávé mindig szertartás volt, és ilyenkor mindig programozási technológiákról beszélgettek. Ali mindig, mindenre tudott valami jót mondani. Hát vagy legalábbis, mindig mindenre tudott valamit mondani. Hogy jót-e, arról megoszlottak a vélemények, de Ali nem volt Troppaüer Hümér, hogy mindenképpen meg akarjon mindenkit győzni, meg nem is volt egy virág.

  • Ma miről beszéljünk? — kérdezte, amikor elkezdték szürcsölni a kávét.
  • Az jutott eszembe, hogy még a cégnél egyszer kávézás közben egy szenior kolléga azt mondta, hogy a belső osztályoknak nincs Java-ban semmi értelmük, nem nagyon lehet semmire sem értelmesen használni őket, és amit használatot látott, az mind csak lustaság volt, és re kellett volna a kódot faktorálni, hogy külső, normális osztályok legyenek. Neked erről mi a véleményed?
  • A dolog addig stimmel, hogy a belső osztályok legelterjedtebb használatát valóban a lustaság ösztönzi. A legtöbb esetben a belső osztály csak azért belső osztály, mert a programozó lusta valamiért még egy fájlt készíteni, sajnálja azt a 10 másodpercet, és inkább egy belső osztályt hoz létre. De valójában ez még nem jelenti azt, hogy nincs is értelme belső osztályt használni. Mielőtt azonban belekezdenék, meg tudod-e mondani, hogy mi különbség van a beágyazott osztály (nested class) és a belső osztály (inner class) között?
  • Persze, hogy meg tudom mondani. A beágyazott osztály elé oda kell írni, hogy static, nem éri el a külső osztály példányváltozóit és metódusait, mert nincs a külső osztály egy példányához kötve a beágyazott osztály egy példánya sem. A belső osztály példányban viszont van egy referencia, amelyik a külső osztály egy példányára mutat, és ezen keresztül eléri a külső osztály példányváltozóit és metódusait.
  • És persze, tedd hozzá azt is, a blog olvasók kedvéért, hátha esetleg valaki nem tudná — fűzte még hozzá Ali — hogy ez a referencia a lefordított kód belső ügye. Java szinten a programozónak ezzel nem kell foglalkoznia, a változók, és a metódusok úgy érhetőek el, mintha a saját, belső osztálybéli metódusok, változók (mezők) lennének.
  • Blog olvasók? — csodálkozott Bazsi és körülnézett. — A birkákra gondolsz? — kérdezte csodálkozva.
  • Igen, azokra. — legyintett Ali — Inkább hagyjuk. Térjünk vissza a belső osztályokra. Vegyünk egy példát. Valamit a való életből, amit le lehet programozni.
  • Mondjuk egy birkát?
  • Hát…, konkrétan nem egy birkára gondoltam, de akkor legyen egy birka. A birka egy osztály: Sheep. De van belőle kicsi, Lamb, felnőtt kos Ram, nőstény Wether meg anyajuh is van Dam. Ezek mind a Sheep osztályból származnak le.
  • Ez eddig leszármazás. Ez nem olyan nagy kaland, ez még a birkáknak is megy.
  • Persze, ha a szexre és a szaporodásra gondolsz, az bizony megy a birkáknak. De amikor van egy kiváló tenyészkosod, és jön a részeg Paddy a napi két centjéért, hogy lenyírja, akkor hogyan oldod meg, hogy le ne vágja a remegő kezével a birka tökét?
  • A közelébe nem engedem!!!
  • Pontosan. A birka tökéhez nem engedheted oda Paddy-t! Ezt pedig úgy tudod leginkább megtenni, hogy minden olyan metódust, amelyik a birka nyírásához kell, egy belső osztályban valósítasz meg, és csak a belső osztályra mutató referenciát adod oda Paddy-nek. Ezzel ő nem fér hozzá a birkához, csak nyírni fog tudni. Valahogy így:
package com.javax0.sheepfarm;

import com.javax0.sheepfarm.exceptions.CanNotClipABaldSheepException;
import com.javax0.sheepfarm.exceptions.DoNotMixDifferentlyColoredWoolException;
import com.javax0.sheepfarm.exceptions.PackIsFullException;

public class Sheep implements DomesticatedAnimal {
	private double weight;
	private Herd<Sheep> herd;
	private Wool wool = new Wool();
	private Mouth mouth = new Mouth();

	public Mouth getMouth() {
		return mouth;
	}

	public Herd<Sheep> getHerd() {
		return herd;
	}

	public void setHerd(Herd<Sheep> herd) {
		this.herd = herd;
	}

	protected void gainWeight(double amount) {
		weight += amount;
	}

	protected void looseWeight(double amount) {
		weight -= amount;
	}

	protected void cloneTo(Sheep other) {
		other.weight = this.weight;
		other.wool.color = this.wool.color;
		other.wool.length = this.wool.length;
	}

	public class Mouth {
		void feed(double amount) {
			gainWeight(amount);
		}
	}

	public class Wool {
		private static final double WEIGHT_SCALING = 0.022;
		private static final double MINIMAL_WOOL_LENGTH = 0.75;
		private int color;
		private double length;

		public WoolPack clip(WoolPack woolPack)
				throws CanNotClipABaldSheepException, PackIsFullException,
				DoNotMixDifferentlyColoredWoolException {
			if (length < MINIMAL_WOOL_LENGTH) {
				throw new CanNotClipABaldSheepException();
			}
			woolPack.setColor(color);
			double amountToCut = length * weight * WEIGHT_SCALING;
			if (amountToCut > woolPack.getPackSize()) {
				amountToCut = woolPack.getPackSize();
			}
			woolPack.addWool(amountToCut);
			length -= amountToCut / weight / WEIGHT_SCALING;
			return woolPack;
		}
	}
}
  • Ez jó. Persze van itt még egy pár osztály, azt látom, hiszen van rögtön nyájunk is, meg etetjük is a birkát, de ehhez értek, ezt nem kell magyarázni. De mit kezdjek annyi kossal? Azért a legtöbb fiatal hím állatot csak kimiskároltatjuk. Azt hogyan kódolod le ebben az osztályban?
  • Ebben az osztályban sehogy. Nem birkát miskárolunk, csak kost. Annak az implementációja már a Ram osztályban van.
package com.javax0.sheepfarm;

import com.javax0.sheepfarm.exceptions.AbstractFarmException;

public class Ram extends Sheep {
	private final Penis penis = new Penis();

	public MaleGenitalia getPenis() {
		return penis;
	}

	public class Testicle {
		public void neutrize() throws AbstractFarmException {
			Wether wether = new Wether();
			synchronized (Ram.this) {
				cloneTo(wether);
				getHerd().replace(Ram.this).to(wether);
			}
		}
	}

	public class Penis implements MaleGenitalia {
		public void pee(double amount) {
			looseWeight(amount);
		}

		public void copulate(AbstractFemaleSheep ewe)
				throws AbstractFarmException {
			// see
			// http://socyberty.com/sexuality/volume-of-semen-ejaculated-at-once-by-various-species-of-animals/
			looseWeight(1e-3);
			Dam dam = new Dam();
			synchronized (ewe) {
				ewe.cloneTo(dam);
				dam.growUdders();
				ewe.getHerd().replace(ewe).to(dam);
			}
		}
	}
}
  • Értem. Itt is csak a Testicle példányhoz férhet hozzá aki miskárol. Nem kellene, hogy abból is legyen egy példány, mint a Penis-ből?
  • De kellene, majd kódreview-n beleírod. És látom rögtön le van programozva a szaporodás is, a birka pénisze is egy belső osztály.
  • Pontosan. Amikor heréled a kost, ugye csak a tökeire koncentrálsz, és nem érdekel a pénisze? … Nem! Nem! Bazsi! Az ég áldjon meg, ember! Ez költői kérdés volt, nem akarom hallani a választ!
  • Nem is akartam válaszolni.
  • Akkor jó. Viszont a nőstény birkákat érdekli a dolog, és ezért implementálja a Penis belső osztály a funkcióit. Mit tud vele csinálni? Párzani, meg vizelni.
  • Azért azt a szinkronizált részt, azt nem egészen értem.
  • Hogy miért szinkronizált? Van ugye a nőstény. Arra szinkronizál, mert nem lenne szerencsés, ha a párosodás közben más is hozzáférne. Ezért amíg fut a copuláció metódus, más kos nem kezdhet bele ebbe a műveletbe. A művelet pedig, bár a valóságban elég összetett, de a programozási leírásban csupán két dolgot csinál: csökkenti a kos tömegét az ejakulátum tömegével, és átalakítja a nőstény birkát anyabirkává. Ez a cloneTo művelet. Nem egészen klónozás a valóságban, de Dolly óta (nem, nem Parton) azért nem áll távol ez a dolog a birkáktól. Végül a nyájban a nőstény példányt, ami elvileg megszűnik, kicseréli az anyabirkára, amelyik átveszi a helyét.
  • És ha közben végrehajt egy ewe.wait() hívást a kos?
  • Úgy érted, hogy ha bealszik közben? Akkor elég birka. De ilyen nincs a kódban. Ebben az esetben természetesen elveszíti a kizárólagosságát, és ha bármelyik másik kos meg akarja kezdeni a copulate műveletet, akkor el fogja tudni kezdeni, és ha az első kos közben fel akarna ébredni, hát nem fog.
  • És a Herd osztálynak van replace metódusa? De mi az a to utána?
package com.javax0.sheepfarm;

import java.util.HashSet;
import java.util.Set;

import com.javax0.sheepfarm.exceptions.AbstractFarmException;
import com.javax0.sheepfarm.exceptions.CantReplaceSheepException;

public class Herd<AnimalType extends DomesticatedAnimal> {
	private final Set<AnimalType> individuals = new HashSet<AnimalType>();

	public synchronized void add(AnimalType animal) {
		individuals.add(animal);
	}

	public synchronized void remove(AnimalType animal) {
		individuals.remove(animal);
	}

	public class Replacer {
		private AnimalType animalToReplace;

		Replacer(AnimalType animal) {
			animalToReplace = animal;
		}

		public void to(AnimalType animal) throws AbstractFarmException {
			synchronized (Herd.this) {
				if (!individuals.contains(animalToReplace)
						|| individuals.contains(animal)) {
					throw new CantReplaceSheepException();
				}
				remove(animalToReplace);
				add(animal);
			}
		}
	}

	public Replacer replace(AnimalType animal) {
		return new Replacer(animal);
	}
}
  • A Herd osztálynak van egy metódusa, amelyikkel egy új állatot hozzá lehet adni a nyájhoz, és egy olyan, amelyikkel el lehet venni egy állatot a nyájból. Ezen kívül van egy replace metódus is, amelyik létrehoz egy Replacer belső osztályt, és ennek van to() metódusa. Ezzel a módszerrel lehet szép fluent api-t készíteni.
  • Tetszik. — révedezett el Bazsi — Így, ilyen távolról nem is olyan rémes ez a mi szakmánk. Csak ügyfelek ne lennének…

Akit részletesen érdekel az egész állatság, az a https://github.com/verhas/sheepfarm oldalon megtalálja.

Tudásteszt, singleton

Szeretném előrebocsátani, hogy a következő párbeszéd nem egy konkrét interjú részlete!

  • Mi az a szingleton?
  • Az egy olyan osztály, amelyikből csak egy példány létezik, és ezt úgy érjük el, hogy olyan patternt használunk, amelyikkel csak egy példányt lehet létrehozni.
  • Egyetlen egyet? Nem lehet szingletonból több?
  • Nem lehet. Csak egy.
  • Akkor ez azt jelenti, hogy ha elindítom az otthoni gépemen az alkalmazást, amiben van a szingleton, akkor a munkahelyemen a fejlesztő gépen már nem tud elindulni, mert még egy példány nem tud létrejönni a szingletonból?
  • Természetesen nem ezt jelenti. Egy gépen lehet belőle csak egy példány.
  • Vagyis ha a fejlesztő gépemen elindítottam egy alkalmazást, amiben van egy szingleton osztály, és abból már létrejött egy példány, akkor azt az alkalmazást még egy példányban, egy másik JVM-ben nem indíthatom el, nem fog működni?
  • De. Igazából egy JVM-en belül lehet csak egy példány belőle.
  • És mi a helyzet mondjuk egy webes alkalmazással. Feltölthetek két példányban is egy szervletet? Mondjuk van benne egy szingleton. A szervlet alklamazások egy JVM-en belül futnak. A másodiknak elinduló már nem tud példányt létrehozni a szingletonból?
  • Nem tudom. Azt hiszem nem.
  • Hallott-e arról a fogalomról, hogy XXXXXX

Tisztelt kommentezők! Milyen fogalom kerül vajh az XXXXXX helyére?