Dlaczego Java nie korzysta z enkapsulacji w niektórych klasach?

25

Moje pytanie dotyczy klas System.ini System.outklas (mogą istnieć inne, takie jak te w bibliotece Standard). Dlaczego? Czy to nie jest zła praktyka w OOP? Nie należy go używać jako: System.getIn()i System.getOut()? Zawsze miałem to pytanie i mam nadzieję, że znajdę tutaj dobrą odpowiedź.

Clawdidr
źródło

Odpowiedzi:

36

Definicje pól ini outw obrębie Systemklasy są następujące:

public final static PrintStream out;
public final static InputStream in;

To są stałe. Zdarza się, że są też obiektami, ale są stałymi. Jest bardzo podobny do klasy Math:

public static final double E = 2.7182818284590452354;
public static final double PI = 3.14159265358979323846;

Lub w klasie Boolean:

public static final Boolean TRUE = new Boolean(true);
public static final Boolean FALSE = new Boolean(false);

Lub w klasie Kolor:

public final static Color white     = new Color(255, 255, 255);
public final static Color black     = new Color(0, 0, 0);
public final static Color red       = new Color(255, 0, 0);

Podczas uzyskiwania dostępu do stałej publicznej, która się nie zmienia, nie ma istotnej korzyści z jej enkapsulacji - pod względem koncepcyjnym lub wydajnościowym. Jest tutaj. To się nie zmieni.

Nie ma prawdziwej różnicy między Color.whitei System.out.


źródło
Cóż, nie widziałem tego w ten sposób, są stałymi obiektami. To całkiem dobre wytłumaczenie.
Clawdidr,
1
@Clawdidr w dzisiejszej Javie, można rozważyć użycie ich enumdo trzymania ich ... chociaż enumbyło nowe w Javie 1.5 (nie jest to opcja w ciągu 1.0 dni).
Z ciekawości (nie jestem programistą Java): czy istnieje różnica między final statici static final? Jeśli tak to co to jest?
Marjan Venema,
1
@MarjanVenema bez różnicy, tylko preferowana kolejność modyfikatorów. Check Modifier Check pokazuje preferowaną kolejność. Najwyraźniej ktokolwiek napisał te dwa pliki źródłowe w wersji jdk, nie zgodziłem się na zamówienie. To tylko konwencja.
:) i dziękuję Michaelowi, za poświęcenie czasu na odpowiedź i link.
Marjan Venema,
5

Prawdziwym powodem jest to, że jest to problem spuścizny. Na System.in,out,errstałe były częścią Java 1.0 ... i pewnie dużo dalej wstecz. Gdy stało się jasne, że projekt ma problemy, było już za późno, aby go naprawić. Najlepsze, co mogli zrobić, to dodać System.setIn,setOut,setErrmetody w Javie 1.1, a następnie rozwiązać problemy ze specyfikacją języka 1 .

Jest to podobne do pytania, dlaczego istnieje System.arraycopymetoda statyczna , której nazwa narusza konwencje nazewnictwa Java.


Jeśli chodzi o to, czy jest to „zły projekt”, czy nie, myślę, że tak. Są sytuacje, w których obecna obsługa bez OO stanowi poważny problem. (Pomyśl ... jak możesz uruchomić jeden program Java w innym, gdy konflikt wymagań jego standardowego strumienia we / wy. Pomyśl ... kod testu jednostkowego, który pociąga za sobą zmianę strumieni.)

Mogę jednak również odnieść się do argumentu, że obecny sposób robienia rzeczy jest wygodniejszy w wielu przypadkach.


1 - Warto zauważyć, że System.in,out,errzmienne są szczególnie wymieniane w JLS jako mające „specjalną semantykę”. JLS mówi, że jeśli zmienisz wartość finalpola, zachowanie jest niezdefiniowane ... z wyjątkiem tych pól.

Stephen C.
źródło
2

Uważam, że nasz obiekt jest niezmienny, co czyni go w jakiś sposób bezpiecznym (jest to kwestia dyskusyjna) z powodu trzymania go w publicznym polu statycznym.

Wiele klas w JDK nie przestrzega najlepszych zorientowanych obiektowo zasad projektowania. Jednym z powodów tego jest fakt, że zostały napisane prawie 20 lat temu, kiedy Object-Orientation pojawił się jako główny paradygmat i wielu programistów po prostu ich nie znało. Bardzo dobrym przykładem złego projektu API jest Date & Time API, którego zmiana zajęła im 19 lat ...

m3th0dman
źródło
3
Sprawiłbym, że instrukcja byłaby jeszcze silniejsza, publiczna zmienna końcowa (statyczna lub nie) jest dokładnie tak samo „Bezpieczna” jak getter, a ja przypisałbym niezmienność parom setter / getter. Setery są zawsze złym pomysłem (niezmienny jest dobry - a jeśli nie można go uniezależnić od metody „ustawiania”, to prawdopodobnie również zasługuje na małą logikę biznesową). Jeśli potrzebujesz gettera, równie dobrze możesz go upublicznić ostateczne, ale żadne z nich nie jest lepsze - nie pytaj klasy o dane, poproś klasę, aby coś zrobiła z danymi.
Bill K
@BillK: Tak, znany również jako Prawo Demetera (choć nie jest to prawo, a raczej wytyczna).
śleske,
2

Ta odpowiedź jest świetna i prawdziwa.

Chciałem dodać, że w niektórych przypadkach dokonano kompromisów ze względu na użyteczność.

Obiekty typu String mogą być tworzone bez nowych, zdarzenie, gdy String nie jest prymitywem:

String s = "Hello";

Ciąg znaków, który nie jest prymitywny, powinien być utworzony w następujący sposób:

String s = new String("Hello");          // this also works 

Ale kompilator pozwala na krótszą, mniejszą liczbę opcji OO, ponieważ String jest zdecydowanie najczęściej używaną klasą w API.

Również tablice mogą być inicjowane w sposób inny niż OO:

int i[] = {1,2,3};

Co dziwne, obiekt jest instancją klasy lub tablicy . Tablice znaczeń są całkowicie odrębnym typem klas.

Tablice mają lengthpubliczne pole, które nie jest stałe. Również nie ma dokumentacji dotyczącej tablic klas, które są instancją. (nie mylić z klasą Arrays lub java.reflect.Array).

int a = myArray.length;    // not length()
Tulains Córdova
źródło
6
Są różne rzeczy - new String("Hello")zawsze utworzy nowy obiekt String. Chociaż String s = "Hello";użyje internowanego obiektu. Porównaj: "Hello" == "Hello"może być prawdziwe, podczas gdy new String("Hello") == new String("Hello")zawsze jest fałszywe. Po pierwsze dzieje się magia optymalizacji czasu kompilacji, która nie jest możliwa new String("Hello"). Zobacz en.wikipedia.org/wiki/String_interning
@MichaelT Dodałem trochę dziwności do tablic, dla samej kompletności.
Tulains Córdova,
2
@Tarik Sprzeciwiłem się temu, że „ciąg znaków, który nie jest prymitywny, powinien być zinstytucjonalizowany w ten sposób: String s = new String("Hello");”, co jest niepoprawne. Krótsza opcja ( String s = "Hello";) jest bardziej poprawna z powodu internowania łańcucha.
2
Dzięki Stringnie ma opcji „więcej poprawnych”. Obydwie są prawidłowe. Chociaż, jak mówi MichaelT, krótszy jest preferowany ze względu na internowanie String.
Andres F.,
1
@AndresF. Zmieniłem „mniej poprawne” na „mniej OO”.
Tulains Córdova,