Kiedy się korzysta z modyfikatora "private"?

0

Co oznacza modyfikator private w języku C# można znaleźć z łatwością. Ale zastanawia mnie fakt, po co się go w ogóle używa? W jednej z książek o testowaniu znalazłem cytat:

R. Osharove napisał(a):

Metody prywatne lub chronione - zazwyczaj zgodnie z tym, co myśli deweloper - nie bez powodu są prywatne. Czasami chodzi o to, aby ukryć szczegóły implementacji, tak aby później można było zmienić implementację, nie zmieniając ostatecznej funkcjonalności. Może to być również związane z bezpieczeństwem lub dotyczyć ochrony kodu (na przykład zaciemniania).

VS2017 (nie jestem pewien co do poprzednich wersji) przy automatycznym generowaniu metod domyślnie tworzy je jako private. Można by pomyśleć "hmm, chyba tak jest lepiej". Ale dlaczego lepiej? Przecież publiczne metody są testowalne i dostępne z innych klas. W takim razie w jakim celu je ukrywać dla innych klas? Rozumiem, że proste funkcje, lub funkcje które jedynie korzystają z biblioteki/frameworka nie wymagają testowania i mogą pozostać prywatne. Ale dalej ciekaw jestem, po co? Można też refaktoryzować większe metody na mniejsze prywatne, ale czemu mają być prywatne?

Podsumowując, co może się stać złego jak metoda będzie publiczna? W jakich sytuacjach jest to niebezpieczne?

4

Metody prywatne są używane przez publiczne. Po prostu jak chcesz metodę publiczną rozbić na drobne czytelne kawałeczki, to sobie robisz kilka prywatnych metod, które używa ta publiczna.
Ale czemu do tych drobnych podzadań ma mieć ktoś dostęp z zewnątrz? Zwłaszcza jeśli te zadania wykonane wybiórczo mogą zaszkodzić działaniu zadaniu realizowanemu przez daną klasę? Np. zmienisz wartości pól, które powinny być zmienione razem z innymi polami, aby wszystkie dane dotyczyły tego samego przebiegu algorytmu. Np. metoda publiczna SetData z odpowiednimi argumentami wywoła kilka metod prywatnych, które przetworzą/wstawią dane z argumentów w odpowiednie pola, w odpowiedniej kolejności. Jeśli programista korzystający z tej klasy mógłby skorzystać z metod prywatnych, to jest szansa, że narobiłby bajzel, odpalając różne metody w złej kolejności i nie mógłby dojść, co jest przyczyną. Więc lepiej zrobić publiczną, idiotoodporną metodę, zamiast pisać w dokumentacji, żeby ładować dane do klasy w takiej i takiej kolejności...

5

Robisz metode publiczną, jeśli jest powód, żeby ją upubliczniać, podzielić się nią ze światem. Jeśli jest używana tylko w swojej klasie to nie ma sensu jej upubliczniać a nawet nie powinno się :) z takich podstawowych publiczntch to na pewno metody implementujące interfejs, jakieś utilsy/commonsy

3

Gdyby była klasa nazwijmy ją Kitchen i udostępnia ona jedną publiczną metodę BakeACake(), z której mogą korzystać inne klasy.
Natomiast klasa Kitchen posiada kilka metod prywatnych, z których nie powinny korzystać inne klasy np:
kneadTheDough()
layOutTheDoughOnTheBakingTray()
putSomeIngredientsruitOnTheCake
bakeACake()

Wówczas metoda publiczna, BakeACake() mogłaby wyglądać mniej więcej tak:

bool BakeACake()
{
  bool result = false;

  result = kneadTheDough();

  if( result == true )
    result = layOutTheDoughOnTheBackingTray();

  if( result == true )
    putSomeIngredientsOnTheCake();
  
  if( result == true )
    result = bakeACake();

  return result;
}

Wyobraź sobie, że twoja klasa piecze ciasto z owocami (tak zaimplementowałeś metodę putSomeIngredientsOnTheCake, że kładzie na ciasto owoce). Jednak chcesz to zmienić, żeby robiła sernik. Wtedy zmieniasz implementację tej metody, a kod "na około" zostaje taki sam. Czyli z wielu miejsc nadal wołasz BakeACake(). To tylko taki prosty przykład, mam nadzieję, że może coś wyjaśnił.

3

Ja ujmę to, co napisane wyżej. Możemy mieć kod podzielony na małe metody, które są wywoływane w publicznych. Może się okazać, że metoda publiczna blokuje muteks a nie robią tego metody prywatne. Może tak być w przypadku klasy reprezentującej listę. Blokowanie muteksu nie jest potrzebne na samą alokację nowego obiektu, ale na moment dodania albo sprawdzania rozmiaru.
Jeszcze jeden przykład:

public class Window extends JFrame {
	private static final long serialVersionUID = 6594367122489466981L;
	
	public Window() {
		init("Nazwa okna");
	}
	
	public Window(String title) {
		init(title);
	}

	private void init(String title) {
		setTitle(title);
		setDefaultCloseOperation(EXIT_ON_CLOSE);
		setLayout(new BorderLayout());
	}
}

Mamy kawałek kodu powtarzający się w kilku miejscach. Może to być chociażby zmiana rozmieszczenia elementów GUI po dodaniu przycisku. Jak chcę dodać element do interfejsu, to nie ma potrzeby, żebym pamiętał o układzie.

