Mam wartość, której potrzebuje wiele obiektów. Na przykład aplikacja finansowa z różnymi inwestycjami jako przedmiotami i większość z nich potrzebuje bieżącej stopy procentowej.
Miałem nadzieję, że moje otoczenie finansowe zostanie zamknięte jako przedmiot, a stopa procentowa jako własność. Ale obiekty rodzeństwa, które potrzebują tej wartości, nie mogą do niej dotrzeć.
Jak więc współdzielić wartości między wieloma obiektami bez nadmiernego łączenia mojego projektu? Oczywiście myślę o tym źle.
object-oriented
Greg
źródło
źródło
update
funkcji, która jest wywoływana za każdym razem? Czy możesz opublikować w pseudokodzie sposób działania symulacji?Singleton
jest globalny z cukrem syntaktycznym OO i jest okropnym rozwiązaniem, które ściśle łączy Twój kod na kilka najgorszych możliwych sposobów. Przeczytaj ten artykuł w kółko, dopóki go nie zrozumiesz!DateTime
jako dane wejściowe i zwraca liczbę jako dane wyjściowe.Odpowiedzi:
To zapach designu. Rzadko zdarza się, że wiele przedmiotów musi o czymś wiedzieć. To powiedziawszy, obecna stopa procentowa jest dość dobrym przykładem wyjątkowych okoliczności. Jedna rzecz się martwić o to, że jest rzadko oprocentowanie. Różne instrumenty finansowe stosują różne stawki. Co najmniej różne lokalizacje używają różnych „standardowych” stawek. Ponadto, aby pomóc w testowaniu i raportowaniu, zwykle chcesz przekazać stawkę, ponieważ nie chcesz tam używać bieżącej stawki. Chcesz użyć stawki „co jeśli” lub „na dzień sprawozdawczy”.
Udostępniając je, nie wszystkie mają odniesienie do jednej instancji. Przekazywanie tego samego nadal wiąże się do pewnego stopnia, ale nie do końca , ponieważ coś takiego jak bieżąca stopa procentowa jest potrzebne jako dane wejściowe do różnych obliczeń.
źródło
W tym konkretnym przypadku użyłbym Wzorca Singletona . FinancialEnvironment byłby obiektem, o którym wiedzą wszystkie inne biblioteki klas, ale zostałby utworzony przez Singleton. Idealnie byłoby wtedy wysłać ten utworzony obiekt do różnych bibliotek klas.
Na przykład:
Pozostałe klasy są powiązane tylko za pomocą biblioteki klas Entities, akceptują one instancję obiektu FinancialEnvironment. Nie obchodzi ich, jak to zostało stworzone, robi to tylko warstwa usługi, wszystko czego chcą to informacje. Singleton może być również wystarczająco inteligentny, aby przechowywać kilka obiektów FinancialEnvironment, w zależności od reguł lokalnych, jak wskazał @Telastyn.
Na marginesie, nie jestem wielkim fanem Singleton Pattern, uważam to za zapach kodu, ponieważ można go bardzo łatwo nadużyć. Ale w niektórych przypadkach jest to potrzebne.
Aktualizacja:
Jeśli absolutnie, pozytywnie musisz mieć zmienną globalną, wówczas wdrożenie Wzorca Singletona, jak opisano powyżej, działałoby. Jednak nie jestem wielkim fanem tego i na podstawie komentarzy z mojego oryginalnego postu kilka innych osób też nie jest. Jako coś tak niestabilnego jak stopa procentowa, Singleton może nie być najlepszym rozwiązaniem. Singletony działają najlepiej, gdy informacje się nie zmieniają. Na przykład użyłem Singletona w jednej z moich aplikacji do utworzenia liczników wydajności. Ponieważ jeśli się zmieniają, musisz mieć logikę do obsługi aktualizowanych danych.
Gdybym był bukmacherem, stawiałbym zakład, że stopa procentowa była przechowywana gdzieś w bazie danych lub została pobrana za pośrednictwem serwisu internetowego. W takim przypadku zalecane byłoby repozytorium (warstwa dostępu do danych) w celu pobrania tych informacji. Aby uniknąć niepotrzebnych przejazdów do bazy danych (nie jestem pewien, jak często zmieniają się stopy procentowe lub inne informacje w klasie FinancialInformation), można zastosować buforowanie. W świecie C # biblioteka Microsoft Caching Application Block działa bardzo dobrze.
Jedyną rzeczą, która zmieniłaby się w stosunku do powyższego przykładu, byłyby różne klasy w warstwie usług, które potrzebują informacji finansowych pobieranych z warstwy dostępu do danych zamiast tworzenia instancji obiektu przez singletona.
źródło
Singleton
to globalny cukier z syntezą OO i kula dla leniwych i słabych umysłów.Singleton/global
to absolutnie najgorszy sposób na ścisłe powiązanie kodu z czymś, co będzie rakiem, gdy zdasz sobie sprawę, jak kolosalnie zły był to pomysł i dlaczego wszyscy tak twierdzą!Pliki konfiguracyjne?
Jeśli masz wartości używane globalnie, umieść je w pliku konfiguracyjnym. Następnie każdy system i podsystem może się do tego odwoływać i wyciągnąć potrzebne klucze, czyniąc je tylko do odczytu.
źródło
Mówię z doświadczenia kogoś, kto ma około miesiąc prac konserwacyjnych nad dużym projektem (~ 50 000 LOC), który właśnie wydaliśmy.
Mogę powiedzieć, że prawdopodobnie tak naprawdę nie chcesz obiektu globalnego. Wprowadzenie tego rodzaju cruft daje o wiele więcej możliwości nadużyć niż nie pomaga.
Moja wstępna sugestia jest taka, że jeśli masz kilka różnych klas, które wymagają bieżącej stopy procentowej, prawdopodobnie prawdopodobnie po prostu chcesz, aby wprowadziły
IInterestRateConsumer
coś takiego. Wewnątrz tego interfejsu będziesz miećSetCurrentInterestRate(double rate)
(lub cokolwiek, co ma sens), a może po prostu właściwość.Przekazywanie stóp procentowych nie jest w rzeczywistości sprzężeniem - jeśli klasa potrzebuje stopy procentowej, jest to część jej interfejsu API. Łączą się tylko wtedy, gdy jedna z twoich klas zaczyna się martwić, w jaki sposób druga klasa używa tej stopy procentowej.
źródło
Martin Fowler ma artykuł, który krótko mówi o tym, jak przeformułować statyczny glob na coś bardziej elastycznego. Zasadniczo przekształcasz go w singleton, a następnie modyfikujesz singleton, tak aby obsługiwał przesłonięcie klasy instancji za pomocą podklasy (i w razie potrzeby przenieś logikę tworzącą instancję do osobnej klasy, którą można podklasować, co zrobiłbyś jeśli tworzenie instancji superklasy to późniejsze jej zastąpienie stanowi problem).
Oczywiście musisz rozważyć problemy związane z singletonami (nawet singletonami zastępowalnymi) w porównaniu do bólu związanego z przekazywaniem wszędzie tego samego obiektu.
Jeśli chodzi o obiekt „środowiska finansowego” - wygodnie jest programować przy pierwszym przejściu, ale kiedy skończysz, dodałeś dodatkowe zależności. Klasy, które potrzebują tylko stopy procentowej, działają teraz tylko po przejściu obiektu środowiska finansowego, co utrudni ich ponowne użycie, gdy nie będzie obiektu leżącego w pobliżu. Dlatego odradzałbym to szeroko.
źródło
Dlaczego nie umieścić danych o stopach procentowych w centralnej pamięci podręcznej?
Możesz użyć jednej z kilku bibliotek pamięci podręcznej, w zależności od tego, która najbardziej odpowiada Twoim wymaganiom, coś takiego jak memcached rozwiązuje wszystkie problemy związane z współbieżnością i zarządzaniem kodem i pozwoli na skalowanie aplikacji do wielu procesów.
Lub przejdź cały wieprz i przechowuj je w bazie danych, co pozwoli na skalowanie do wielu serwerów.
źródło
W takich sytuacjach z powodzeniem wprowadziłem (ponownie użyłem) termin „kontekstowy” z czasami wieloma warstwami.
Oznacza to singleton, a więc „globalną” składnicę obiektów, od której można żądać tego rodzaju obiektów. Kody, które ich wymagają, zawierają nagłówek sklepu i używają funkcji globalnych, aby uzyskać instancje obiektów (jak teraz dostawca stopy procentowej).
Sklep może być:
Im większy system, tym bardziej użyteczne jest to drugie rozwiązanie, dla dość małego ryzyka użycia niewłaściwego wyliczenia. Z drugiej strony, w przypadku języków, które umożliwiają deklaracje typu przesyłania dalej, myślę, że można używać wpisywanych akcesoriów bez uwzględnienia wszystkich nagłówków w sklepie.
Jeszcze jedna uwaga: możesz mieć wiele instancji tego samego typu obiektu dla różnych zastosowań, takich jak czasami inna wartość języka dla GUI i dla wydruków, dzienników globalnych i dzienników na poziomie sesji itp., Więc nazwa wyliczenia / akcesorium NIE powinna odzwierciedlać faktycznego typu , ale rola żądanej instancji (CurrentInterestRate).
W implementacji sklepu musisz zarządzać poziomami kontekstu i kolekcjami instancji kontekstu. Prostym przykładem jest usługa internetowa, w której masz kontekst globalny (jedna instancja dla wszystkich żądań dla tego obiektu - problem w przypadku farmy serwerów) oraz kontekst dla każdej sesji internetowej. Możesz także mieć konteksty dla każdego użytkownika, który może mieć wiele równoległych sesji itp. Przy wielu serwerach powinieneś używać do tego rodzaju rozproszonej pamięci podręcznej.
Po nadejściu żądania decydujesz, na jakim poziomie kontekstu znajduje się żądany obiekt, uzyskaj kontekst dla wywołania. Jeśli obiekt tam jest, odsyłasz go; jeśli nie, tworzysz go i przechowujesz na tym poziomie kontekstu, i zwracasz. Oczywiście zsynchronizuj sekcję tworzenia (i opublikuj ją w rozproszonej pamięci podręcznej). Tworzenie może być konfigurowalne jak wtyczka, najlepiej z językami umożliwiającymi tworzenie instancji obiektów według ich nazw klas (Java, Objective C, ...), ale możesz to zrobić również w C, z bibliotekami podłączanymi z funkcjami fabrycznymi.
Uwaga dodatkowa: obiekt wywołujący NIE powinien wiedzieć zbyt wiele o swoich kontekstach i poziomie kontekstu żądanego obiektu. Powody: 1: łatwo jest popełnić błąd (lub „sprytne sztuczki”), grając z tymi parametrami; 2: poziom kontekstu żądanego może się później zmienić. Przeważnie łączę informacje kontekstowe z wątkiem, więc magazyn obiektów zawiera informacje bez dodatkowych parametrów z żądania.
Z drugiej strony żądanie może zawierać wskazówkę dotyczącą instancji: na przykład uzyskanie stopy procentowej na określony dzień. Powinien to być ten sam „globalny” dostęp, ale wiele instancji w zależności od daty (i wprowadzanie różnych wartości daty do tej samej instancji między zmianami stawek), dlatego zaleca się dodanie do żądania obiektu „podpowiedzi”, używanego przez instancja fabryki, a nie sklep; oraz keyForHint do fabryki, używane przez sklep. Możesz dodać te funkcje później, właśnie wspomniałem.
W twoim przypadku jest to rodzaj przesady (tylko jeden obiekt jest obsługiwany na poziomie globalnym), ale w przypadku dość małego i prostego dodatkowego kodu w tej chwili otrzymujesz mechanizm dla dalszych, być może bardziej skomplikowanych wymagań.
Kolejna dobra wiadomość: jeśli jesteś w Javie, korzystasz z tej usługi od wiosny bez zastanowienia, chciałem tylko wyjaśnić to szczegółowo.
źródło
Powodem, dla którego NIE należy używać globalnego (lub singleton), jest to, że choć początkowo spodziewasz się mieć tylko jedną wartość, często zaskakująco przydatne jest wielokrotne użycie tego samego kodu w tym samym programie, na przykład:
Uczyniłbym stopę procentową członkiem klasy „instrumentów finansowych” i zaakceptowałem, że musicie ją przekazać wszystkim klasom członkostwa (albo według obliczeń, albo nadając im wskaźnik / haczyk na budowie).
źródło
Rzeczy powinny być przekazywane w wiadomościach, a nie czytane z globalnej rzeczy.
źródło