Znaczna część czasu, nie mogę wymyślić powodu, aby mieć obiekt zamiast klasy statycznej. Czy przedmioty mają więcej zalet niż myślę? [Zamknięte]

9

Rozumiem pojęcie obiektu i jako programista Java czuję, że paradygmat OO przychodzi mi w praktyce dość naturalnie.

Jednak ostatnio pomyślałem:

Poczekaj chwilę, jakie są praktyczne korzyści z używania obiektu w porównaniu z użyciem klasy statycznej (z odpowiednimi praktykami enkapsulacji i OO)?

Mógłbym wymyślić dwie zalety używania obiektu (oba są znaczące i potężne):

  1. Polimorfizm: pozwala dynamicznie i elastycznie zamieniać funkcjonalność w czasie wykonywania. Pozwala również łatwo dodawać nowe funkcje „części” i alternatywy do systemu. Na przykład, jeśli istnieje Carklasa zaprojektowana do pracy z Engineobiektami i chcesz dodać nowy silnik do systemu, z którego może korzystać samochód, możesz utworzyć nową Enginepodklasę i po prostu przekazać obiekt tej klasy do Carobiektu, bez konieczności zmienić cokolwiek na temat Car. Możesz to zrobić podczas działania.

  2. Będąc w stanie „przekazywać funkcjonalność”: możesz dynamicznie przekazywać obiekt wokół systemu.

Ale czy są jakieś przewagi obiektów nad klasami statycznymi?

Często, gdy dodam nowe „części” do systemu, robię to, tworząc nową klasę i tworząc z niej obiekty.

Ale ostatnio, kiedy zatrzymałem się i pomyślałem o tym, zdałem sobie sprawę, że klasa statyczna zrobiłaby to samo co obiekt, w wielu miejscach, w których normalnie używam obiektu.

Na przykład pracuję nad dodaniem mechanizmu zapisywania / ładowania plików do mojej aplikacji.

W przypadku obiektu linia wywołująca kodu będzie wyglądać następująco: Thing thing = fileLoader.load(file);

W przypadku klasy statycznej wyglądałoby to tak: Thing thing = FileLoader.load(file);

Co za różnica?

Dość często nie potrafię wymyślić żadnego powodu, aby utworzyć obiekt, gdy zwykła klasa statyczna działałaby tak samo. Ale w systemach OO klasy statyczne są dość rzadkie. Więc coś mi brakuje.

Czy są jeszcze jakieś zalety innych obiektów niż te, które wymieniłem? Proszę wytłumacz.

EDYCJA: aby wyjaśnić. Uważam, że obiekty są bardzo przydatne podczas wymiany funkcji lub przekazywania danych. Na przykład napisałem aplikację, która tworzy melodie. MelodyGeneratormiał kilka podklas, które inaczej tworzą melodie, a obiekty tych klas były wymienne (wzorzec strategii).

Melodie też były przedmiotami, ponieważ warto je przekazywać. Podobnie jak Akordy i Łuski.

Ale co z „statycznymi” częściami systemu - które nie zostaną przekazane? Na przykład - mechanizm „zapisz plik”. Dlaczego powinienem zaimplementować go w obiekcie, a nie w klasie statycznej?

Aviv Cohn
źródło
Więc zamiast obiektu z polami i być może metodami, żadnych obiektów, żadnych rekordów, tylko wiele wartości skalarnych przekazywanych i zwracanych z metod statycznych? Edycja: Twój nowy przykład sugeruje inaczej: Obiekty, tylko bez metod instancji? W przeciwnym razie co Thing?
Co się stanie, gdy będziesz musiał zamienić swój FileLoaderna czytający z gniazda? A może próbny test? Lub taki, który otwiera plik zip?
Benjamin Hodgson
1
@delnan Okay Wymyśliłem pytanie, na które odpowiedź pomoże mi zrozumieć: dlaczego miałbyś implementować „statyczną” część systemu jako obiekt? Podobnie jak w przykładzie w pytaniu: mechanizm zapisu pliku. Co zyskuję na implementacji w obiekcie?
Aviv Cohn
1
Domyślnie należy użyć obiektu niestatycznego. Używaj tylko klas statycznych, jeśli uważasz, że to naprawdę lepiej wyraża twoją intencję. System.Mathw .NET jest przykładem czegoś, co ma o wiele większy sens jako klasa statyczna: nigdy nie będziesz musiał go wymieniać ani wyśmiewać i żadna z operacji nie mogłaby logicznie stać się częścią instancji. Naprawdę nie sądzę, że twój przykład „oszczędzania” pasuje do tego rachunku.
Benjamin Hodgson,