4

Publiczne metody stanowią pewien "kontrakt" na którym inni będą polegać. Jeśli twoja klasa miała publiczną metodę X to jest szansa że ktoś jej używa, szczególnie jeśli mówimy o jakiejś bibliotece. To oznacza że nie mozesz już tej klasy refaktorować, bo złamiesz kompatybilność wsteczną. Gdyby metoda była prywatna, tego problemu by nie było. Miałbyś klasyczną zaletę enkapsulacji -> masz publiczny interfejs i masz wewnętrzną implementacje, która moze sie zmieniać, o ile kontrakt pozostaje ten sam.
Generalnie udostępnianie szczegółów implementacyjnych to błąd, bo cementuje ci ten kod.

1
bakunet napisał(a):

Podsumowując, co może się stać złego jak metoda będzie publiczna? W jakich sytuacjach jest to niebezpieczne?

Jeśli będzie publiczna, to pewnie ktoś ktoś prędzej czy później Ci jej użyje, przez co przy ewentualnych zmianach masz wiecej zabawy

0

Dzięki wszystkim za odpowiedzi. Poniższy akapit chyba najlepiej pozwolił mi zrozumieć, że metody publiczne, jako że implementują interfejsy, są dostępne z zewnątrz, są testowane, nie powinny być zmieniane i korzystają z prywatnych, które mogą być refaktoryzowane dla zachowania ogólnej funkcjonalności metody publicznej. A prywatne, jako że mogą podlegać zmianom, nie powinny być testowane, bo wtedy trzeba by było pisać w kółko testy, oraz ich funkcjonalność, gdyby była dostępna z zewnątrz, by się ciągle zmieniała. W sumie to mega dobrze to wiedzieć, trochę to wpłynie na moje podejście do kodu.

Shalom napisał(a):

Publiczne metody stanowią pewien "kontrakt" na którym inni będą polegać. Jeśli twoja klasa miała publiczną metodę X to jest szansa że ktoś jej używa, szczególnie jeśli mówimy o jakiejś bibliotece. To oznacza że nie mozesz już tej klasy refaktorować, bo złamiesz kompatybilność wsteczną.

3

A prywatne, jako że mogą podlegać zmianom, nie powinny być testowane

Powinny testować sie "automatycznie" kiedy testujesz publiczne API. W ogóle nie pisze się testów "metody" czy "klasy", bo to nie ma sensu. Pisze się testy dla konkretnego feature, konkretnej funkcjonalności którą dostarczasz. I one są właśnie określone przez API/kontrakt który udostępniasz.

1

Dodam jeszcze że chodzi o pewne podstawowe koncepcje OOP, w tym przypadku enkapsulacje. Mając prywatne metody ukrywamy szczegóły implementacji które dla użytkowników klasy nie mają znaczenia, a więc nie powinny być wystawiane publicznie. Pozwala to uniknąć wyżej wymienionych źródeł błędów.

2

Ogólnie ten problem może być trudny do zrozumienia, gdy tworzysz małe aplikacje z prostą logiką pisane "na sztywno", czyli bez pomyślenia o dalszych modyfikacjach i rozbudowie. Gdy jednak kod rośnie jego utrzymanie robi się kłopotliwe to to wszystko zaczyna mieć znaczenie. Na to właśnie odpowiedzią miało być OOP. W OOP jest wiele rozwiązań i bardziej lub mniej formalnych zasad (SOLID, KISS, DRY YAGNI, LoD itp), które pozwalają pisać i utrzymywać duży kod.

O modyfikatorach metod lubię myśleć w ten sposób, że każdy modyfikator inny niż private stwarza pewien dług, dlatego że udostępniając w interfejsie klasy "coś" i musisz później to utrzymywać. Tutaj warto zauważyć, że klient kodu może mieć dostęp nie tylko do public ale i protected (przy dziedziczeniu), więc stosowanie protected też zwiększa ten dług (choć w zdecydowanie mniejszym stopniu niż public). Ja osobiście jako domyślny obecnie stosuje "private", a w pewnych sytuacjach (np. libka narzędziowa) protected aby nie utrudniać rozszerzania / modyfikowania innym. Staram się aby moje klasy udostępniały 1 metodę publiczną (pomijam tu obiekty DTO itp.) i jeśli jest ich więcej to staram się pomyśleć czy by nie podzielić na 2 klasy - ale oczywiście wszystko z rozsądkiem.

Niestety OOP to jest dość skomplikowany twór i wiele zasad opisywanych w podręcznikach mądrych ludzi trudno jest zrozumieć czy przyjąć na wiarę. Dopiero jak kilka razy wdepniesz w kupę w większym projekcie zaczynasz je doceniać i tak na prawdę rozumieć. Zawsze gdy już wydaje mi się, że rozumiem OOP to pojawia się nowy "level" i zmieniam spojrzenie na wiele rzeczy.

0

Poczytaj o hermetyzacji. Ogólnie chodzi o to, by jak najwięcej metod/właściwości chować w ich własnym ekosystemie, tudzież klasie. To tak, jakbyś uchylał okno, zamiast otwierać je na oścież wraz z drzwiami, tworząc przeciąg.

1 użytkowników online, w tym zalogowanych: 0, gości: 1