Odpowiedzi:
Umieszczanie statycznych elementów w interfejsie (i implementowanie tego interfejsu) jest złą praktyką i istnieje nawet nazwa, Antipattern Constant Interface , patrz Efektywna Java , punkt 17:
Stały wzorzec interfejsu to słabe wykorzystanie interfejsów . To, że klasa używa wewnętrznie pewnych stałych, jest szczegółem implementacji. Implementacja stałego interfejsu powoduje wyciek szczegółów implementacji do wyeksportowanego interfejsu API klasy. Dla użytkowników klasy nie ma żadnego znaczenia, że klasa implementuje stały interfejs. W rzeczywistości może ich to nawet zmylić. Co gorsza, stanowi zobowiązanie: jeśli w przyszłej wersji klasa zostanie zmodyfikowana tak, aby nie musiała już używać stałych, nadal musi zaimplementować interfejs, aby zapewnić zgodność binarną. Jeśli klasa nie ostateczna implementuje stały interfejs, wszystkie jej podklasy będą miały swoje przestrzenie nazw zanieczyszczone stałymi w interfejsie.
Istnieje kilka stałych interfejsów w bibliotekach platformy Java, takich jak
java.io.ObjectStreamConstants
. Te interfejsy powinny być traktowane jako anomalie i nie powinny być emulowane.
Aby uniknąć pułapek związanych ze stałym interfejsem (ponieważ nie można uniemożliwić ludziom jego implementacji), należy preferować odpowiednią klasę z prywatnym konstruktorem (przykład zapożyczony z Wikipedii ):
public final class Constants {
private Constants() {
// restrict instantiation
}
public static final double PI = 3.14159;
public static final double PLANCK_CONSTANT = 6.62606896e-34;
}
Aby uzyskać dostęp do stałych bez konieczności ich pełnej kwalifikacji (tj. Bez konieczności poprzedzania ich nazwą klasy), użyj importu statycznego (od wersji Java 5):
import static Constants.PLANCK_CONSTANT;
import static Constants.PI;
public class Calculations {
public double getReducedPlanckConstant() {
return PLANCK_CONSTANT / (2 * PI);
}
}
„ Stały wzorzec interfejsu to słabe wykorzystanie interfejsów ”
Ktokolwiek wymyślił tę hipotezę, niezależnie od tego, jakim był guru, wymyślił ją na podstawie potrzeby dalszego skutecznego wdrażania złych nawyków i praktyk. Hipoteza opiera się na promowaniu słuszności złych nawyków projektowania oprogramowania.
Napisałem odpowiedź obalającą tę hipotezę tutaj: Jaki jest najlepszy sposób implementacji stałych w Javie? wyjaśniając bezpodstawność tej hipotezy.
Pytanie to pozostawało otwarte przez 10 lat, aż zostało zamknięte w ciągu 2 godzin po tym, jak zamieściłem moje powody, które obalały tę hipotezę, w ten sposób narażając NIEWOLI do debaty tych, którzy mocno trzymają się tej błędnej hipotezy.
Oto punkty, które przedstawiłem przeciwko hipotezie
Podstawą do przyjęcia tej hipotezy jest potrzeba metod i OGRANICZAJĄCYCH reguł radzenia sobie ze skutkami złych nawyków i metodologii oprogramowania.
Zwolennicy sentymentu „ Ciągły wzorzec interfejsów to kiepskie wykorzystanie interfejsów” nie są w stanie podać żadnych innych powodów niż te spowodowane potrzebą radzenia sobie ze skutkami tych złych nawyków i praktyk.
Rozwiąż podstawowy problem.
Dlaczego więc nie w pełni wykorzystać i nie wykorzystać każdej funkcji językowej struktury języka Java dla własnej wygody. Nie są wymagane kurtki. Po co wymyślać zasady, które blokują Twój nieefektywny styl życia, aby dyskryminować i obciążać bardziej efektywny styl życia?
jest organizacją informacyjną. Informacje pośredniczące w procesie oraz zachowanie tych informacji należy najpierw zrozumieć wraz z tzw. Regułami biznesowymi - przed inżynierią lub uzupełnieniem rozwiązań w procesie. Taki sposób organizacji informacji kilkadziesiąt lat temu nazwano normalizacją danych.
Wtedy możliwa jest tylko inżynieria rozwiązania, ponieważ dopasowanie ziarnistości i modularności składników rozwiązania z ziarnistością i modułowością składników informacji jest strategią optymalną.
Istnieją dwie lub trzy istotne przeszkody w organizowaniu informacji.
Brak postrzegania potrzeby „normalizacji” modelu danych.
Oświadczenia EF Codda dotyczące normalizacji danych są błędne, wadliwe i niejednoznaczne.
Najnowszą modą podszywającą się pod zwinną inżynierię jest błędne przekonanie, że nie należy planować i warunkować organizacji modułów z wyprzedzeniem, ponieważ można je modyfikować na bieżąco. Jako wymówkę używa się refaktoryzacji i ciągłych zmian bez przeszkadzania przez przyszłe odkrycia. Niezbędne są wtedy odkrycia zachowania informacji procesowych, przy użyciu sztuczek księgowych, aby opóźnić zyski i aktywizację, w związku z czym uważa się, że podstawowa wiedza i ich przetwarzanie nie są obecnie potrzebne.
Nie wymyślaj reguł ani nie wystawiaj na to fatwy tylko dlatego, że kochasz swoje nawyki programistyczne ad-hoc typu hit and run.
Nie zakazuj posiadania broni z tego powodu, że są ludzie, którzy albo nie wiedzą, jak obchodzić się z bronią, albo są skłonni do jej nadużywania.
Jeśli reguły, które wymyślisz, są przeznaczone dla początkujących programistów, którzy nie potrafią profesjonalnie kodować i zaliczasz się do nich, powiedz to - nie deklaruj swojej fatwy jako stosownej do odpowiednio znormalizowanych modeli danych.
Nie obchodzi mnie, jakie pierwotne intencje ojców założycieli mieli co do Konstytucji Stanów Zjednoczonych. Nie obchodzą mnie niepisane, nieskodyfikowane intencje. Dbam tylko o to, co jest dosłownie skodyfikowane w spisanej Konstytucji i jak mogę je wykorzystać do efektywnego funkcjonowania społeczeństwa.
Zależy mi tylko na tym, na co pozwalają mi specyfikacje języka / platformy Java i zamierzam je w pełni wykorzystać, aby zapewnić mi medium do wydajnego i skutecznego przedstawiania moich rozwiązań programowych. Nie są wymagane żadne kurtki.
Wymaga napisania dodatkowego kodu, aby odwzorować parametr na wartość. Fakt, że twórcy Javy nie zapewnili mapowania wartości parametrów bez twojego napisania, że kod mapujący pokazuje, że Stałe Enum są równie niezamierzonym użyciem języka Java.
Zwłaszcza, że nie jest się zachęcanym do normalizowania i komponowania parametrów, można by odnieść fałszywe wrażenie, że parametry zmieszane w torebce Enum należą do tego samego wymiaru.
Nie zapomnij o tym. Jeśli zaprojektowałeś i znormalizowałeś swój model danych i zawiera on stałe, wtedy te stałe są kontraktami. Jeśli nie znormalizowałeś swojego modelu danych, powinieneś zastosować się do podanych przez Ciebie fatwy, jak praktykować restrykcyjne kodowanie, aby poradzić sobie z tym złym nawykiem.
Dlatego interfejsy są doskonałym sposobem implementacji kontraktu Constants.
Tak. Każdy może nieumyślnie zaimplementować dowolny interfejs. Nic nie stanie na przeszkodzie takim przypadkowym programistom.
Nie wprowadzaj restrykcyjnych dekretów, aby chronić domniemane złe praktyki, które powodują wyciek niezakontraktowanych / zbłąkanych parametrów do API. Rozwiąż podstawowy problem, zamiast przypisywać winę stałym interfejsu.
Normalnie działający i SKUTECZNY programista nie jest obecny, aby udowodnić, jak długo może przebywać pod wodą, jak daleko może przejść w piekielnym upale lub mokrej burzy. Ma używać wydajnego narzędzia, takiego jak samochód, autobus lub przynajmniej rower, aby codziennie zabierać ją do pracy 10 mil.
Nie nakładaj ograniczeń na innych programistów tylko dlatego, że masz obsesję na punkcie ezoterycznej ascezy programowania bez IDE.
OSGI jest takim szkieletem. Tak samo jest z dekretem dotyczącym stałych interfejsu.
Stałe interfejsu są skutecznym i wydajnym sposobem umieszczania w kontraktach dobrze zaprojektowanych i znormalizowanych komponentów modelu danych.
Stałe interfejsu w odpowiednio nazwanym interfejsie prywatnym zagnieżdżonym w pliku klasy to również dobra praktyka polegająca na grupowaniu wszystkich prywatnych stałych zamiast rozpraszania ich po całym pliku.
Kilka razy natknąłem się na to stare pytanie, a przyjęta odpowiedź wciąż mnie dezorientuje. Po długim namyśle myślę, że to pytanie można dalej wyjaśnić.
Wystarczy je porównać:
public final class Constants {
private Constants() {
// restrict instantiation
}
public static final double PI = 3.14159;
public static final double PLANCK_CONSTANT = 6.62606896e-34;
}
vs
public interface Constants {
double PI = 3.14159;
double PLANCK_CONSTANT = 6.62606896e-34;
}
To samo użycie. Znacznie mniej kodu.
Myślę, że odpowiedź @Pascal Thivent ma niewłaściwy nacisk, oto moja wersja:
Umieszczanie statycznych elementów członkowskich w interfejsie ( i implementowanie tego interfejsu ) jest złą praktyką.
Cytat z Effective Java zakłada, że interfejs jest implementowany przez innych, co moim zdaniem nie powinno (i nie będzie).
Kiedy tworzysz stały interfejs o nazwie podobnej Constants
, nikt nie powinien go implementować. (choć technicznie możliwe, co jest jedynym problemem tutaj)
Biblioteka standardowa nie może pozwolić sobie na jakiekolwiek możliwe niewłaściwe wykorzystanie projektu, więc po prostu go tam nie zobaczysz.
Jednakże , dla normalnych codziennych projektów, programistów wykorzystujących interfejs stałych jest o wiele łatwiejsze, ponieważ nie musisz się martwić static
, final
, empty constructor
, etc, i to nie spowoduje żadnego problemu złego projektu. Jedynym minusem, o jakim przychodzi mi do głowy, jest to, że nadal ma nazwę „interfejs”, ale nic poza tym.
Na koniec myślę, że wszyscy po prostu cytują z książek, opiniują i uzasadniają swoje stanowiska. Nie ma dla mnie wyjątku. Może decyzja nadal należy do twórców każdego projektu. Po prostu użyj go, jeśli czujesz się komfortowo. Najlepsze, co możemy zrobić, to zapewnić spójność w całym projekcie .
public
można również pominąć, ponieważ jest to interfejs, dzięki czemu jest po prostu. double PI = 3.14159;
Użycie Constants.PI
nie potrzebuje klasy używającej tego do zaimplementowania Constants
interfejsu! Myślę, że podejście do interfejsu jest znacznie czystsze pod względem użytkowania, IMHO
Joshua Bloch, „Effective Java - Programming Language Guide”:
Stały wzorzec interfejsu to słabe wykorzystanie interfejsów. To, że klasa używa wewnętrznie pewnych stałych, jest szczegółem implementacji. Implementacja stałego interfejsu powoduje wyciek szczegółów implementacji do wyeksportowanego interfejsu API klasy. Dla użytkowników klasy nie ma żadnego znaczenia, że klasa implementuje stały interfejs. W rzeczywistości może ich to nawet zmylić. Co gorsza, stanowi zobowiązanie: jeśli w przyszłej wersji klasa zostanie zmodyfikowana tak, aby nie musiała już używać stałych, nadal musi zaimplementować interfejs, aby zapewnić zgodność binarną. Jeśli klasa nie ostateczna implementuje stały interfejs, wszystkie jej podklasy będą miały swoje przestrzenie nazw zanieczyszczone stałymi w interfejsie.
Są przydatne, jeśli masz wspólne stałe, które będą używane w klasach implementujących interfejs.
Oto przykład: http://www.javapractices.com/topic/TopicAction.do?Id=32
Należy jednak pamiętać, że zalecaną praktyką jest używanie statycznych importów zamiast stałych w interfejsach. Oto odniesienie: http://www.javapractices.com/topic/TopicAction.do?Id=195
Są odpowiedzi, które są bardzo rozsądne.
Ale mam pewne przemyślenia na ten temat. (może się mylić)
Moim zdaniem pola w interfejsie nie powinny być stałymi dla całego projektu, są jedynie środkami dla interfejsu, a interfejsy go rozszerzają i klasy, które te interfejsy implementują lub mają z nimi ścisły związek. Powinny być używane w pewnym zakresie, a nie globalnym.
Dwie kwestie dotyczące interfejsu:
Interfejs opisuje podzbiór tego, co może zrobić obiekt, który go implementuje. (To jest intuicja)
Interfejs opisuje wspólne stałe, po których następują obiekty, które go implementują.
Więc myślę, że jeśli Constants Interface nie jest używany do globalnych stałych , to jest to dopuszczalne:
implements
ją (i oczywiście użyj tych wspólnych stałych w implementacji).Przykład:
interface Drawable {
double GOLDEN_RATIO = 1.618033988;
double PI = 3.141592653;
...
// methods
...
}
public class Circle implements Drawable {
...
public double getCircumference() {
return 2 * PI * r;
}
}
void usage() {
Circle circle = new Circle(radius: 3.0);
double maxRadius = 5.0;
if ( circle.getCircumference() < 2 * Circle.PI * maxRadius ) {
...
}
}
W tym przykładzie:
Circle implements Drawable
, od razu wiesz, że Circle
prawdopodobnie jest zgodny ze stałymi zdefiniowanymi w Drawable
, w przeciwnym razie będą musieli wybrać gorszą nazwę od dobrych PI
i GOLDEN_RATIO
już została zajęta!Drawable
obiekty są zgodne ze specyfiką PI
i GOLDEN_RATIO
zdefiniowane w Drawable
, mogą istnieć obiekty, które nie Drawable
mają różnej precyzji pi i złotego podziału.javax.swing.SwingConstants
Interfejs jest przykładem, który dostał pól statycznych, które są wykorzystywane w klasach skrzydłowych. Pozwala to na łatwe użycie czegoś takiego jak
this.add(LINE_START, swingcomponent);
this.add(this.LINE_START, swingcomponent);
lub this.add(SwingComponents.LINE_START, swingcomponent);
Jednak ten interfejs nie ma metod ...
Natknąłem się na to pytanie i pomyślałem, że dodam coś, o czym nie było mowy. Ogólnie rzecz biorąc, zgadzam się z odpowiedzią Pascala tutaj . Jednak nie sądzę, aby stałe na interfejsie były „zawsze” antywzorem.
Na przykład, jeśli definiowane przez Ciebie stałe są częścią kontraktu dla tego interfejsu, myślę, że interfejs jest doskonałym miejscem na stałe. W niektórych przypadkach prywatna weryfikacja parametrów bez ujawniania umowy użytkownikom implementacji nie jest odpowiednia. Bez publicznego kontraktu użytkownicy mogą tylko zgadywać, z czym walidujesz, bez dekompilacji klasy i czytania kodu.
Jeśli więc zaimplementujesz interfejs, a interfejs ma stałe, których używasz, aby zapewnić swoje kontrakty (na przykład zakresy liczb całkowitych), wówczas użytkownik Twojej klasy może być pewien, że używa instancji interfejsu poprawnie, sprawdzając stałe w interfejsie sami. Byłoby to niemożliwe, gdyby stałe były prywatne dla twojej implementacji lub twoja implementacja była pakietem prywatnym lub coś w tym rodzaju.
Używam stałych interfejsu, gdy mam do czynienia ze stałymi współdzielonymi między klasami.
public interface TestConstants
{
String RootLevelConstant1 = "RootLevelConstant1";
interface SubGroup1
{
String SubGroupConstant1 = "SubGroup1Constant1";
String SubGroupConstant2 = "SubGroup1Constant2";
}
interface SubGroup2
{
String SubGroupConstant1 = "SubGroup2Constant1";
String SubGroupConstant2 = "SubGroup2Constant2";
}
}
Grupowanie jest ogromnym atutem, szczególnie przy dużym zestawie stałych.
Aby użyć, po prostu połącz je ze sobą:
System.out.println(TestConstants.SubGroup1.SubGroupConstant1);
System.out.println(TestConstants.SubGroup2.SubGroupConstant1);
System.out.println(TestConstants.RootLevelConstant1);