Odpowiedzi:

14

lol brzmisz jak zespół, nad którym kiedyś pracowałem;)

Java (i prawdopodobnie C #) z pewnością obsługuje ten styl programowania. Pracuję z ludźmi, których pierwszym instynktem jest: „Mogę zrobić to metodą statyczną!” Ale z czasem pojawiają się pewne subtelne koszty.

1) Java jest językiem obiektowym. I to doprowadza funkcjonalnych facetów do szaleństwa, ale tak naprawdę dobrze sobie radzi. Ideą OO jest połączenie funkcjonalności ze stanem w celu uzyskania małych jednostek danych i funkcjonalności, które zachowują ich semantykę poprzez ukrywanie stanu i ujawnianie tylko funkcji, które mają sens w tym kontekście.

Przechodząc do klasy tylko metodami statycznymi, łamiesz część „stanu” równania. Ale państwo wciąż musi gdzieś mieszkać. Z czasem widziałem, że klasa ze wszystkimi metodami statycznymi zaczyna mieć coraz bardziej skomplikowane listy parametrów, ponieważ stan przechodzi z klasy do wywołań funkcji.

Po utworzeniu klasy ze wszystkimi metodami statycznymi, przejrzyj i po prostu sprawdź, ile z tych metod ma jeden wspólny parametr. Jest wskazówką, że ten parametr powinien być albo klasą zawierającą te funkcje, albo parametr ten powinien być atrybutem instancji.

2) Zasady OO są dość dobrze zrozumiane. Po chwili możesz spojrzeć na projekt klasy i sprawdzić, czy spełnia on kryteria takie jak SOLID. Po wielu testach jednostek ćwiczeniowych dobrze rozumiesz, co sprawia, że ​​klasa jest „odpowiedniej wielkości” i „spójna”. Ale nie ma dobrych reguł dla klasy ze wszystkimi metodami statycznymi i nie ma żadnego prawdziwego powodu, dla którego nie należy po prostu pakować w nią wszystkiego. Klasa jest otwarta w twoim edytorze, więc do cholery? Po prostu dodaj tam swoją nową metodę. Po pewnym czasie twoja aplikacja zamienia się w szereg konkurujących „Boskich obiektów”, z których każdy próbuje zdominować świat. Ponownie, przekształcenie ich w mniejsze jednostki jest wysoce subiektywne i trudno stwierdzić, czy masz rację.

3) Interfejsy są jedną z najpotężniejszych funkcji Java. Dziedziczenie klas okazało się problematyczne, ale programowanie za pomocą interfejsów pozostaje jedną z najpotężniejszych sztuczek języka. (ditto C #) Klasy całkowicie statyczne nie mogą wejść w ten model.

4) Zatrzaskuje drzwi przed ważnymi technikami OO, z których nie możesz skorzystać. Możesz więc pracować przez lata z młotkiem w skrzynce z narzędziami, nie zdając sobie sprawy, o ile łatwiej byłoby, gdybyś miał także śrubokręt.

4.5) Tworzy najtrudniejsze, najbardziej niezniszczalne zależności czas kompilacji. Tak więc, na przykład, jeśli masz, FileSystem.saveFile()nie ma możliwości, aby to zmienić, poza udawaniem JVM w czasie wykonywania. Co oznacza, że ​​każda klasa, która odwołuje się do twojej klasy funkcji statycznej, ma twardą zależność od czasu kompilacji od tej konkretnej implementacji, co sprawia, że ​​rozszerzenie jest prawie niemożliwe i ogromnie komplikuje testowanie. Możesz przetestować klasę statyczną w izolacji, ale bardzo trudno jest przetestować klasy, które odnoszą się do tej klasy w izolacji.

