Czy istnieje dobry powód, aby funkcje czysto niepubliczne?

25

Odbyłem krótką debatę ze współpracownikiem. Mówiąc najprościej, czy istnieje dobry powód, aby ukrywać / enkapsulować funkcje, które są czyste?

Przez „czysty” rozumiem definicję wikipedii :

  • Zawsze zwraca te same wyniki z tego samego wejścia. (Ze względu na tę dyskusję Foo Create(){ return new Foo(); }uważa się ją za nieczystą, jeśli Foonie ma semantyki wartości.)
  • Nie używa stanu zmiennego (oprócz zmiennych lokalnych) ani I / O.
  • Nie powoduje skutków ubocznych.
Telastyn
źródło
26
Może istnieć argument, aby w ogóle nie uczynić takiej funkcji członkiem klasy.
Pieter B
6
@PieterB - rzeczywiście, choć nie wszystkie języki to obsługują, a niektóre języki umożliwiają moduł wewnętrzny nawet dla bezpłatnych funkcji.
Telastyn
3
Jaka była twoja opinia Nie sądzę, aby czystość funkcji była związana z tym, czy należy ona do publicznego API.
Andres F.,
@andresF. - debata dotyczyła tego, czy nieograniczoną zasadę walidacji należy podać do wiadomości publicznej. Argumentowałem, że ponieważ była to czysta funkcja, szkoda była niewielka. W tym konkretnym przypadku pomógł w testowaniu i prawdopodobnie został ponownie wykorzystany. To pytanie dotyczy bardziej tego, jak szeroko ten argument mógłby / powinien mieć zastosowanie.
Telastyn
2
@Telastyn Jeśli jest wielokrotnego użytku, ale nie wchodzi w zakres odpowiedzialności obecnej klasy, prawdopodobnie powinien znajdować się w osobnej klasie / module / w jakimkolwiek języku. Będąc częścią odpowiedzialności nowej klasy / modułu, z konieczności musiałby zostać upubliczniony. Ponieważ wspominasz o testowaniu, zauważę, że niekoniecznie musisz wyśmiewać tę metodę. Jako „szczegół implementacji” drwina z niego podczas testów daje niewielkie korzyści.
jpmc26

Odpowiedzi:

76

Czysta funkcja może nadal stanowić szczegół implementacji. Chociaż funkcja może nie wyrządzić szkody (z punktu widzenia nie zerwania ważnych niezmienników / umów), narażając ją zarówno autor, jak i użytkownicy tej klasy / modułu / pakietu tracą. Autor przegrywa, ponieważ teraz nie może go usunąć, nawet jeśli implementacja ulegnie zmianie, a funkcja nie będzie mu już przydatna. Użytkownicy przegrywają, ponieważ muszą je przejrzeć i zignorować dodatkowe funkcje, które nie są istotne dla używania interfejsu API, aby go zrozumieć.

Doval
źródło
33
+1. Publicznymi członkami twojej klasy jest jego interfejs API. Nie jest to pytanie „Czy może mnie to zranić, jeśli jest publiczne?”, A raczej „Czy powinno być częścią API?”
Ordous
1
Dodam tylko, że jeśli zaczniesz widzieć podobne funkcje wyskakujące w innych lokalizacjach, przekształć je w inną klasę, aby wiele klas mogło z niej korzystać. Lub jeśli jest to na początek problem, zdefiniuj go w osobnej klasie na początek.
jpmc26,
1
Pieczętowałbym klasy publiczne, które również nie powinny być przedłużane.
Den
+1 za wskazanie, że ukrywanie lub pokazywanie funkcji (lub klasy) są przede wszystkim kwestiami związanymi z ochroną i utrzymaniem interfejsu API i nie są związane z typem kodu.
Marco
42

Pytanie jest odwrócone.

Nie szukasz powodu, aby funkcja była niepubliczna. Na początek jest to niewłaściwy sposób myślenia (moim zdaniem). Rozumowanie powinno pójść w drugą stronę.

Innymi słowy - nie pytaj „dlaczego miałbym to uczynić prywatnym?”. Zapytaj: „dlaczego miałbym to upublicznić?”

W razie wątpliwości nie ujawniaj tego. To trochę jak brzytwa Ockhama - nie rozmnażaj się, ponieważ jest to konieczne.

EDYCJA: Reagowanie na kontrargumenty poruszone przez @Telastyn w komentarzach (aby uniknąć tam długiej dyskusji):

Słyszałem to z czasem, a nawet opowiadałem się za tym od dłuższego czasu, ale z mojego doświadczenia wynika, że ​​sprawy wydają się zbyt prywatne.

Tak, czasem jest to bolesne, jeśli klasa jest otwarta na dziedziczenie, ale nie można przesłonić niektórych metod prywatnych (których zachowanie chciałbyś zmienić).

Ale protectedby wystarczyło - i nadal jest niepubliczne.

Prowadzi to do dużej ilości duplikacji kodu i narzutów, aby dostać się do „rzeczy, które nie powinny być publiczne”, ale i tak są dostępne pośrednio.

Jeśli stanie się problematyczny, po prostu upublicznij go! Jest konieczność, o której mówiłem :)

Chodzi mi o to, że nie powinieneś tego robić na wszelki wypadek (YAGNI i wszystko).

