- Operacja
szybka()
potrafi tylko powiedzieć czy coś spełnia warunek:
-
true
znaczy "na pewno spełnia"
-
false
znaczy "nie wiadomo czy spełnia"
- Operacja
wolna()
sprawdza już to na 100%:
-
true
znaczy "na pewno spełnia"
-
false
znaczy "na 100% nie spełnia"
Żadna z propozycji podanych w tym temacie mi się nie podoba.
Shallow, deep, heuristic, "sensitive and specific"... Ilu programistów poprawnie zrekonstruuje z tych terminów tę rozpiskę, którą podałeś powyżej? :)
"With false negatives" jest lepsze, ale rozwlekłe, wymaga chwili namysłu. Na dodatek ma wydźwięk, który - przynajmniej dla mnie - sugeruje nieprawidłowe działanie metody.
Kiedy znalezienie dobrych nazw stanowi problem, czasem warto zrobić krok do tyłu i zastanowić się, czy nie trzeba zmienić podejścia. Może problem wynika z prób upakowania trzech możliwych odpowiedzi w wartości boolean, których znaczenie zmienia się kontekstowo?
Może powinno być np. tak, że szybka metoda zwraca Optional<Boolean>
. A wolna - Boolean
.
Ważną informację przenosimy w ten sposób do sygnatur tych metod. Staje się oczywiste, że metoda szybka udziela odpowiedzi niepewnej. Dopilnuje tego sam silnik typów.
I odpada problem, że false
miewa dwa różne znaczenia zależnie od kontekstu. Co wcześniej mogło zdezorientować.
Znalezienie nazwy staje się wtedy łatwiejsze, bo:
nie wiem jak nazwać te metody, tak żeby było z samych nazw wiadomo:
- Że
szybka()
jest optymalna, ale potrafi tylko powiedzieć "na pewno tak, ale nie wiadomo czy nie".
- Że
wolna()
jest zasobożerna, ale potrafi na 100% ocenić poprawność.
Od tej pory nie masz już tego "ale". Teraz nazwa musi oddać tylko pierwszą różnicę. Druga wynika z sygnatury metody.
Możliwe nazwy:
Optional<Boolean> quicklyConfirm(Object o);
Boolean exhaustivelyConfirm(Object o);
A wtedy:
boolean check(Object o) {
if (szybka(o)) {
return true;
}
return wolna(o);
}
Zmieni się w:
boolean check(Object o) {
return quicklyConfirm(o)
.orElseGet(() -> exhaustivelyConfirm(o));
}
Wytknąć można jedno niedociągnięcie - kwestia dyskusji i gustu, czy w praktyce istotne. W praktyce quicklyConfirm
może zwrócić: Optional.of(true)
albo Optional.empty()
*.
Rezultat Optional.of(false)
jest niemożliwy. Ale to nie wynika już ani z nazwy, ani z sygnatury. Dotarliśmy do limitu tego, co może wyrazić boolean. Nawet udekorowany trójwartościowością.
Jeśli jesteśmy nieco pedantyczni i nam to przeszkadza...
...możemy pokusić się o jeszcze ściślejsze podejście.
Na przykład - używając Javy:
public class ConfirmationResult {
public interface ConfirmationStatus {
Optional<Boolean> value();
}
public enum Quick implements ConfirmationStatus {
CONFIRMED,
UNKNOWN;
@Override
public Optional<Boolean> value() {
if (this == CONFIRMED) {
return Optional.of(true);
}
return Optional.empty();
}
}
public enum Exhaustive implements ConfirmationStatus {
CONFIRMED,
REJECTED;
@Override
public Optional<Boolean> value() {
return Optional.of(this == CONFIRMED);
}
}
}
I teraz:
ConfirmationResult.Quick quicklyConfirm(Object o);
ConfirmationResult.Exhaustive exhaustivelyConfirm(Object o);
Oba enumy wspierają ten sam interfejs, więc mają wspólny mianownik: ConfirmationStatus
, więc można tę wartość podawać - i wyciągnąć z niej Optional<Boolean>
- nie interesując się kompletnie, czy jest to instancja Quick
, czy Exhaustive
.
A zarazem nie sposób się w żaden sposób pomylić. Wszystko jest udokumentowane na poziomie typów i definicji. if (exhaustivelyConfirm(o) == ConfirmationResult.Quick.UNKNOWN)
nawet się nie zbuduje. if (quicklyConfirm(o) == ConfirmationResult.Exhaustive.REJECTED)
nawet się nie zbuduje.
Można, jeśli potrzebujemy, napisać sobie nawet szybką "porównywarkę" dla instancji ConfirmationStatus
, która umożliwiłaby łatwe porównania między wartościami obu enumów. Jest to Java, więc niestety wszędzie włazi nam pełno boilerplate'u. Ale z tym męczy się pół świata i na to już nie ma prostej rady.
- Na potrzeby poglądowe używam jakiejś bibliotecznej wersji Optionala, bo akurat jest dodana do projektu, który mam otwarty w IDE. Więc tak mi prościej. Your naming may vary.