Zastanawiałem się nad dobrym projektowaniem klas obiektowych. W szczególności trudno mi zdecydować między tymi opcjami:
- metoda statyczna vs instancja
- metoda bez parametrów lub zwracanej wartości vs metoda z parametrami i zwracaną wartością
- nakładanie się i odrębna funkcjonalność metody
- metoda prywatna a publiczna
Przykład 1:
Ta implementacja korzysta z metod instancji, bez wartości zwracanej lub parametrów, bez nakładających się funkcji, a wszystkie metody są publiczne
XmlReader reader = new XmlReader(url);
reader.openUrl();
reader.readXml();
Document result = reader.getDocument();
Przykład 2:
Ta implementacja wykorzystuje metody statyczne z zwracanymi wartościami i parametrami, z nakładającymi się funkcjami i metodami prywatnymi
Document result = XmlReader.readXml(url);
W przykładzie pierwszym wszystkie metody są instancjami publicznymi, co ułatwia ich testowanie jednostkowe. Chociaż wszystkie metody są różne, readXml () jest zależny od openUrl (), ponieważ openUrl () musi zostać wywołany jako pierwszy. Wszystkie dane są deklarowane w polach instancji, więc nie ma wartości zwracanych ani parametrów w żadnej metodzie, z wyjątkiem konstruktora i akcesoriów.
W przykładzie drugim tylko jedna metoda jest publiczna, pozostałe są prywatne statyczne, co utrudnia ich testowanie jednostkowe. Metody nakładają się na siebie w wywołaniach readXml () openUrl (). Nie ma pól, wszystkie dane są przekazywane jako parametry w metodach, a wynik jest natychmiast zwracany.
Jakie zasady należy stosować, aby właściwie programować obiektowo?
źródło
Odpowiedzi:
Przykład 2 jest dość zły do testowania ... i nie mam na myśli, że nie możesz przetestować wewnętrznych. Nie możesz również zastąpić swojego
XmlReader
obiektu próbnym obiektem, ponieważ w ogóle go nie masz.Przykład 1 jest niepotrzebnie trudny w użyciu. Co powiesz na
co nie jest trudniejsze w użyciu niż metoda statyczna.
Takie rzeczy jak otwieranie adresu URL, czytanie XML, konwertowanie bajtów na ciągi, parsowanie, zamykanie gniazd i tak dalej, są nieciekawe. Ważne jest tworzenie obiektu i korzystanie z niego.
Więc IMHO właściwym projektem OO jest upublicznienie tylko tych dwóch rzeczy (chyba że naprawdę potrzebujesz pośrednich kroków z jakiegoś powodu). Statyczne jest złe.
źródło
Document result = new XmlReader(url).getDocument();
Dlaczego? Abym mógł go uaktualnićDocument result = whateverReader.getDocument();
iwhateverReader
podarować mi przez coś innego.O to chodzi - nie ma jednej właściwej odpowiedzi i nie ma absolutnej definicji „właściwego projektowania obiektowego” (niektórzy ludzie ci to oferują, ale są naiwni ... daj im czas).
Wszystko sprowadza się do twoich CELÓW.
Jesteś artystą, a papier jest pusty. Możesz narysować delikatny, drobnoziarnisty czarno-biały portret z boku lub abstrakcyjny obraz z ogromnymi nacięciami mieszanych neonów. Lub coś pomiędzy.
Co więc POCZUJE SIĘ w odniesieniu do problemu, który rozwiązujesz? Jakie są skargi osób, które muszą korzystać z twoich zajęć do pracy z xml? Co jest trudne w ich pracy? Jakiego rodzaju kod starają się napisać, który otacza wywołania do Twojej biblioteki, i jak możesz pomóc im w tym, aby przepływ był lepszy?
Czy chcieliby więcej zwięzłości? Czy chcieliby, aby był bardzo sprytny w ustalaniu domyślnych wartości parametrów, aby nie musieli określać zbyt wiele (ani nic takiego) i zgaduje poprawnie? Czy potrafisz zautomatyzować zadania związane z konfiguracją i czyszczeniem, których wymaga biblioteka, aby nie mogły zapomnieć o tych krokach? Co jeszcze możesz dla nich zrobić?
Do diabła, prawdopodobnie musisz to zrobić na 4 lub 5 różnych sposobów, a następnie załóż kapelusz klienta i napisz kod, który używa wszystkich 5, i zobacz, co jest lepsze. Jeśli nie możesz tego zrobić dla całej biblioteki, zrób to dla podzbioru. Musisz także dodać kilka dodatkowych alternatyw do swojej listy - co z płynnym interfejsem, bardziej funkcjonalnym podejściem, nazwanymi parametrami lub czymś opartym na DynamicObject, abyś mógł wymyślić sensowne „pseudo-metody”, które im pomogą na zewnątrz?
Dlaczego jQuery jest teraz królem? Ponieważ Resig i zespół postępowali zgodnie z tym procesem, dopóki nie natknęli się na zasadę syntaktyczną, która niesamowicie zmniejszyła ilość kodu JS potrzebnego do pracy z domem i zdarzeniami. Ta zasada składniowa nie była dla nich ani nikogo jasna, kiedy zaczęli. ZNALEZIŁO to.
Jako programista, to jest twoje najwyższe powołanie. Po omacku szukacie różnych rzeczy, dopóki ich nie znajdziecie. Kiedy to zrobisz, będziesz wiedział. I zapewnisz swoim użytkownikom ogromny skok wydajności. I o to właśnie chodzi w projektowaniu (w dziedzinie oprogramowania).
źródło
Druga opcja jest lepsza, ponieważ jest łatwiejsza w użyciu dla ludzi (nawet jeśli to tylko ty), znacznie prostsza.
Do testów jednostkowych po prostu przetestuję interfejs, a nie elementy wewnętrzne, jeśli naprawdę chcesz przenieść elementy wewnętrzne z prywatnego na chronione.
źródło
1. Statyczny vs. instancja
Myślę, że istnieją bardzo jasne wytyczne dotyczące tego, co jest dobrym projektem OO, a co nie. Problem polega na tym, że blogosfera utrudnia oddzielenie dobra od zła i brzydoty. Możesz znaleźć jakieś referencje wspierające nawet najgorsze praktyki, jakie możesz wymyślić.
A najgorszą praktyką, jaką mogę wymyślić, jest stan globalny, w tym wspomniana statystyka i ulubiony Singleton każdego. Niektóre fragmenty klasycznego artykułu Misko Hevery na ten temat .
Sprowadza się to do tego, że nie powinieneś dostarczać statycznych odniesień do niczego, co ma jakiś stan przechowywania. Jedynym miejscem, w którym używam statyki, są wyliczone stałe i mam wątpliwości co do tego.
2. Metody z parametrami wejściowymi i zwracanymi wartościami a metody bez żadnych
Należy zdać sobie sprawę z tego, że metody, które nie mają parametrów wejściowych i parametrów wyjściowych, są w zasadzie pewne, że działają na jakimś wewnętrznie przechowywanym stanie (w przeciwnym razie, co robią?). Istnieją całe języki oparte na unikaniu stanu przechowywanego.
Za każdym razem, gdy zapisujesz stan, masz możliwość wystąpienia działań niepożądanych, więc upewnij się, że zawsze używasz go ostrożnie. Oznacza to, że powinieneś preferować funkcje ze zdefiniowanymi wejściami i / lub wyjściami.
W rzeczywistości funkcje, które mają zdefiniowane wejścia i wyjścia, są znacznie łatwiejsze do przetestowania - nie musisz tutaj uruchamiać funkcji i patrzeć tam, aby zobaczyć, co się stało, i nie musisz gdzieś ustawiać właściwości jeszcze przed uruchomieniem testowanej funkcji.
Możesz również bezpiecznie używać tego typu funkcji jako statyki. Jednak nie zrobiłbym tego, ponieważ gdybym później chciał gdzieś użyć nieco innej implementacji tej funkcji, zamiast zapewnić inną instancję z nową implementacją, utknąłem bez możliwości zastąpienia tej funkcji.
3. Pokrywające się kontra odrębne
Nie rozumiem pytania. Jaka byłaby korzyść z 2 nakładających się metod?
4. Prywatny vs. publiczny
Nie narażaj niczego, czego nie musisz ujawniać. Jednak nie jestem też wielkim fanem prywatnych. Nie jestem programistą C #, ale programistą ActionScript. Spędziłem dużo czasu w kodzie Flex Framework firmy Adobe, który został napisany około 2007 roku. I dokonali naprawdę złych wyborów, co zrobić, aby stać się prywatnym, co czyni z tego koszmaru próbę rozszerzenia swoich klas.
Więc jeśli nie uważasz, że jesteś lepszym architektem niż programiści Adobe około 2007 r. (Z twojego pytania powiedziałbym, że masz jeszcze kilka lat, zanim będziesz mieć szansę na złożenie takiego oświadczenia), prawdopodobnie chcesz po prostu wybrać domyślną ochronę .
Występują pewne problemy z przykładami kodu, co oznacza, że nie są one dobrze zaprojektowane, więc nie można wybrać A lub B.
Po pierwsze, prawdopodobnie powinieneś oddzielić tworzenie obiektu od jego użycia . Więc zwykle nie masz
new XMLReader()
prawa obok miejsca, w którym jest używany.Ponadto, jak mówi @djna, powinieneś obudować metody używane w czytnikach XML, aby interfejs API (przykład instancji) mógł zostać uproszczony w celu:
Nie wiem, jak działa C #, ale ponieważ pracowałem z wieloma technologiami internetowymi, podejrzewałbym, że nie zawsze będziesz w stanie natychmiast zwrócić dokument XML (z wyjątkiem może jako obietnicy lub przyszłego typu) obiekt), ale nie mogę udzielić porady na temat obsługi obciążenia asynchronicznego w języku C #.
Zauważ, że dzięki temu podejściu możesz stworzyć kilka implementacji, które mogą przyjąć parametr, który mówi im, gdzie / co mają odczytać i zwrócić obiekt XML, i zamieniać je w zależności od potrzeb projektu. Na przykład możesz czytać bezpośrednio z bazy danych, ze sklepu lokalnego lub, jak w oryginalnym przykładzie, z adresu URL. Nie możesz tego zrobić, jeśli używasz metody statycznej.
źródło
Skoncentruj się na perspektywie klienta.
Metody statyczne zwykle ograniczają przyszłą ewolucję, nasz klient pracuje w zakresie interfejsu zapewnionego przez możliwie wiele różnych implementacji.
Główny problem z twoim idiomem open / read polega na tym, że klient musi znać kolejność wywoływania metod, gdy chce tylko wykonać proste zadanie. Oczywiste tutaj, ale w większej klasie nie jest to oczywiste.
Podstawową metodą testu jest read (). Metody wewnętrzne mogą być widoczne dla programów testowych, nie czyniąc ich publicznymi ani prywatnymi i umieszczając testy w tym samym pakiecie - testy można nadal przechowywać oddzielnie od wydanego kodu.
źródło
W praktyce okaże się, że metody statyczne ograniczają się do klas użyteczności i nie powinny zagracać obiektów domeny, menedżerów, kontrolerów lub DAO. Metody statyczne są najbardziej przydatne, gdy wszystkie niezbędne odniesienia można racjonalnie przekazać jako parametry i uzyskać pewną funkcjonalność, którą można ponownie wykorzystać w wielu klasach. Jeśli okaże się, że używasz metody statycznej jako obejścia posiadania odwołania do obiektu instancji, zastanów się, dlaczego zamiast tego nie masz tego odniesienia.
Jeśli nie potrzebujesz parametrów metody, nie dodawaj ich. To samo dotyczy wartości zwracanej. Mając to na uwadze, uprościsz kod i upewnisz się, że nie kodujesz wielu scenariuszy, które nigdy się nie zdarzają.
Dobrym pomysłem jest unikanie nakładających się funkcji. Czasami może to być trudne, ale gdy konieczna jest zmiana logiki, o wiele łatwiej jest zmienić jedną metodę, która jest ponownie używana, niż zmienić całą masę metod o podobnej funkcjonalności
Często pobierające, ustawiające i konstruktory powinny być publiczne. Wszystko, co chcesz spróbować zachować prywatność, chyba że istnieje przypadek, w którym inna klasa musi to wykonać. Ustawienie domyślnych metod na prywatne pomoże utrzymać enkapsulację . To samo dotyczy pól, przyzwyczaj się do prywatnych jako domyślnych
źródło
Nie odpowiem na twoje pytanie, ale myślę, że problem wynika z użytych terminów. Na przykład
Myślę, że potrzebujesz XML, więc utworzę obiekt XML, który można utworzyć z typu tekstowego (nie znam C # ... w Javie nazywa się String). Na przykład
Możesz to przetestować i jest jasne. Następnie, jeśli potrzebujesz fabryki, możesz dodać metodę fabryczną (i może być statyczna, jak w twoim drugim przykładzie). Na przykład
źródło
Możesz przestrzegać kilku prostych zasad. Pomaga to w zrozumieniu przyczyn reguł.
metoda statyczna vs instancja
W przypadku metod nie musisz świadomie podejmować tej decyzji. Jeśli wydaje się, że twoja metoda nie używa żadnych elementów pola (twój ulubiony analizator powinien ci to powiedzieć), powinieneś dodać słowo kluczowe static.
metoda bez parametrów lub zwracanej wartości vs metoda z parametrami i zwracaną wartością
Druga opcja jest lepsza ze względu na zakres. Zawsze powinieneś trzymać swój celownik mocno. Dotarcie do potrzebnych rzeczy jest złe, powinieneś mieć wkład, pracować nad tym lokalnie i zwrócić wynik. Twoja pierwsza opcja jest zła z tego samego powodu, dla którego zmienne globalne są zwykle złe: mają one znaczenie tylko dla części twojego kodu, ale są widoczne (a więc i szum) wszędzie indziej i mogą być modyfikowane z dowolnego miejsca. Utrudnia to uzyskanie pełnego obrazu logiki.
nakładanie się i odrębna funkcjonalność metody
Nie sądzę, żeby to był problem. Metody wywołujące inne metody są w porządku, jeśli dzieli to zadania na mniejsze, wyraźnie funkcjonalne części.
metoda prywatna a publiczna
Ustaw wszystko jako prywatne, chyba że potrzebujesz, aby było publiczne. Użytkownik twojej klasy może obejść się bez hałasu, chce zobaczyć tylko to, co jest dla niego ważne.
źródło