tifyty

pure Java, what else ?

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.

9 responses to “Birka inner class a pénisz?

  1. Zoltán Baranyi (@blazember) március 13, 2013 11:55 de.

    Veszélyesen közelítünk Sven Bomwollenhez 🙂

    És mit szólna Bazsi, ha a copulate() egy Future-et adna vissza?

  2. tamasrev március 13, 2013 12:02 du.

    Ez a legállatabb összefoglaló, amit az Adapter patternről valaha olvastam!

  3. Bence március 13, 2013 3:08 du.

    “Ezt pedig úgy tudod leginkább megtenni, hogy minden olyan metódust, amelyik a birka nyírásához kell, egy belso osztályban valósítasz meg, és csak a belso osztályra mutató referenciát adod oda Paddy-nek. Ezzel o nem fér hozzá a birkához, csak nyírni fog tudni.”

    public interface Clippable?

    No meg, csak cloneTo() és getMouth() van, getWool() nincs, így lenyírni sem lehet szerencsétlen állatot – WoolOverflowException -t fog dobni 🙂

    A fluent API része a dolognak jó; de azokat nem az én ízlésem szerint találták ki, ráadásul ha összetettebb műveletsorról van szó, akkor keresztbe-kasul/többszintű belső osztálystruktúra lesz a végeredmény 😦

    Tulajdonképpen, most, hogy jobban belegondolok, tényleg nem látom értelmét a belső osztálynak: minek is enkapszulálom azt, ami már enkapszulálva van?

    Megszavaznék egy “reloaded” epizódot, mert nem csak hogy a fentiek okán misszelem a pojntot, de ráadásul az sem derült ki, hogy a birkákat végül megnyírták-e, avagy timeout lett a történet vége…

    • v március 13, 2013 3:21 du.

      Az egyik kérdésedre a következőt tudom mondani:

      public class RiskyHairCut {
      
      	interface Clippable {
      		void clip();
      	}
      
      	static class You implements Clippable {
      		public void clip() {
      			System.out.println("clip");
      		}
      
      		public void castrate() {
      			System.out.println("castrate");
      		}
      	}
      
      	private static void evilHairDresser(Clippable head) {
      		You you = (You) head;
      		you.castrate();
      	}
      
      	public static void main(String[] args) {
      		You you = new You();
      		Clippable yourHead = (Clippable) you;
      		evilHairDresser(yourHead);
      	}
      }
      

      Te bele mernél ülni abba a fordrász székbe?

      • Bence március 13, 2013 6:15 du.

        Sejtettem, hogy ez lesz az érv. 🙂
        Azonban ne felejtsük el, hogy, ha evilHairDresser() képes átmenni a falon (gy.k. Reflection), akkor fújhatom, hogy kint maradok az utcán, és csak a fejemet dugom be a fodrászüzletbe.
        Akkor meg ugye ne is járjunk egyáltalán fodrászhoz, meg sehova sem, mert akinek herélhetnékje van, az herélni is fog.
        …avagy mindjárt elmondod, miért nem.

        • v március 13, 2013 6:53 du.

          Nem csak az a kérdés, hogy az a fodrász mennyire gonosz, hanem az is, hogy mennyire okos, és milyen messzire hajlandó elmenni, mi az amit hajlandó megtenni azért, hogy a gonoszságot végrehajthassa.

          Konkrét esetben egy instaceof ellenőrzés és egy cast-olás belefér abba a kategóriába, amit egy junior fejlesztő úgy elkövet, hogy nem is jön rá, mit tett. Szüksége volt a funkcionalitása, tudja, hogy az objektum olyan típusú, átalakítja.

          Ha egy inner class-t kap, akkor ezt már nem tudja megtenni. Akkor nem csak, hogy reflection-höz kell nyúlnia, de adott esetben még ki is kell listáznia, hogy milyen mezők vannak abban az inner class-ban, és meg kell tippelnie, hogy melyik lehet az a mező, amelyik a körülvevő osztály példányra mutat. Persze általában ennek a mezőnek a neve this$0, de ez már a fordító magányügye, és ha te magad is használsz egy ilyen nevű mezőt (ne tedd!!!), akkor az Eclipse JDT már más nevet használ, az ORACLE javac egyes verziói meg azt mondják, hogy error.

          Erre nem lehet azt mondani, hogy bocsássunk meg nekik, mert nem tudják, hogy mit cselekszenek. Aki production kódban ilyet tesz az se nem junior, se nem programozó.

          • Bence március 14, 2013 1:54 de.

            No, ez tény. De ugye – többek közt – erre is jó a ha ri van vjúzva a kód. Ha az embernek juniorral van dolga, fel lehet rá hívni a figyelmét, hogy nem kellene. Sajnos, találkoztam már senior fejlesztővel, aki hasonló kaliberű “megoldásokat” alkalmazott; neki nem lehetett felhívni a figyelmét, hogy nem kéne…

            Én már attól is lábon kihordok egy agyvérzést, ha rákényszerülök, hogy castoljak (Swing Listener, ‘Droid getViewById, Spring 2.x getBean, stb).

            Apropó! Valami olyan poszt esetleg, amely során pásztoraink által megtolódik a kasz?

            Gondolom, Nepálban vannak róla legendák…

  4. Kofa március 14, 2013 11:42 de.

    Hú, ez fárasztó volt. 🙂
    Talán megemlíthetnél olyan, a gyakorlatban is használt belső típusokat, mint a Map.Entry (és megvalósításai), vagy a Guava Immutable* osztályok builderei, esetleg Iterable típusok Iteratorai (amelyek nem feltétlenül a publikus interface-en keresztül adják vissza az elemeket, pl. a LinkedList iterátor nem a get(index) metódust használja – vagy akár a LinkedList Entry osztálya, amelyben az aktuális elem értéke és az előző/következő referenciája van (ill. hasonló lehet egy fa Node-ja), és kívülről nem kell, nem is szabad elérni).

  5. Visszajelzés:Hídeljárások és volatile metódusok | tifyty

Vélemény, hozzászólás?

Adatok megadása vagy bejelentkezés valamelyik ikonnal:

WordPress.com Logo

Hozzászólhat a WordPress.com felhasználói fiók használatával. Kilépés / Módosítás )

Twitter kép

Hozzászólhat a Twitter felhasználói fiók használatával. Kilépés / Módosítás )

Facebook kép

Hozzászólhat a Facebook felhasználói fiók használatával. Kilépés / Módosítás )

Google+ kép

Hozzászólhat a Google+ felhasználói fiók használatával. Kilépés / Módosítás )

Kapcsolódás: %s

%d blogger ezt kedveli: