2010-02-25 23 views
11

Wydaje mi się, że utknąłem mentalnie w dylemacie związanym z Flyweight.Problem z wagą i fabryki z IDisposable

pierwsze, powiedzmy, że mam do dyspozycji rodzaj DisposableFiddle i fabryki FiddleFactory:

public interface DisposableFiddle : IDisposable 
{ 
    // Implements IDisposable 
} 

public class FiddleFactory 
{ 
    public DisposableFiddle CreateFiddle(SomethingThatDifferentiatesFiddles s) 
    { 
     // returns a newly created fiddle. 
    } 
} 

wtedy moim zdaniem, jest to całkiem jasne dla klienta z FiddleFactory że fabryka nie rości sobie żadnych praw własności tworzonego skrzypce oraz, że klient jest odpowiedzialny za wyrzucenie skrzypiec po zakończeniu pracy.

Jednak niech zamiast powiedzieć, że chcę podzielić skrzypcach między klientami za pomocą wzoru muszej:

public class FiddleFactory 
{ 
    private Dictionary<SomethingThatDifferentiatesFiddles, DisposableFiddle> fiddles = new ...;   

    public DisposableFiddle CreateFiddle(SomethingThatDifferentiatesFiddles s) 
    { 
     // returns an existing fiddle if a corresponding s is found, 
     // or a newly created fiddle, after adding it to the dictionary, 
     // if no corresponding s is found. 
    } 
} 

wtedy czuję się moralnie zobowiązany do fabryki Sam jednorazowy, ponieważ tworzy skrzypce i utrzymuje referencje do nich przez całe ich życie. Ale to mogłoby spowodować problemy dla klientów, którzy zakładali, że są właścicielami skrzypiec i dlatego powinni je wyrzucić.

faktycznie jest problem, który ja nazywam fabrykę FiddleFactory zamiast, powiedzmy, FiddlePool i „tworzenie” metoda CreateFiddle zamiast GetFiddle? Tak:

public class FiddlePool : IDisposable 
{ 
    private Dictionary<SomethingThatDifferentiatesFiddles, DisposableFiddle> fiddles = new ...;   

    public DisposableFiddle GetFiddle(SomethingThatDifferentiatesFiddles s) 
    { 
     // returns an existing fiddle if a corresponding s is found, 
     // or a newly created fiddle, after adding it to the dictionary, 
     // if no corresponding s is found. 
    } 

    // Implements IDisposable 
} 

Wtedy to wyraźniej do klienta, że ​​nie będzie właścicielem wracającą skrzypce i jest to odpowiedzialność basenu do dysponowania na skrzypcach.

Czy może to być łatwo rozwiązane tylko w zakresie dokumentacji?

Czy istnieje wyjście z dylematu? Czy istnieje nawet dylemat? :-)

Odpowiedz

7

widzę dwie drogi wyjścia z tego problemu:

  • puli wątków stylu: przeprojektowanie klas więc FiddlePool zapewnia interfejs do zrobienia fiddly rzeczy. Pula nie przekazuje instancji Fiddle, ponieważ zamiast tego ma metodę FiddlePool.PlayFiddle. Ponieważ puli kontroluje życie na skrzypcach, jest odpowiedzialny za ich usunięcie.

  • SqlConnection stylu: zmodyfikować sposób rozporządzać publicznego Fiddle jest tak, że to naprawdę tylko zwraca na skrzypce skrzypce basenie (która obudowuje skrzypce klasa). Wewnętrznie, skrzypce basen zajmuje naprawdę zwalniając zasobów jednorazowych.

+0

Dzięki, pierwszy podąża za Prawem Demeter lepiej, a drugi pasuje lepiej w moim ogólnym projekcie. Hmmm ... –

+0

Wrzucę moje dwa centy za pierwsze podejście - widziałem, że zbyt wielu ludzi próbuje napisać własną pulę SqlConnection! (Niezupełnie, ale musiałem * musiałem wyjaśnić, dlaczego nie jest to konieczne.) –

2

Zgadzam się z drugą opinią. Terminologia "Pool" i "Get" sprawiają, że sprawy stają się bardziej przejrzyste dla konsumenta. Jednak nadal nie jest wystarczająco jasne, a dokumentacja powinna zawsze być dodana, aby zapewnić pełne, prawidłowe zrozumienie.

+3

Moje myśli dokładnie. Ponadto, prawdopodobnie nie nazwałbym twojej klasy Jednorazowym Fiddle, chyba że chcesz, aby użytkownik zlikwidował() to ... –

+0

@Reed: Zgoda na temat Jednorazowego Fiddle. Właściwe nazewnictwo ma zasadnicze znaczenie dla przenoszenia i jest pierwszą linią obrony przed niewłaściwym użyciem. – jrista

+0

Heh, tak, właśnie dodałem jednorazowe do nazwy przykładu, aby ułatwić śledzenie w moich (zdezorientowanych) krokach ... –

1

Powinieneś zrobić coś więcej niż tylko dokumentacji i metod, aby powiedzieć klientom, aby nie wywołać dysponowania nazewnictwa. Naprawdę lepiej byłoby, gdyby klienci zadzwonili do dyspozycji, więc zrób wzór użycia. Poświęć trochę czasu z naszej bazy danych, aby zbudować pule połączeń.

Baza danych tworzy pulę połączeń, które same są oparte na puli.Kod wywołujący tworzy połączenie, otwiera je, a wywołania zamykają (usuwają) na nim. Kod wywołujący nie wie nawet, czy jest połączony, czy nie, wszystkie są obsługiwane wewnętrznie przez klasę połączenia. W przypadku połączenia puli wywołanie metody Open() jest ignorowane, jeśli połączenie jest już otwarte. Polecenie Close()/Dispose() jest wywoływane, a w przypadku połączenia z pulą faktycznie zwraca połączenie z powrotem do puli zamiast go zamykać.

Możesz zrobić to samo, tworząc klasę PooledFiddle, która przesłania funkcję Dispose i zwraca obiekt do puli. Najlepiej gdyby klient nie musiał nawet wiedzieć, że jest to połączone Fiddle.

+0

"Idealnie klient nie musiałby nawet wiedzieć, że jest to połączone Fiddle". - tak, zgadzam się z tym. Myślę, że to szczegół implementacji, który jest lepiej ukryty. –