5) Doprowadzisz swoich pracowników do szaleństwa. Większość profesjonalistów, z którymi współpracuję, poważnie podchodzi do swojego kodu i zwraca uwagę na co najmniej pewien poziom zasad projektowania. Odkładając na bok rdzeń języka, zmuszą go do wyciągania włosów, ponieważ będą ciągle poprawiać kod.

Kiedy jestem w języku, zawsze staram się dobrze go używać. Na przykład, kiedy jestem w Javie, używam dobrego projektu OO, ponieważ wtedy naprawdę wykorzystuję język do tego, co robi. Kiedy jestem w Pythonie, mieszam funkcje na poziomie modułu z klasami okazjonalnymi - mogłem pisać klasy tylko w Pythonie, ale myślę, że nie użyłbym języka do tego, w czym jest dobry.

Inną taktyką jest złe używanie języka, a oni narzekają na wszystkie problemy, które powoduje. Ale dotyczy to praktycznie każdej technologii.

Kluczową funkcją Java jest zarządzanie złożonością w małych, testowalnych jednostkach, które łączą się ze sobą, dzięki czemu są łatwe do zrozumienia. Java kładzie nacisk na jasne definicje interfejsu niezależnie od implementacji - co jest ogromną korzyścią. Dlatego właśnie (i inne podobne języki OO) są tak szeroko stosowane. Mimo całej gadatliwości i rytualizmu, kiedy skończę z dużą aplikacją Java, zawsze mam wrażenie, że pomysły są bardziej przejrzyste w kodzie niż moje projekty w bardziej dynamicznym języku.

Ale to trudne. Widziałem, jak ludzie dostają błąd „całkowicie statyczny”, i ciężko jest im z tego zrezygnować. Ale widziałem, jak odczuwają wielką ulgę, gdy się z tym pogodzą.

Obrabować
źródło
Mówisz głównie o utrzymywaniu małych elementów i modułowości oraz o utrzymaniu stanu i funkcjonalności razem. Nic nie powstrzymuje mnie przed zrobieniem tego za pomocą klas statycznych. Mogą mieć stan. I możesz je zamknąć za pomocą getters-setters. I możesz podzielić system na małe klasy, które zawierają funkcjonalność i stan. Wszystko to dotyczy zarówno klas, jak i obiektów. I to jest właśnie mój dylemat: powiedz, że dodałem nową funkcjonalność do mojej aplikacji. Oczywiście pójdzie w osobnej klasie, aby być posłusznym SRP. Ale dlaczego dokładnie miałbym tworzyć tę klasę? Tego nie rozumiem.
Aviv Cohn
Mam na myśli: czasami jasne jest, dlaczego chcę przedmiotów. Wykorzystam przykład z edycji do mojego pytania: miałem aplikację, która tworzy melodie. Były różne Skale, które mogłem podłączyć do MelodyGenerators, aby generować różne melodie. Melodie były obiektami, więc mogłem je ułożyć w stos i wrócić do gry na stare, itp. W takich przypadkach jasne jest dla mnie, dlaczego powinienem używać przedmiotów. Nie rozumiem, dlaczego powinienem używać obiektów do reprezentowania „statycznych” części systemu: np. Mechanizmu zapisywania plików. Dlaczego nie powinna to być tylko klasa statyczna?
Aviv Cohn
Klasa z metodami statycznymi musi uzewnętrznić swój stan. Co czasami jest bardzo ważną techniką faktoringową. Ale przez większość czasu chcesz obudować stan. Konkretny przykład: lata temu współpracownik napisał „Javascript picker” w Javascript. I to był koszmar. Klienci ciągle na to narzekali. Przekształciłem go więc w obiekty „kalendarzowe” i nagle utworzyłem wiele z nich, umieszczając je obok siebie, skacząc między miesiącami i latami itp. Było tak bogate, że musieliśmy wyłączyć funkcje. Tworzenie instancji daje taką skalę.
Rob
3
„Ideą OO jest połączenie funkcjonalności ze stanem w celu posiadania małych jednostek danych i funkcjonalności, które zachowują ich semantykę poprzez ukrywanie stanu i ujawnianie tylko funkcji, które mają sens w tym kontekście”. To jest zasadniczo błędne. OO nie oznacza stanu zmiennego, a abstrakcja nie dotyczy wyłącznie OO. Istnieją dwa sposoby uzyskania abstrakcji, a obiekty są tylko jednym z nich (drugim są abstrakcyjne typy danych).
Doval
1
@Doval Nie rozumiem, dlaczego każda dyskusja na temat OO musi koniecznie przerodzić się w dyskusję na temat programowania funkcjonalnego.
Rob
6

Zapytałeś:

Ale czy są jakieś przewagi obiektów nad klasami statycznymi?

Przed tym pytaniem wymieniono polimorfizm i przekazano go jako dwie zalety korzystania z obiektów. Chcę powiedzieć, że takie są cechy paradygmatu OO. Enkapsulacja to kolejna cecha paradygmatu OO.

Nie są to jednak korzyści. Korzyści to:

  1. Lepsze abstrakcje
  2. Lepsza konserwacja
  3. Lepsza testowalność

Powiedziałeś:

Ale ostatnio, kiedy zatrzymałem się i pomyślałem o tym, zdałem sobie sprawę, że klasa statyczna zrobiłaby to samo co obiekt, w wielu miejscach, w których normalnie używam obiektu.

Myślę, że masz rację. U podstaw programowania leży wyłącznie transformacja danych i tworzenie efektów ubocznych opartych na danych. Czasami przekształcanie danych wymaga danych pomocniczych. Innym razem tak nie jest.

Gdy mamy do czynienia z pierwszą kategorią transformacji, dane pomocnicze należy albo przekazać jako dane wejściowe, albo gdzieś przechowywać. Obiekt to lepsze podejście niż klasy statyczne dla takich przekształceń. Obiekt może przechowywać dane pomocnicze i wykorzystywać je we właściwym czasie.

W przypadku drugiej kategorii transformacji klasa statyczna jest tak dobra jak obiekt, jeśli nie lepsza. Funkcje matematyczne są klasycznymi przykładami tej kategorii. Większość standardowych funkcji biblioteki C również należy do tej kategorii.

Zapytałeś:

W przypadku obiektu linia wywołująca kodu będzie wyglądać następująco: Thing thing = fileLoader.load(file);

W przypadku klasy statycznej wyglądałoby to tak: Thing thing = FileLoader.load(file);

Co za różnica?

Jeśli FileLoadernie trzeba nigdy przechowywać żadnych danych, wybrałbym drugie podejście. Jeśli istnieje niewielka szansa, że ​​do wykonania operacji będą potrzebne dane pomocnicze, pierwsze podejście jest bezpieczniejsze.

Zapytałeś:

Czy są jeszcze jakieś zalety innych obiektów niż te, które wymieniłem? Proszę wytłumacz.

Wymieniłem zalety (zalety) stosowania paradygmatu OO. Mam nadzieję, że są zrozumiałe. Jeśli nie, chętnie to rozwinę.

Zapytałeś:

Ale co z „statycznymi” częściami systemu - które nie zostaną przekazane? Na przykład - mechanizm „zapisz plik”. Dlaczego powinienem zaimplementować go w obiekcie, a nie w klasie statycznej?

Jest to przykład, w którym klasa statyczna po prostu nie działa. Istnieje wiele sposobów zapisywania danych aplikacji w pliku:

  1. Zapisz go w pliku CSV.
  2. Zapisz go w pliku XML.
  3. Zapisz go w pliku json.
  4. Zapisz go jako plik binarny z bezpośrednim zrzutem danych.
  5. Zapisz go bezpośrednio w tabeli w bazie danych.

Jedynym sposobem na zapewnienie takich opcji jest utworzenie interfejsu i obiektów, które implementują interfejs.

Podsumowując

Zastosuj odpowiednie podejście do danego problemu. Lepiej nie być religijnym w stosunku do jednego podejścia do drugiego.

