Czy DAO powinno być singlem czy nie?

14

Tworzę API RESTful i myślę, że wygodnie jest używać DAO dla moich zasobów, ponieważ chociaż planuję po prostu używać pamięci do ich przechowywania, nie chcę zamykać drzwi przed kimkolwiek, kto korzysta z mojej biblioteki, jeśli zdecydują się użyć implementacja bazy danych dla DAO.

Moje pytanie brzmi, czy DAO powinno być singlem, czy nie. Jeśli nie, usługa będzie miała instancję DAO i będzie wyglądać mniej więcej tak:

@Path("eventscheduler")
public class EventSchedulerService {
    private IEventSchedulerDao dao = new EventSchedulerDao();

    // in case a different implementation is to be used
    public void setEventSchedulerDao(IEventSchedulerDao dao) {
        this.dao = dao;
    }

    @Path("{uniqueName}")
    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public Tournament getTournament(@PathParam("name") String uniqueName) {
        return dao.get(uniqueName);
    }

    @Path("create")
    @POST
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    public Tournament createTournament(Tournament tournament) {
        return dao.create(tournament);
    }
}

Chociaż jeśli DAO byłby singlem, ale myślę, że nie byłoby dużej różnicy, tylko w pierwszym wierszu:

private IEventSchedulerDao dao = EventSchedulerDao.getInstance();

Nadal musiałbym użyć IEventSchedulerDaoinstancji, ale chyba wszystkie singletony działają w ten sposób, prawda? Z jakiegoś powodu zawsze koreluję singletony z metodami statycznymi, więc zamiast wyświetlania instancji singletonu dla użytkownika getInstance(), byłoby to ukryte, a on / ona używałaby EventSchedulerDao.get(name)itd. W sposób statyczny. Czy to coś, czy to tylko ja?

Czy powinienem, czy nie powinienem mieć singleton DAO?

I na marginesie, czy w porządku jest moje podejście do posiadania otwartych drzwi dla użytkownika do wdrożenia własnych DAO?

dabadaba
źródło
Możesz użyć singletonu IoC zamiast singletona ze statycznym akcesorium.
CodesInChaos

Odpowiedzi:

10

Nie użyłbym singletona. Jest to uznany anty-wzór i utrudnia testowanie. Wolałbym raczej wprowadzić konkretną implementację, a twoja usługa powinna odwoływać się do interfejsu DAO (pozwalając ci na wprowadzanie różnych implementacji)

Brian Agnew
źródło
1
Co sugerujesz w ostatnim zdaniu, to właśnie to, co robię dobrze?
dabadaba
1
Odwołujesz się przez interfejs (tak), ale nie wstrzykujesz DAO (żeby być jasnym ...)
Brian Agnew
Co masz na myśli? Mam do tego setera, prawda?
dabadaba
@dabadaba Linia private IEventSchedulerDao dao = new EventSchedulerDao();jest tam, gdzie popełniłeś błąd. Implementacja dla IEventSchedulerDaopowinna zostać wstrzyknięta przez konstruktor i nigdy nie zmieniana (tj. Pozbyć się setEventSchedulerDaoteż).
David Arno,
Okej rozumiem. Właśnie to zrobiłem, aby zapewnić domyślną DAO, a jej zmiana byłaby „opcjonalna”. Ale przyjęcie twojej sugestii oznacza posiadanie konstruktora dla usługi innej niż domyślny, i szczerze mówiąc, nie mam pojęcia, jak to działa z Jersey, ponieważ używa domyślnego konstruktora. Czy wiesz, jak to zrobić?
dabadaba
4

D ata ccess O bject powinna istnieć naprawdę tylko raz w swojej aplikacji. Logika pozostaje taka sama, jedyne, co się różni, to wartości przychodzące i wychodzące z metod zapewnianych przez DAO.

Mając to na uwadze, oczywiście pierwszą rzeczą, która zwykle się zdarza, jest implementacja DAO jako silnego singletona , to znaczy, gdy masz staticmetodę w klasie fabrycznej, coś w rodzaju getInstanceleniwego ładowania instancji DAO, jeśli jest zerowa i zwracania jej.

Przepraszam, jeśli składnia jest nieprawidłowa, nie jestem programistą Java.

