2010-01-20 7 views
6

mam ten problem, gdy trzeba projektować pakiet Java, która jest wykorzystywana do:Java sterownik bazy danych projekt

  • pobieranie danych z różnych źródeł danych. Na przykład klasa A pobierze dane klienta z bazy danych Oracle, podczas gdy klasa B pobierze te same informacje ze źródła danych usługi WWW (przez SOAP).
  • Wyniki muszą zostać połączone, a zasada kombinacji jest dość złożona, więc najlepiej powinienem ukryć to przed użytkownikami (innymi programistami) tego pakietu.
  • Gdy jedno źródło danych zawiedzie, muszę nadal zwracać wynik z innych źródeł danych. Muszę jednak powiadomić dzwoniącego, że jedno ze źródeł danych nie odpowiedziało.

W tej chwili robię to poprzez wartość boolowską w klasie A i klasie B, wskazującą, czy wystąpił błąd, oraz inny obiekt do przechowywania rzeczywistego komunikatu o błędzie. Po wywołaniu wywołujący będzie musiał sprawdzić tę wartość logiczną, aby sprawdzić, czy wystąpił błąd.

Co to jest dobry model projektu?

+1

Uwielbiam, gdy ktoś zadaje dokładnie to samo pytanie, nad którym zastanawiam się w tej chwili. Dzięki. – Martin

Odpowiedz

6

Odpowiedź będzie bardzo szeroki, więc proponuję używać:

  • Data Access Object (DAO) design pattern abstrahować źródłem danych (baz danych lub usługa)
  • strategy pattern do abstrakcyjny algorytm przez w którym dane są scalane (gdy oba źródła są dostępne, a jeden jest tylko jeden)
  • I wreszcie state design pattern, aby zmienić sposób działania aplikacji w zależności od tego, które źródło jest dostępne.
  • Wszystko to zapakowane (dlaczego nie) w miłym facade.

Ten kod psuedo ma podobną składnię jak UML i Python:

// The data implements one interface 
Data {interface} 

// And you implement it with DatabaseData 
DbData -> Data 
    ... 

// Or WebServiceData 
WsData -> Data 
    ... 

// -- DAO part 
Dao {interface} 
    + fetch(): Data[] 

// From database 
DatabaseDao -> Dao 
    - data: Data[0..*] 
    // Query database and create dbData from rows... 
    + fetch(): Data[] 
     self.status = "Not ok" 
     self.status = connectToDb() 
     if(self.status == ok , 
      performQuery() 
      forEach(row in resultSet, 
       data.add(DbData.new(resultSet.next())) 
      ) 
      disconnect() 
     ) 
    ... 

// From web service 
WebServiceDao -> Dao 
    - data: Data[0..*] 
    // Execute remote method and create wsData from some strange object 
    + fetch(): Data[] 
     remoteObject: SoapObject = SoapObject() 
     remoteObject.connect() 
     if (remoteObject.connected?(), 
      differentData: StrangeObject = remoteObject.getRemoteData() 
      forEach(object in differentData , 
       self.data.add(WsData.new(fromElement)) 
      ) 
     ).else(
      self.status = "Disconnected" 
     ) 
    .... 
// -- State part 
// Abstract the way the data is going to be retrieved 
// either from two sources or from a single one. 
FetcheState { abstract } 

    - context: Service 
    - dao: Dao // Used for a single source 

    + doFetch(): Data[] { abstract } 

    + setContext(context: Service) 
     self.context = context 
    + setSingleSource(dao: Dao) 
     self.dao = dao 

// Fetches only from one DAO, and it doesn't quite merge anything 
// because there is only one source after all. 
OneSourceState -> FetcheState 
    // Use the single DAO and fetch 
    + doFetch(): Data[] 
     data: Data[] = self.dao.doFetch() 
     // It doesn't hurt to call "context's" merger anyway. 
     context.merger.merge(data, null) 