R Sahu
źródło
Ponadto, jeśli wiele różnych typów obiektów (klas) muszą być zapisane (np Melody, Chord, Improvisations, SoundSamplei inne), to będzie ekonomiczny uprościć wdrażanie poprzez abstrakcje.
rwong
5

Czasami zależy to od języka i kontekstu. Na przykład PHPskrypty używane do obsługi żądań zawsze mają jedno żądanie obsługi i jedną odpowiedź do wygenerowania przez cały okres istnienia skryptu, więc statyczne metody działania na żądanie i wygenerowania odpowiedzi mogą być odpowiednie. Ale na serwerze, na którym napisano Node.js, może być wiele różnych żądań i odpowiedzi jednocześnie. Pierwsze pytanie brzmi: czy jesteś pewien, że klasa statyczna naprawdę odpowiada obiektowi singletonowemu?

Po drugie, nawet jeśli masz singletony, używanie obiektów pozwala wykorzystać polimorfizm przy użyciu technik takich jak Dependency_injection i Factory_method_pattern . Jest to często używane w różnych wzorcach Inversion_of_control i przydatne do tworzenia próbnych obiektów do testowania, rejestrowania itp.

Wspomniałeś o zaletach powyżej, więc oto jedna, której nie zrobiłeś: dziedziczenie. Wiele języków nie ma możliwości zastąpienia metod statycznych w taki sam sposób, w jaki można zastąpić metody instancji. Ogólnie rzecz biorąc, znacznie trudniej jest dziedziczyć i zastępować metody statyczne.

Gregory Magarshak
źródło
Dzięki za odpowiedź. Twoim zdaniem: podczas tworzenia „statycznej” części systemu coś, co nie będzie „przekazywane” - na przykład część systemu, która odtwarza dźwięki, lub część systemu, która zapisuje dane do pliku: czy powinienem zaimplementować go w obiekcie czy w klasie statycznej?
Aviv Cohn
3

Oprócz postu Roba Y.


Tak długo, jak funkcjonalność twojego load(File file)może być wyraźnie oddzielona od wszystkich innych funkcji, których używasz, możesz używać statycznej metody / klasy. Eksternalizujesz stan (co nie jest złe), a także nie dostajesz redundancji, ponieważ możesz na przykład częściowo zastosować lub curry swoją funkcję, abyś nie musiał się powtarzać. (to w rzeczywistości jest takie samo lub podobne do używania factorywzorca)

Jednak gdy tylko dwie z tych funkcji zaczną mieć wspólne zastosowanie, chcesz mieć możliwość ich grupowania. Wyobraź sobie, że masz nie tylko loadfunkcję, ale także hasValidSyntaxfunkcję. Co zrobisz?

     if (FileLoader.hasValidSyntax(myfile))
          Thing thing = FileLoader.load(myfile);
     else
          println "oh noes!"

Widzisz dwa odniesienia myfiletutaj? Zaczynasz się powtarzać, ponieważ twój stan zewnętrzny musi być przekazywany dla każdego połączenia. Rob Y opisał sposób internalizacji stanu (tutaj file), abyś mógł to zrobić w następujący sposób:

     FileLoader myfileLoader = new FileLoader(myfile)
     if (myfileLoader.hasValidSyntax())
          Thing thing = myfileLoader.load();
     else
          println "oh noes!"
walenteria
źródło
Konieczność dwukrotnego przejścia do tego samego obiektu jest powierzchownym objawem; faktyczny problem, jaki może to wskazywać, polega na tym, że niektóre prace są wykonywane nadmiarowo - plik może wymagać dwukrotnego otwarcia, parsowania dwukrotnie i dwukrotnego zamknięcia, jeśli hasValidSyntax()i load()nie może ponownie użyć stanu (wynik częściowo przeanalizowanego pliku).
rwong
2

Głównym problemem podczas korzystania z klas statycznych jest to, że zmuszają cię do ukrycia zależności i zmuszają cię do uzależnienia od implementacji. W poniższej sygnaturze konstruktora, jakie są twoje zależności:

