tło
Obecnie mam sytuację, w której mam obiekt przesyłany i odbierany przez urządzenie. Ten komunikat ma kilka konstrukcji, takich jak:
public void ReverseData()
public void ScheduleTransmission()
ScheduleTransmission
Metoda wymaga , aby wywołać ReverseData
metodę, gdy jest to tzw. Są jednak chwile, w których będę musiał wywoływać ReverseData
zewnętrznie (i powinienem całkowicie dodać poza obszarem nazw ), z którego obiekt jest tworzony w aplikacji.
Jeśli chodzi o „otrzymywanie”, to znaczy, że ReverseData
zostanie ono wywołane zewnętrznie w object_received
procedurze obsługi zdarzeń w celu cofnięcia danych.
Pytanie
Czy obiekt ogólnie może nazywać własne metody publiczne?
c#
design-patterns
object-oriented
methods
Podejrzeć
źródło
źródło
Odpowiedzi:
Powiedziałbym, że jest to nie tylko dopuszczalne, ale zachęcane, zwłaszcza jeśli planujesz zezwolić na rozszerzenia. Aby obsługiwać rozszerzenia klasy w języku C #, musisz oflagować metodę jako wirtualną zgodnie z komentarzami poniżej. Możesz jednak to udokumentować, aby ktoś nie był zaskoczony, gdy zastąpienie ReverseData () zmienia sposób działania ScheduleTransmission ().
To naprawdę sprowadza się do projektu klasy. ReverseData () brzmi jak podstawowe zachowanie twojej klasy. Jeśli chcesz użyć tego zachowania w innych miejscach, prawdopodobnie nie chcesz mieć innych jego wersji. Musisz tylko uważać, aby nie dopuścić do wycieku danych szczegółowych dotyczących ScheduleTransmission () do ReverseData (). To stworzy problemy. Ale ponieważ już używasz tego poza klasą, prawdopodobnie już to przemyślałeś.
źródło
ReverseData()
jest abstrakcyjne, bez względu na to, co robisz w klasie potomnej, zawsze będzie to oryginalna metoda, która zostanie wywołana.virtual
... A jeśli zastępujesz metodę, to z definicji musi to byćvirtual
lubabstract
.virtual
też działa. We wszystkich przypadkach podpis, według OP, jestpublic void ReverseData()
, więc część odpowiedzi na temat nadpisywania rzeczy jest nieco myląca.abstract
? Szukałem odpowiedzi naabstract
vsvirtual
i nie widziałem tego wspomnianego: raczej abstrakcyjny oznacza po prostu, że nie ma wersji podanej w tej bazie.Absolutnie.
Widoczność metody ma na celu wyłącznie umożliwienie lub odmowę dostępu do metody poza klasą lub w klasie potomnej; metody publiczne, chronione i prywatne zawsze mogą być wywoływane wewnątrz samej klasy.
Nie ma nic złego w wywoływaniu metod publicznych. Ilustracja w twoim pytaniu jest doskonałym przykładem sytuacji, w której posiadanie dwóch publicznych metod jest dokładnie tym, co powinieneś zrobić.
Możesz jednak uważnie obserwować następujące wzorce:
Hello()
Wywołanie metodyWorld(string, int, int)
wygląda następująco:Unikaj tego wzoru. Zamiast tego użyj opcjonalnych argumentów . Opcjonalne argumenty ułatwiają wykrywanie: wywołujący, który pisze,
Hello(
niekoniecznie będzie wiedział, że istnieje metoda, która umożliwia wywołanie metody z wartościami domyślnymi. Opcjonalne argumenty są również samo-dokumentujące.World()
nie pokazuje dzwoniącemu, jakie są rzeczywiste wartości domyślne.Hello(ComplexEntity)
Wywołanie metodyWorld(string, int, int)
wygląda następująco:Zamiast tego użyj przeciążeń . Ten sam powód: lepsza wykrywalność. Dzwoniący może natychmiast zobaczyć wszystkie przeciążenia za pomocą IntelliSense i wybrać właściwy.
Metoda po prostu wywołuje inne metody publiczne, nie dodając żadnej istotnej wartości.
Pomyśl dwa razy. Czy naprawdę potrzebujesz tej metody? A może powinieneś go usunąć i pozwolić dzwoniącemu wywołać różne metody? Wskazówka: jeśli nazwa metody wydaje się nieprawidłowa lub trudna do znalezienia, prawdopodobnie należy ją usunąć.
Metoda sprawdza poprawność danych wejściowych, a następnie wywołuje drugą metodę:
Zamiast tego
World
powinien sam zweryfikować swoje parametry lub być prywatny.źródło
Jeśli coś jest publiczne, może zostać wywołane w dowolnym momencie przez dowolny system. Nie ma powodu, dla którego nie możesz być jednym z tych systemów!
W wysoce zoptymalizowanych bibliotekach chcesz skopiować wprowadzony idiom
java.util.ArrayList#ensureCapacity(int)
.zapewnić pojemność
ensureCapacity
jest publicznyensureCapacity
ma wszystkie niezbędne sprawdzanie granic i wartości domyślne itp.ensureCapacity
połączeniaensureExplicitCapacity
sureCapacityInternal
ensureCapacityInternal
jest prywatneensureCapacityInternal
ma minimalne sprawdzanie błędów, ponieważ wszystkie dane wejściowe pochodzą z klasyensureCapacityInternal
TAKŻE połączeniaensureExplicitCapacity
zapewnićExplicitCapacity
ensureExplicitCapacity
jest również prywatnyensureExplicitCapacity
nie ma sprawdzania błędówensureExplicitCapacity
wykonuje rzeczywistą pracęensureExplicitCapacity
nie jest wywoływany z dowolnego miejsca opróczensureCapacity
iensureCapacityInternal
W ten sposób zaufany kod uzyskuje uprzywilejowany (i szybszy!) Dostęp, ponieważ wiesz, że jego dane wejściowe są dobre. Kod, któremu nie ufasz, przechodzi pewien rygor, aby zweryfikować jego integralność i bombardować lub zapewnić domyślne lub w inny sposób obsłużyć złe dane wejściowe. Oba prowadzą do miejsca, które faktycznie działa.
Jest to jednak używane w ArrayList, jednej z najczęściej używanych klas w JDK. Jest bardzo możliwe, że twoja obudowa nie wymaga takiego poziomu złożoności i rygoru. Krótko mówiąc, gdyby wszystkie
ensureCapacityInternal
połączenia zostały zastąpioneensureCapacity
połączeniami, wydajność byłaby naprawdę bardzo dobra. Jest to mikrooptymalizacja prawdopodobnie dokonana dopiero po dogłębnym rozważeniu.źródło
Podano już kilka dobrych odpowiedzi i zgodziłbym się z nimi, że tak, obiekt może wywoływać swoje metody publiczne z innych metod. Istnieje jednak niewielkie zastrzeżenie projektowe, na które trzeba uważać.
Metody publiczne zwykle zawierają umowę „weź obiekt w spójny stan, zrób coś sensownego, pozostaw obiekt w (możliwie innym) spójnym stanie”. W tym przypadku „spójny” może oznaczać na przykład, że
Length
aList<T>
nie jest większe niż jegoCapacity
i że odniesienia do elementów w indeksach od0
doLength-1
nie rzucą.Ale w metodach obiektu obiekt może znajdować się w niespójnym stanie, więc gdy wywołasz jedną z publicznych metod, może to zrobić coś bardzo złego, ponieważ nie zostało napisane z myślą o takiej możliwości. Jeśli więc planujesz wywołać swoje metody publiczne z innych metod, upewnij się, że ich umowa polega na „zabraniu obiektu w jakimś stanie, zrób coś sensownego, pozostaw obiekt w (być może innym) (być może niespójnym - ale tylko jeśli początkowy stan był niespójny).
źródło
Innym przykładem, gdy wywoływanie metody publicznej w innej metodzie publicznej jest całkowicie w porządku, jest podejście CanExecute / Execute. Używam go, gdy potrzebuję zarówno walidacji, jak i niezmiennej konserwacji .
Ale generalnie zawsze jestem ostrożny. Jeśli metoda
a()
jest wywoływana w metodzie wewnętrznejb()
, oznacza to, że metodaa()
ta jest szczegółem implementacjib()
. Bardzo często oznacza to, że należą one do różnych poziomów abstrakcji. A fakt, że oba są publiczne, sprawia, że zastanawiam się, czy jest to naruszenie zasady pojedynczej odpowiedzialności, czy nie.źródło
Przepraszam, ale muszę się nie zgodzić z większością innych odpowiedzi „tak, możesz” i powiedzieć, że:
Zniechęciłbym klasę, która nazywa jedną metodę publiczną inną
Istnieje kilka potencjalnych problemów z tą praktyką.
1: Nieskończona pętla w odziedziczonej klasie
Więc twoja klasa podstawowa wywołuje metodę1 z metody2, ale potem ty lub ktoś inny dziedziczy po niej i ukrywa metodę1 za pomocą nowej metody, która wywołuje metodę2.
2: Zdarzenia, rejestrowanie itp.
np. mam metodę Add1, która uruchamia zdarzenie „1 added!” Prawdopodobnie nie chcę, aby metoda Add10 wywoływała to zdarzenie, zapisywała dziennik lub cokolwiek innego, dziesięć razy.
3: gwintowanie i inne zakleszczenia
Np. InsertComplexData otwiera połączenie db, rozpoczyna transakcję, blokuje tabelę, a następnie wywołuje InsertSimpleData, otwierając połączenie, rozpoczyna transakcję, czeka na odblokowanie tabeli ...
Jestem pewien, że jest więcej powodów, jedna z pozostałych odpowiedzi dotyczy „edytujesz metodę 1 i dziwisz się, że metoda 2 zaczyna zachowywać się inaczej”
Ogólnie, jeśli masz dwie metody publiczne, które współużytkują kod, lepiej, aby obie wywoływały metodę prywatną, a nie jedną.
Edytować ----
Rozwińmy konkretny przypadek w PO.
nie mamy zbyt wielu szczegółów, ale wiemy, że funkcja ReverseData jest wywoływana przez moduł obsługi zdarzeń, a także metodę ScheduleTransmission.
Zakładam, że odwrotne dane również zmieniają wewnętrzny stan obiektu
Biorąc pod uwagę ten przypadek, uważam, że bezpieczeństwo wątków byłoby ważne, a zatem mój trzeci sprzeciw wobec tej praktyki ma zastosowanie.
Aby zapewnić bezpieczeństwo wątku ReverseData, możesz dodać blokadę. Ale jeśli ScheduleTransmission również musi być bezpieczny dla wątków, będziesz chciał udostępnić tę samą blokadę.
Najłatwiejszym sposobem jest przeniesienie kodu ReverseData na metodę prywatną i wywołanie go przez obie metody publiczne. Następnie można umieścić instrukcję blokady w metodach publicznych i udostępnić obiekt blokady.
Oczywiście możesz argumentować „to się nigdy nie wydarzy!” lub „Mógłbym zaprogramować blokadę w inny sposób”, ale najważniejszą kwestią dotyczącą dobrej praktyki kodowania jest dobre ustrukturyzowanie kodu.
W kategoriach akademickich powiedziałbym, że narusza to literę L na stałe. Metody publiczne są więcej niż publicznie dostępne. Można je również modyfikować przez jego spadkobierców. Twój kod powinien zostać zamknięty w celu modyfikacji, co oznacza, że musisz pomyśleć o tym, co robisz w metodach publicznych i chronionych.
Oto jeszcze jedna: potencjalnie naruszasz DDD. Jeśli twój obiekt jest obiektem domeny, jego publicznymi metodami powinny być Warunki Domeny, które coś znaczą dla firmy. W tym przypadku jest bardzo mało prawdopodobne, aby „kupić tuzin jaj” jest tym samym, co „kupić 1 jajko 12 razy”, nawet jeśli zaczyna się w ten sposób.
źródło