class DaoSingletonFactory
{
    private static Dao dao = null;

    public static Dao getInstance()
    {
        if (DaoSingletonFactory.dao == null) {
            DaoSingletonFactory.dao = new Dao();
        }

        return DaoSingletonFactory.dao;
    }
}

class UsesDao
{
    public void someMethod()
    {
        Dao dao = DaoSingletonFactory.getInstance();
    }
}

Jest to niezwykle trudne do przetestowania, ponieważ nie można zamienić implementacji bez zmiany kodu UsesDaoklasy. Można to zrobić poprzez łatanie małp , ale ogólnie nie jest to uważane za dobrą praktykę.

Jest też lepszy sposób, słaby wzorzec singletonu , w którym nie odzyskujesz instancji za pomocą staticmetody, ale uzależniasz wszystkie klasy od instancji poprzez konstruktor lub seter (w twoim EventSchedulerServiceprzypadku używasz iniekcji setera).

Jedynym problemem jest to, że musisz upewnić się, że wszystkie klasy, które zależą od instancji klasy, która powinna istnieć tylko po cyklu życia aplikacji, przyjmują tę samą instancję jako parametr, tj. newjest wywoływana tylko raz na obiekt DAO w całej aplikacji.

Oczywiście, śledzenie tego jest niezwykle trudne, a konstruowanie wykresu obiektowego jest żmudną i irytującą pracą.

Na szczęście istnieją pojemniki IoC , które znacznie to ułatwiają. Oprócz wiosny , kontener Guice IoC firmy Google jest dość popularny wśród programistów Java.

Korzystając z kontenera IoC, konfigurujesz go tak, aby zachowywał się w określony sposób, tj. mówisz, czy ma ona konstruować pewne klasy i czy jakaś klasa jest wymagana jako zależność, jak powinna wyglądać zależność (czy powinna to być zawsze nowa instancja, czy singleton), a kontener łączy to wszystko.

Możesz sprawdzić ten link dla singletonowego przykładu z Guice.


Plusy i minusy korzystania z kontenera IoC

Plusy

  • oszczędność budżetu, ponieważ nie musisz sam pisać wszystkich metod fabrycznych
  • (zwykle) bardzo prosta konfiguracja
  • szybki rozwój

Cons

  • magia czarodzieja, klasy są w jakiś sposób skonstruowane i naprawdę nie widać, jak to się stało
  • niewielki spadek wydajności ze względu na wyszukiwanie klas (ręcznie napisane fabryki będą nieco szybsze)
Andy
źródło
1

Singleton odnosi się do koncepcji tylko jednej instancji i sposobu uzyskania dostępu do instancji (za pomocą tak znanej metody statycznej getInstance () )

Ale za tym wszystkim stoi jeszcze jeden przykład. Obiekt zbudowany z pewnego rodzaju ograniczonym dostępem.

W twoim przypadku wolę podejście DI (wstrzykiwanie zależności). Jak pierwszy odsłonięty blok kodu. Mała zmiana. Wstrzyknąć DAO za pomocą konstruktora. Aby usunąć lub nie setter zależy od ciebie. Jeśli chcesz chronić kontroler przed zmianami w środowisku wykonawczym, usuń go. Jeśli chcesz zaoferować taką możliwość, zachowaj ją.

Masz rację, korzystając z interfejsu i oferując otwarte okno na dalsze implementacje DAO. Może, ale nie musi być potrzebny. Zajmuje to tylko minutę pracy, ale sprawia, że ​​Twój projekt jest elastyczny. Twoja pamięć DAO jest dość powszechna. Bardzo przydatny jako próbny w czasie testu. Lub jako domyślna implementacja DAO.

Tylko wskazówka. Zasoby statyczne (obiekty, metody, stałe lub zmienne) są jak zasoby globalne. Jeśli globale są złe, czy nie, jest to kwestią potrzeb lub gustów. Jednak wiążą się z nimi ukryte niedociągnięcia. Są one związane z współbieżnością , bezpieczeństwem wątków (w Javie, nie wiem o innych językach), serializacją ...

Sugerowałbym więc ostrożne używanie statyki

Laiv
źródło