public Person(String name)

Sądząc po podpisie, osoba potrzebuje tylko imienia, prawda? Cóż, nie jeśli implementacja jest:

public Person(String name) {
    ResultSet rs = DBConnection.getPersonFilePathByName(name);
    File f = FileLoader.load(rs.getPath());
    PersonData.setDataFile(f);
    this.name = name;
    this.age = PersonData.getAge();
}

Więc osoba nie jest tylko instancją. W rzeczywistości pobieramy dane z bazy danych, co daje nam ścieżkę do pliku, który następnie musi zostać przeanalizowany, a następnie rzeczywiste dane, które chcemy, muszą zostać usunięte. Ten przykład jest wyraźnie przesadzony, ale dowodzi tego. Ale czekaj, może być o wiele więcej! Piszę następujący test:

public void testPersonConstructor() {
    Person p = new Person("Milhouse van Houten");
    assertEqual(10, p.age);
}

To powinno jednak minąć, prawda? Mam na myśli, że zamknęliśmy wszystkie inne rzeczy, prawda? Właściwie to wysadza w powietrze wyjątek. Dlaczego? Och, tak, ukryte zależności, o których nie wiedzieliśmy, mają stan globalny. Do DBConnectionpotrzeb zainicjalizowany i połączone. Do FileLoaderpotrzeb inicjalizowane na FileFormatprzedmiot (np XMLFileFormatlub CSVFileFormat). Nigdy o nich nie słyszałeś? O to właśnie chodzi. Twój kod (i kompilator) nie może ci powiedzieć, że potrzebujesz tych rzeczy, ponieważ wywołania statyczne ukrywają te zależności. Czy powiedziałem test? Miałem na myśli, że nowy młodszy programista właśnie dostarczył coś takiego w swoim najnowszym wydaniu. W końcu skompilowane = działa, prawda?

Ponadto powiedz, że korzystasz z systemu, na którym nie działa instancja MySQL. Lub powiedzmy, że korzystasz z systemu Windows, ale twoja DBConnectionklasa wychodzi tylko na serwer MySQL w systemie Linux (ze ścieżkami systemu Linux). Lub powiedzmy, że jesteś w systemie, w którym ścieżka zwrócona przez DBConnectionnie jest dla ciebie do odczytu / zapisu. Oznacza to, że próba uruchomienia lub przetestowania tego systemu w którymkolwiek z tych warunków zakończy się niepowodzeniem, nie z powodu błędu kodu, ale z powodu błędu projektu, który ogranicza elastyczność kodu i wiąże cię z implementacją.

Powiedzmy, że chcemy rejestrować każde wywołanie bazy danych dla jednej konkretnej Personinstancji, która przechodzi pewną, kłopotliwą ścieżkę. Moglibyśmy DBConnectionlogować się , ale to wszystko rejestruje , wprowadzając dużo bałaganu i utrudniając rozróżnienie konkretnej ścieżki kodu, którą chcemy śledzić. Jeśli jednak używamy wstrzykiwania zależności z DBConnectioninstancją, moglibyśmy po prostu zaimplementować interfejs w dekoratorze (lub rozszerzyć klasę, ponieważ mamy obie opcje dostępne dla obiektu). Dzięki klasie statycznej nie możemy wstrzyknąć zależności, nie możemy zaimplementować interfejsu, nie możemy zawinąć go w dekorator i nie możemy rozszerzyć klasy. Możemy to nazwać tylko bezpośrednio, gdzieś głęboko ukryte w naszym kodzie. W ten sposób jesteśmy zmuszeni mieć ukrytą zależność od implementacji.

Czy to zawsze jest złe? Niekoniecznie, ale może być lepiej odwrócić swój punkt widzenia i powiedzieć: „Czy istnieje dobry powód, aby to nie był przypadek?” zamiast „Czy jest dobry powód, aby to była instancja?” Jeśli naprawdę możesz powiedzieć, że Twój kod będzie równie niezachwiany (i bezstanowy), jak Math.abs()zarówno pod względem jego implementacji, jak i sposobu, w jaki zostanie użyty, możesz rozważyć ustawienie go jako statycznego. Posiadanie instancji w stosunku do klas statycznych daje jednak świat elastyczności, który nie zawsze jest łatwy do odzyskania po fakcie. Może także dać ci większą jasność co do prawdziwej natury zależności twojego kodu.