Pamiętaj, że zawsze łatwiej jest upublicznić funkcję prywatną niż cofnąć ją do prywatności. Ten ostatni prawdopodobnie złamie istniejący kod.

Konrad Morawski
źródło
Słyszałem to z czasem, a nawet opowiadałem się za tym od dłuższego czasu, ale z mojego doświadczenia wynika, że ​​sprawy wydają się zbyt prywatne. Prowadzi to do dużej ilości duplikacji kodu i narzutów, aby dostać się do „rzeczy, które nie powinny być publiczne”, ale i tak są dostępne pośrednio.
Telastyn
3
@Telastyn IMHO, brzmi to tak, jakby aplikacja cierpiała na błędy projektowe; jeśli masz ochotę ujawnić metodę, ponieważ musisz ją wywołać w dyskretnych ścieżkach kodu, prawdopodobnie jest to znak, że ta metoda powinna zostać podzielona na własny moduł, który można wprowadzić lub w razie potrzeby włączyć, szczególnie jeśli jest to czysta funkcja .
leo
@leo - przepraszam, nie śledzę. Rozważ funkcję taką jak liczba całkowita mniejsza niż. Jest to miła, czysta funkcja, która jest szczególnie widoczna, ponieważ można ją następnie wstrzyknąć i włączyć (lub po prostu użyć) w razie potrzeby.
Telastyn
5
@Telastyn Moim zdaniem jest to objaw filozofii „wszystko jest przedmiotem / klasą” w połączeniu z faktem, że niektóre języki OOP nie mają złożonych typów danych innych niż klasy. Gdy tylko ktoś musi przekazać dwie wartości w tym samym czasie, kończy pisać klasę, a następnie każdy współpracownik i oprogramowanie sprawdzające styl powie ci, abyś uczynił te pola prywatnymi.
Doval
@Telastyn Right. Chodzi mi o to, że jeśli twoja metoda „integerLessThan” zaczęła się jako enkapsulowany szczegół implementacji metody publicznej, ale okaże się, że musisz wywołać metodę prywatną w innych miejscach, prawdopodobnie jest to znak, że metoda prywatna należy do innej moduł / pakiet / klasa, dzięki czemu można go importować niezależnie od logiki, która go wywołuje. Po prostu opublikowanie metody w stanie, w jakim się znajduje, oznaczałoby, że metoda jest arbitralnie umieszczona w pierwszym module, w którym uznano ją za przydatną.
leo
6

Nie sądzę, aby decyzja o ukryciu / zamknięciu funkcji mogła zależeć od jej czystości. To, że funkcja jest czysta, nie oznacza, że ​​zewnętrzni muszą o niej wiedzieć. Co ciekawe, jeśli funkcja jest czysta i ma być publiczna, może wcale nie musi być członkiem instancji interfejsu, może lepiej nadaje się jako statyczna. Ale znowu wszystko to zależy od intencji umowy iw tym przypadku logicznego grupowania funkcjonalności, a nie czystości funkcji.

pllee
źródło
5

Klasy powinny być zgodne z zasadą jednolitej odpowiedzialności . Chociaż klasa może potrzebować wezwać inną funkcjonalność, aby osiągnąć swoje cele, powinna ujawniać tylko funkcje, które są częścią jej pojedynczej odpowiedzialności.

Oto tylko jeden przykład przypadku, w którym widoczność może powodować problemy.

Rozważ klasę, która łączy widżety. Być może jako część kodu frobnication potrzebuje jakiejś funkcji narzędzia, która analizuje ciąg znaków: być może musi przekształcić nazwę widżetu w sposób, który nie obsługuje standardowych funkcji łańcucha.

Ponieważ jest to funkcja czysta (łańcuch przychodzi, jakoś go przekształca, zwraca nowy łańcuch), może być publiczny lub prywatny bez konsekwencji. A może to?

Jeśli podasz to do wiadomości publicznej, teraz twoja klasa ma dwa obowiązki: tworzenie widżetów i przekształcanie łańcuchów. Narusza to SRP i może powodować problemy, jeśli inne klasy polegają na tej funkcji. Ponieważ jest to coś, co Twoim zdaniem jest używane tylko wewnątrz klasy, być może zmienisz jej interfejs lub widoczność. Teraz klasy w innych częściach systemu są zepsute.

Zachowując prywatność tej funkcji, nikt nigdy nie ma możliwości polegania na kodzie, który nie jest częścią jednej klasy.


źródło
2
Twierdziłbym, że powinien on zawierać tylko funkcje, które są częścią jego pojedynczej odpowiedzialności, a nie tylko ujawniać.
Telastyn
1
Myślę, że jest szary obszar. Czy naprawdę potrzebujesz osobnej StringTransformerklasy do enkapsulacji dwóch lub trzech wierszy kodu, które są używane tylko w jednym miejscu? Zgadzam się, że po użyciu kodu w wielu miejscach najlepiej jest podzielić go na nową klasę z jedną odpowiedzialnością, ale istnieje kompromis.
Na pewno. Wytyczne są wytycznymi, a nie regułami.
Telastyn
@snowman in non java po prostu tworzysz bibliotekę funkcji.
Pieter B
@PieterB nie chodzi o Javę v. Cokolwiek innego. Aby było naprawdę OO, potrzebujesz fabryki, kilku klas abstrakcyjnych itp.