2012-05-02 21 views
9

Zdaję sobie sprawę, że istnieje wiele dyskusji na temat singletonów i dlaczego są one złe. Nie o to chodzi w tym pytaniu. Rozumiem wady singletonów.Chcę projektować alternatywę dla singletona

Mam scenariusz, w którym korzystanie z singleton jest łatwe i wydaje się mieć sens. Jednak chcę alternatywy, która pozwoli osiągnąć to, czego potrzebuję bez dużego nakładu pracy.

Nasza aplikacja została zaprojektowana jako klient, który zazwyczaj działa na laptopach w terenie i komunikuje się z serwerem zaplecza. Mamy pasek stanu na dole głównej aplikacji. Zawiera kilka obszarów tekstowych, które pokazują różne posągi i informacje, a także kilka ikon. Ikony zmieniają obraz, aby wskazać ich stan. Takich jak ikona GPS wskazująca, czy jest ona podłączona, czy nie, a także stan błędu.

Nasza główna klasa nosi nazwę MobileMain. Jest właścicielem obszaru paska stanu i odpowiada za jego utworzenie. Następnie mamy klasę StatusBarManager. StatusBarManager jest obecnie klasą statyczną, ale może być także pojedynczą. Oto początek zajęć.

public static class StatusBarManager 
{ 
    static ScreenStatusBar StatusBar; 