Cbojar
źródło
Ukryte zależności są bardzo dobrym punktem. Prawie zapomniałem swojego idiomu, że „jakość kodu należy oceniać na podstawie tego, jak jest on używany, a nie jak jest on implementowany”.
Euforyczny
Ale ukryte zależności mogą istnieć również w klasie niestatycznej, prawda? Nie rozumiem więc, dlaczego miałoby to być wadą klas statycznych, ale nie klas niestatycznych.
valenterry
@valenterry Tak, możemy ukryć również zależności niestatyczne, ale najważniejsze jest to, że ukrywanie zależności niestatycznych jest wyborem . Mógłbym newutworzyć kilka obiektów w konstruktorze (co nie jest ogólnie wskazane), ale mogę je również wstrzyknąć. W przypadku klas statycznych nie ma możliwości ich wstrzyknięcia (zresztą i tak nie jest to łatwe w Javie, i to byłaby zupełnie inna dyskusja dla języków, które na to pozwalają), więc nie ma wyboru. Dlatego w mojej odpowiedzi podkreślam pomysł bycia zmuszonym do ukrywania zależności tak mocno.
cbojar
Ale możesz je wstrzykiwać za każdym razem, gdy wywołujesz metodę, podając je jako argumenty. Więc nie musisz ich ukrywać, ale musisz wyrazić je wyraźnie, aby wszyscy widzieli „aha, ta metoda zależy od tych argumentów”, a nie „aha, ta metoda zależy od tych (części) argumentów i jakiegoś ukrytego stanu interalu Nie wiem wiedzieć".
valenterry
W Javie, o ile mi wiadomo, nie można podać klasy statycznej jako parametru bez przejścia przez cały szereg refleksji. (Próbowałem wszystkiego, o czym myślę. Jeśli masz jakiś sposób, chciałbym go zobaczyć.) A jeśli zdecydujesz się to zrobić, zasadniczo albo niszczysz wbudowany system typów (parametr to Class, wtedy upewniamy się, że jest to odpowiednia klasa) lub piszemy na klawiaturze (upewniamy się, że odpowiada na właściwą metodę). Jeśli chcesz to zrobić, powinieneś ponownie ocenić swój wybór języka. Jeśli chcesz zrobić to pierwsze, instancje są w porządku i są wbudowane.
cbojar
1

Tylko moje dwa centy.

Na twoje pytanie:

Ale co z „statycznymi” częściami systemu - które nie zostaną przekazane? Na przykład - mechanizm „zapisz plik”. Dlaczego powinienem zaimplementować go w obiekcie, a nie w klasie statycznej?

Możesz zadać sobie pytanie, czy mechanizm części systemu, która odtwarza dźwięki, czy części systemu, która zapisuje dane do pliku, ZMIENI SIĘ . Jeśli Twoja odpowiedź brzmi „tak”, oznacza to, że powinieneś je streścić za pomocą abstract class/ interface. Możesz zapytać, skąd mogę wiedzieć o przyszłości? Zdecydowanie nie możemy. Tak więc, jeśli rzecz jest bezstanowa, możesz użyć „klasy statycznej”, na przykład w java.lang.Mathprzypadku podejścia obiektowego.

użytkownik3663635
źródło
Jest jedna rada: „ Trzy uderzenia i refaktoryzacja” , co sugeruje, że można się powstrzymać, dopóki zmiana nie będzie rzeczywiście potrzebna. Przez „zmianę” rozumiem: należy zapisać więcej niż jeden typ obiektu; lub musisz zapisać w więcej niż jednym formacie pliku (typ pamięci); lub drastyczny wzrost złożoności zapisanych danych. Oczywiście, jeśli jest się całkiem pewnym, że zmiana nastąpi wkrótce, można z góry zastosować bardziej elastyczny projekt.
rwong