wtorek, maja 09, 2006

Lubię enumy

Że Java jest świetnym językiem wiedziałem już od jakichś 2 lat, że Java 5.0 jest udanym rozwinięcie wiedziałem od paru miesięcy, ale wczoraj to przekonanie jeszcze we mnie urosło.

Oficjalnie stwierdzam, że kocham nowe ficzery z Java'y 5.0 :-)

Generyki są wspaniałe, varargsy również, for each także, a wczoraj naprawdę poznałem i polubiłem enumy.

Jedno z przykazań poprawnego obiektowego tworzenia aplikacji jest to by jak najrzadziej używać wartości prymitywów, a już w ogóle unikać jak ognia ich porównywania.

Teraz "mała" dygresja.

Fanatycy wydajności kręcą zwykle nosami przy tym punkcie, krzycząc o tym, że prymitywy są szybsze. Zwykle zapominają tu o jednej z zasad programowania Unix'ów, która można swobodnie przenieść do zasad programowania w ogóle, która mówi:

"Rule 1. You can't tell where a program is going to spend its time. Bottlenecks occur in surprising places, so don't try to second guess and put in a speed hack until you've proven that's where the bottleneck is."

za http://www.faqs.org/docs/artu/ch01s06.html

Porównanie prymitywów jest może i szybsze, ale zwykle zysk w prędkości nie rekompensuje problemów, które mogą powstać. Dlatego w OOP zaleca się używanie gdzie tylko się da typów złożonych, między innymi dlatego, że porównywanie jest możliwe tylko między obiektami, które znajdują się w tej samej gałęzi dziedziczenia.
Używając prymitywów możemy mieć klasę Zwierze, która ma pola rodzajPozywienia i iloscNog:

public class Zwierze
{
// Definicje możliwych rodzajów pożywienia:
public final static int FRYTKI = 0;
public final static int ZIEMNIAKI = 1;
public final static int SERKI_TOPIONE = 2;
public int rodzajPozywienia;
public int iloscNog;

[...]
};

Widać, że rodzajów pożywienia mogą być 3 rodzaje, ilość nóg może być dowolna.
Nic nas jednak poza zdrowym rozsądkiem nie powstrzymuje w tym przypadku przed porównywaniem rodzaju pożywienia z ilością nóg.
Całkiem poprawnym kodem jest więc:

if (rodzajPozywienia==iloscNog) robCos();

My wiemy, że to bez sensu ale kompilator nie.

Do tego dochodzi to, że jeżeli ktoś nam wpisze w rodzajPozywienia wartość większą niż przewidziana przez nas znów zaczynają się problemy.

W OOP dlatego powinniśmy używać gdzie się da typów złożonych. Powyższy przykład mógłby wyglądać następująco:

public class Zwierze
{
public static class RodzajPozywienia
{
private int rodzajPozywienia;
public RodzajPozywienia(int rodzajPozywienia)
{
this.rodzajPozywienia=rodzajPozywienia;
}
}
public final static RodzajPozywienia FRYTKI = new RodzajPozywienia(0);
public final static RodzajPozywienia ZIEMNIAKI = new RodzajPozywienia(1);
public final static RodzajPozywienia SERKI_TOPIONE = new RodzajPozywienia(2);

public RodzajPozywienia rodzajPozywienia;
public int iloscNog;

[...]
}

Teraz już kod:

if (rodzajPozywienia==iloscNog) robCos();

nie zostanie skompilowany.

Mamy postęp, ale ileż pisania więcej, a do tego jakoś to mało czytelne.

W tym miejscu pojawiają sie enumy :-)


public class Zwierze
{
public enum RodzajPozywienia
{
FRYTKI, ZIEMNIAKI, SERKI_TOPIONE
};

public RodzajPozywienia rodzajPozywienia;
public int iloscNog;

[...]
}


Krótko i treściwie :-)
Jeżeli jeszcze chcielibyśmy np. dodać kaloryczność pożywienia [dane wezmę z sufitu :-)] to jest to bardzo proste:

public enum RodzajPozywienia
{
FRYTKI(100.0),
ZIEMNIAKI(75.4),
SERKI_TOPIONE(302.1);

private double kalorycznosc;

private RodzajPozywienia(double kalorycznosc)
{
this.kalorycznosc=kalorycznosc;
}

public double getKalorycznosc()
{
return kalorycznosc;
}
}

Trochę się to rozciąga, ale nadal jest dość czytelne, a w enumie mamy od razu dodatkowe informacje.

Enumy wymagają chwili by się ich nauczyć i je zrozumieć, ale czas zużyty na ich poznanie jest wart "zużycia".


Podobne postybeta
Javozagadka ;-)
Cieniowanie Gourauda w JavaScript part 2 - przyśpieszamy :-)
Przepływ sterowany danymi - A takie Java'owe coś ;-)
My - Niedouczeni ;-)
Groch z kapustą, czyli co daje mieszanie OOP z programowaniem strukturalnym