    /// <summary> 
    /// Creates the status bar that it manages and returns it. 
    /// </summary> 
    public static ScreenStatusBar CreateStatusBar() 
    { 
     StatusBar = new ScreenStatusBar(); 
     return StatusBar; 
    } 

MobileMain prosi StatusBarManager o StatusBar. Następnie wykorzystuje StatusBar. Żadne inne klasy nie widzą paska stanu, tylko StatusBarManager.

Aktualizacje paska stanu mogą pochodzić z praktycznie dowolnego miejsca w aplikacji. Istnieje około 20 klas, które mogą aktualizować obszary tekstu na pasku stanu i dodatkowe klasy, które aktualizują stany ikon.

Tylko jeden będzie StatusBar i jeden StatusBarManager.

Wszelkie sugestie dotyczące lepszej implementacji?

Niektóre myśli, że miałem:

Wykorzystaj StatusBarManager klasa instancji. W mojej klasie MobileMain przytrzymaj na statycznej publicznej instancji klasy StatusBarManager. Następnie, aby zaktualizować pasek statusu, można by nazwać MobileMain.StatusBarManager.SetInformationText lub inną metodą menedżera. StatusBarManager nie byłby singletonem, ale MobileMain tworzyłby tylko jego statyczną instancję. Problem polega na tym, że MobileMain ma teraz StatusBar i StatusBarManager, który zarządza właśnie StatusBar, który jest właścicielem. Nadal istnieje również globalnie dostępne statyczne wystąpienie do StatusBarManager, tylko inny właściciel.

Innym pomysłem było użycie czegoś takiego jak klasa EventEggregator. Nigdy jej nie użyłem, ale przeczytałem o nich. Domyślam się, że jest to klasa dostępna na całym świecie. W każdej klasie, która chce zaktualizować pasek stanu, będzie publikować zdarzenie StatusBarUpdate. StatusBarManager będzie jedyną klasą subskrybującą zdarzenie StatusBarUpdate i otrzymującą wszystkie powiadomienia. Czytałem jednak, że może to skończyć się przeciekami z tym podejściem, jeśli nie jesteś ostrożny z rezygnacją z subskrypcji zdarzeń podczas czyszczenia obiektów. Czy warto to podejście podejrzeć?

+1

Ten wpis zawiera wiele pytań. Wybierz jeden na raz, aby uzyskać odpowiedź. – mydogisbox

+0

Czy myślałeś o użyciu MEF lub Unity? Tam można zarejestrować pojedyncze wystąpienie dowolnego elementu za pomocą kontenera, aby można było go odzyskać gdzie indziej. – stijn

+0

Dobra lektura związana z nazewnictwem StatusBarManager; http://www.codinghorror.com/blog/2006/03/i-shall-call-it-somethingmanager.html – Patrick

Odpowiedz

2

Posiadanie klasy StatusBar lub klasy StatusBarManager, nie jest wielkim problemem. Ale posiadanie wielu klas w twojej aplikacji wie o StatusBars i StatusBarManagers jest złym pomysłem, to spowoduje silne sprzężenie i pewnego dnia prawdopodobnie ból.

Jak?

Wyobraź sobie, że składniki, które obecnie zgłaszają status do paska stanu, muszą być ponownie użyte w innej aplikacji, która jest - używa konsoli tekstowej do zgłaszania statusu? - raportuje status do wielu miejsc? lub - w ogóle nie zgłasza statusu!

Najlepsza alternatywa: -Event listening. Pokazuj zdarzenie zmiany statusu na twojej klasie (możesz użyć wywołania zwrotnego) lub być może na istniejącym udostępnionym zasobie, które mają wspólne klasy. Inne podmioty, takie jak pasek stanu, mogą zasubskrybować wydarzenie. I powinien zrezygnować z subskrypcji, gdy subskrypcja nie jest już potrzebna/ważna, aby zapobiec wyciekom, jak wspominasz!

- Od oznaczania WPF dla WPF posiadanie właściwości zależnej "StatusText" może wydawać się inną kuszącą opcją, przy takim podejściu, gdy masz wiele właściwości statusu, potrzebujesz sposobu na ustalenie, który z nich jest informujący o najciekawszym statusie, który musi być teraz wyświetlony na pasku stanu! Które może być wiążąca, multibinding (blech, złożoność) lub właściwość zależność zmieniona obsługa zdarzeń.

Jednak - radziłbym, aby ograniczyć DependencyObjects i DependencyProperties do warstwy interfejsu użytkownika w jak największym stopniu. Powodem jest to, że polegają one niejawnie na Dispatcherze w wątku UI i dlatego nie mogą być łatwo przystosowane do zadań wykonywanych poza NU.

Ponieważ istnieje wiele różnych części aplikacji, może się okazać, że rozsądne jest połączenie obu tych elementów w jednym miejscu i w innym.

+1

Dzięki za odpowiedź. Chciałem wyjaśnić, jak rozumiem Twoje najlepsze podejście. Powinniśmy umieścić zdarzenie StatusChanged w każdej klasie, która aktualizuje pasek stanu. Podniesienie tego wydarzenia, które będzie miało subskrypcję paska stanu. Czy to właśnie sugerujesz? Jeśli tak, to StatusBar lub StatusBarManager musi znać każdą klasę aktualizującą, aby móc subskrybować zdarzenie StatusChanged. Rozumiem, że problem polegał na tym, że wszystkie nasze klasy posiadały wiedzę o StatusBarManager na potrzeby ponownego użycia, co jest po części powodem, dla którego chcę wprowadzić zmianę. – WPFNewbie

+0

Rozważałem także użycie menedżera powiadomień, który może być objęty propozycją udostępnionego zasobu. Może obsłużyć wszystkie powiadomienia, w tym pasek stanu. Idealnym rozwiązaniem byłoby posiadanie interfejsu, dzięki czemu klasy mogłyby być ponownie użyte w dowolnym projekcie, o ile miałyby klasę, która implementowała interfejs INotifier, ale nie sądzę, że można to zrobić za pomocą pojedynczej lub statycznej klasy. – WPFNewbie

+0

Ah - jest dobry punkt - StatusBar naprawdę nie musi wiedzieć o każdej klasie! Zamiast tego możesz użyć metody StatusBar.UpdateStatus(), a następnie w klasie wyższej warstwy (która ma wiedzę o wszystkim z innych powodów), wykonaj pewne powiązanie, aby wszystkie procedury obsługi zdarzeń wywoływały tę metodę. –

3

Preferuję klasy statyczne, które trzymają twoje obiekty. Tak więc liczba obiektów, do których można uzyskać dostęp, została przywrócona przez interfejs, który oferuje klasa statyczna. Statyczność nie jest zła, o ile twoja aplikacja wciąż się skaluje.

Inną dobrą alternatywą dla singletonów jest wzór Monostate, w którym istnieje klasa implementująca prywatne pola statyczne reprezentujące zachowanie "singleton".

Patrz:
Monostate
Monostate vs. Singleton

UPDATE: To często pomaga mi zachować odpoczynek jak API w umyśle, nawet dla wewnętrznych struktur programowych. Posiadanie jednej klasy, która jest aktualizowana zewsząd i wysyła powiadomienia do wszystkich, jest trudna do kontrolowania pod względem warunków i pętli nieskończoności (Aktualizacja -> Zdarzenie -> Aktualizacja -> ...)

Zbuduj (statycznie lub nie) Interfejs paska stanu, do którego można uzyskać dostęp tam, gdzie jest potrzebny. Poprzez klasę statyczną, w której uzyskujesz dostęp do interfejsu paska stanu lub zastrzyku zależności, jeśli używasz takich technik (niezalecane w przypadku mniejszych projektów). Każde wywołanie interfejsu paska stanu musi być niezależne od zdarzeń, które mogą być podnoszone przez pasek stanu, aby uniknąć dalszych problemów z warunkami podbicia. Pomyśl o interfejsie paska stanu, tak jak strona internetowa, którą można wywołać z innych części programu, aby przekazywać i wyciągać informacje.

+0

Podsumowując, nie widzisz niczego złego w naszym obecnym projektancie, gdzie StatusBarManager jest klasą statyczną? Przejrzałem pozostałe linki, które dołączyłeś. Jeśli chodzi o monostate, problem polega na tym, że nie chcę, aby był przejrzysty. Każdy powinien wiedzieć, że istnieje tylko jeden pasek stanu i właśnie to aktualizują. Tak czy inaczej mam na to uczucie. Moim głównym problemem z klasami statycznymi i singltonami jest powtarzalność klas odwołujących się do nich. Masz teraz odniesienie do globalnej klasy, która musiałaby zostać usatysfakcjonowana, gdybyś ponownie używał klasy w innym projekcie. – WPFNewbie

+0

Zazwyczaj "zła rzecz" na statykach. Gdy masz wiele klas, zależy to od niektórych klas statycznych, co uniemożliwia refaktoryzację i testowanie. Jeśli nie Twój pasek stanu, ale interfejs do paska stanu jest statyczny, pasek stanu można nadal wymieniać na przypadki testowe. Klasa statyczna będzie na tyle mała, aby przepisać ją w razie potrzeby. – Tarion

+0

Jeśli chcesz pozbyć się klasy statycznej widzę jedynie Dependency Injection (Property Injection lub Constructor Injection). Wstrzykiwanie konstruktora będzie wymagało paska stanu (lub statusuBarHandler) w każdym konstruktorze, który może być zbyt duży. PropertyInjection będzie polegać na tym, że wstrzykniesz tę właściwość. Jest to zwykle robione przez MicroKernel. Cały program będzie polegał na tym jądrze. DI to dobry sposób, jeśli potrzebujesz kodu, który można przetestować, ale możesz wprowadzać w błąd w mniejszych projektach, ponieważ trudniej jest debugować. – Tarion

-1

Trudno udzielić porady bez znajomości architektury aplikacji, ale być może powinieneś rozważyć wstrzyknięcie zależności. Na przykład przekaż instancję StatusBar do konstruktora każdej klasy, która bezpośrednio z niego korzysta.

+0

To brzmi jak z grubsza. Teraz musisz przekazać dodatkowe parametry do wszystkich obiektów w czasie budowy, dodać dodatkowy kod do swojego konstruktora, aby go zainicjować, dodatkowe pola do obiektów w całym miejscu, aby przechowywać wiele odniesień do tego, co jest właściwie pojedynczym obiektem, a wszystkie twoje typy nie można już kompilować bez typu StatusBar. –

1

Możesz po prostu użyć wzoru Observer i dodać StatusBar jako słuchacza do swoich 20 obiektów. To wyeliminuje singletony i lepiej podąży za SRP i DIP, ale będziesz musiał rozważyć, czy to jest warte wysiłku. Singleton może być lepszy, jeśli kierunek pośredni dodaje zbyt wiele komplikacji i zastrzyk zależności nie jest możliwy.

0

Klasy będą zależne niejawnie od dowolnego użycia singleton i jawnie do dowolnych parametrów w konstruktorze. Sugerowałbym dodanie interfejsu do singletonu, więc tylko potrzebne metody byłyby wystawione na klasy za pomocą IStatusBar. To jest więcej kodu, ale ułatwi testowanie jednostkowe.

Powiązane problemy