// Two sources, are more complex, fetches both DAOs, and validates error. 
// If one source had an error, it changes the "state" of the application (context), 
// so it can fetch from single source next time. 
TwoSourcesState -> FetcheState 
    - db: Dao = DatabaseDao.new() 
    - ws: Dao = WebServiceDao.new() 

    + doFetch(): Data[] 
     dbData: Data[] = db.doFetch() 
     wsData: Data[] = ws.doFetch() 

     if(ws.hadError() or db.hadError(), 
      // Changes the context's state 
      context.fetcher = OneSourceState.new() 
      context.merger = OneKindMergeStrategy.new() 
      context.fetcher.setContext(self.context) 
      // Find out which one was broken 
      if(ws.hadError(), 
       context.fetcher.setSingleSource(db) 
      ) 
      if(db.hadError(), 
       context.fetcher.setSingleSource(ws) 
      ) 
     ) 
     // Since we have the data already let's 
     // merge it with the "context's" merger. 
     return context.merger.merge(dbData, wsData) 

// -- Strategy part -- 
// Encapsulate algoritm to merge data 
Strategy{ interface } 
    + merge(a: Data[], with : Data[] ) 

// One kind doesn't merge too much, just "cast" one array 
// because there is only one source after all. 
OneKindMergeStrategy -> Strategy 
    + merge(a: Data[], b: Data[] ) 
     mergedData: Data[] 
     forEach(item, in(a), 
      mergedData = Data.new(item) // Take values from wsData or dbData 
     ) 
     return mergedData 

// Two kinds merge, encapsulate the complex algorithm to 
// merge data from two sources. 
TwoKindsMergeStrategy -> Strategy 
    + merge(a: Data[], with: Data[]): Data[] 
     forEach(item, in(a), 
      mergedData: Data[] 
      forEach(other, in(with), 
       WsData wsData = WsData.cast(item) 
       DbData dbData = DbData.cast(other) 
       // Add strange and complex logic here. 
       newItem = Data.new() 
       if(wsData.name == dbData.column.name and etc. etc , 
        newItem.name = wsData+dbData...e tc. etc 
        ... 
        mergedData.add(newItem) 
       ) 
      ) 
     ) 
     return mergedData 

// Finally, the service where the actual fetch is being performed. 
Service { facade } 

    - merger: Strategy 
    - fetcher: FetcheState 

    // Initialise the object with the default "strategy" and the default "state". 
    + init() 
     self.fetcher = TwoSourcesState() 
     self.merger = TwoKindsMergeStrategy() 
     fetcher.setContext(self) 

    // Nahh, just let the state do its work. 
    + doFetch(): Data[] 
     // Fetch using the current application state 
     return fetcher.doFetch() 

użytkowania Klient:

 service: Service = Service.new() 
    service.init() 
    data: Data[] = service.doFetch() 

Niestety, wygląda to nieco skomplikowane.

OOP opiera się w dużym stopniu na polimorfizmie.

Tak więc w Dao pozwalasz podklasie pobierać dane z dowolnego miejsca i po prostu nazywasz to dao.fetch().

W Strategy samo, podklasa wykonuje jeden lub drugi algorytm (aby uniknąć wielu obcych if „S, else” S, switch „S, itp.);

To samo dzieje się z State. Zamiast iść jak:

if isBroken and itDoesntWork() and if ImAlive() 

itp itd po prostu powiedzieć: „Hej, to będzie jeden kod Istnieją dwa połączenia i to jest, gdy jest tylko jeden..”.

Wreszcie, fasada mówi klientowi: "Nie martw się, poradzę sobie z tym.".

+0

Świetna odpowiedź nawet przed pseudo kodem. – Martin

+0

+1 dla wzoru stanu. Ciekawe, dlaczego zacząłeś od nowa po raz trzeci? –

+0

@ Ocet: Dzięki. O trzeciej. Odpowiedziałem w innym wątku – OscarRyz

0

Proponuję fasada że reprezentowania obiektu jako całości (dane klienta) oraz fabrykę który tworzy ten obiekt, pobierając dane z każdego źródła danych i przekazując je do elewacji (np konstruktora lub budowniczego, w zależności od tego, ile istnieje). Poszczególne klasy z określonym źródłem danych miałyby metodę (na wspólnym interfejsie lub klasie bazowej), aby wskazać, czy wystąpił błąd podczas pobierania danych. Fasada (lub delegat) byłaby odpowiedzialna za łączenie danych.

Następnie fasada miałaby metodę, która zwróciłaby kolekcję pewnego rodzaju wskazującą, które źródła danych reprezentowały obiekt lub które z nich nie powiodły się - w zależności od tego, co klient powinien wiedzieć.

Powiązane problemy