Unikaj metody inicjalizacji

12

Mam ten istniejący kod, w którym mają klasę i metodę inicjowania w tej klasie. Oczekuje się, że po utworzeniu obiektu klasy muszą one wywoływać na nim inicjalizację.

Powód, dla którego istnieje metoda inicjalizacji Obiekt jest tworzony wcześnie, aby mieć zasięg globalny, a następnie metoda inicjalizacji jest wywoływana później po załadowaniu biblioteki DLL, od której zależy.

Problem z inicjalizacją Klasa ma teraz ten inicjalizator bool, który należy sprawdzić w każdej metodzie przed kontynuowaniem i zwraca błąd, jeśli nie jest zainicjowany. Mówiąc najprościej, to duży ból.

Jedno możliwe rozwiązanie Zainicjuj w konstruktorze. Miej tylko wskaźnik do obiektu w zasięgu globalnym. Utwórz rzeczywisty obiekt po załadowaniu biblioteki dll.

Problem z powyższym rozwiązaniem Każdy, kto utworzy obiekt tej klasy, musi wiedzieć, że należy go utworzyć dopiero po załadowaniu biblioteki dll, w przeciwnym razie zakończy się niepowodzeniem.

Czy to jest dopuszczalne?


źródło
Miałem ten sam problem podczas tworzenia obiektów powiązanych z OpenGL, które muszą zostać utworzone w postaci instancji przed istnieniem kontekstu OpenGL, ale powinny zawierać obiekty zależne od OpenGL, takie jak listy wywołań, tekstury itp.
3
Głupie pytanie nr 1: Dlaczego konstruktor obiektów nie może załadować biblioteki DLL, jeśli nie jest jeszcze załadowana?
John R. Strohm,
1
Przepraszamy za wskrzeszenie starego pytania, ale jeśli ktoś czyta to w roku 2017, użyj call_oncew C ++ 11 . Projekty, które nie są jeszcze w C ++ 11, powinny zbadać, w jaki sposób call_once jest zaimplementowane w C ++ 11 (skup się na tym, jaki problem rozwiązuje, a następnie jak), a następnie ponownie zaimplementuj go w (starym) smaku C ++. Potrzebuje wielowątkowej operacji podstawowej bezpiecznej synchronizacji, której stan musi zostać zainicjowany statycznie (o stałej wartości). Należy pamiętać, że kompilatory w wersjach wcześniejszych niż C ++ 11 mogą mieć inne osobliwości, które należy spełnić.
rwong

Odpowiedzi:

7

Brzmi jak praca dla wirtualnego proxy.

Możesz utworzyć wirtualny serwer proxy zawierający odniesienie do danego obiektu. Chociaż biblioteka DLL nie jest załadowana, serwer proxy może oferować pewne domyślne zachowanie klientom, po załadowaniu biblioteki DLL serwer proxy po prostu przekaże wszystkie żądania do rzeczywistego podmiotu.

Wirtualny serwer proxy będzie odpowiedzialny za sprawdzenie inicjalizacji biblioteki DLL i na tej podstawie zdecyduje, czy żądanie powinno zostać przekazane rzeczywistemu podmiotowi.

Wzór proxy w Wikipedii

Jak ci się podoba ten pomysł?

edalorzo
źródło
tak naprawdę nie rozwiązujesz tutaj zbyt wiele. dla każdej metody dodanej do rzeczywistego obiektu należy również dodać tę metodę do proxy. Nie?
Pozwala to uniknąć problemu z zapamiętywaniem wywołania init. funkcja, ale wygląda na to, że każda funkcja w proxy musi jeszcze sprawdzić, czy plik .dll jest załadowany.
1
@ Siram jest teraz duża różnica, używając proxy ograniczyłeś konserwację związaną ze sprawdzaniem bibliotek DLL do jednej klasy: proxy. Klienci tej klasy w ogóle nie muszą wiedzieć o sprawdzaniu bibliotek DLL. Ponieważ większość metod wykonuje delegację, nie widzę większego problemu z koniecznością implementacji interfejsu zarówno w prawdziwym temacie w proxy. Innymi słowy, możesz zapobiec tworzeniu rzeczywistego tematu, dopóki nie upewnisz się, że biblioteka DLL została załadowana, a wtedy nie będziesz musiał za każdym razem sprawdzać stanu biblioteki DLL.
@edalorzo: chociaż pomysł jest dobry, problemem jest to, że już mówimy o proxy, a OP narzeka na konieczność sprawdzenia każdej metody swojego proxy ...
Matthieu M.
4

Nic użytecznego nie występuje, jeśli biblioteka DLL nie jest jeszcze załadowana; obiekt generuje tylko błąd. Czy to błąd krytyczny? Jak obsługiwany jest błąd?

Czy metodologia testowania gwarantuje, że ten błąd nigdy nie wystąpi w gotowym produkcie?

Brzmi jak zadanie do testowania, a nie do projektowania architektonicznego. Nie zwracaj tylko błędu, assertktóry nigdy się nie zdarza.

Napraw wszystkich klientów klasy, którzy obecnie wychwytują i ignorują błąd, aby nie próbował powodować go w pierwszej kolejności.

Wzorem wielowątkowym może być ustawienie współużytkowanej zmiennej warunkowej po załadowaniu biblioteki DLL i spowodowanie, że konstruktor obiektu zaczeka (i zablokuje inne wątki), aż biblioteka zostanie załadowana.

Potatoswatter
źródło
Myślę, że masz rację, nie ma nic złego w rzucaniu wyjątku podczas tworzenia obiektu, o którym mowa, jeśli biblioteka DLL nie została poprawnie zainicjowana. Kod klienta musiałby wtedy poradzić sobie tylko z tym wyjątkiem, a testowanie powinno zapewnić, że wyjątek zostanie odpowiednio obsłużony.
2

Po pierwsze: unikaj globalnych obiektów jako szkodników, chyba że ich stan nigdy się nie zmienia (konfiguracja).

Teraz, jeśli utkniesz z tym, z jakichkolwiek powodów, istnieje kilka projektów, które prawdopodobnie mogą ci pomóc.

Wpadłem na dwa pomysły:

  1. Użyj fasady, aby załadować bibliotekę DLL. Dostęp do obiektu można uzyskać tylko przez elewację, fasada ładuje bibliotekę DLL po utworzeniu i jednocześnie tworzy obiekt.

  2. Użyj proxy, ale inteligentnego;)

Pozwólcie mi rozwinąć drugą kwestię, ponieważ obawiam się, że odpowiedź @edalorzo mogła cię przestraszyć:

// Private
static Object& GetObjectImpl() { static Object O; return O; }

// Public
static Object& GetObject() {
  Object& o = GetObjectImpl();
  assert(o.isInitialized() && "Object not initialized yet!");
  return o;
}

Teraz masz tylko jeden czek.

Można to również zrobić za pomocą pewnego inteligentnego wskaźnika:

Pointer<Object> o;

Tutaj rezerwujesz tylko miejsce na wskaźnik, początkowo zerowy, i dopiero po załadowaniu biblioteki DLL faktycznie przydzielisz obiekt. Wszystkie poprzednie dostępy powinny zgłaszać wyjątek (NullException: p?) Lub kończyć program (czysto).

Matthieu M.
źródło
1

To trudne pytanie. Uważam, że architektura może pomóc w rozwiązaniu tego problemu.

Z dowodów wynika, że ​​.dll nie jest ładowany, gdy program jest ładowany. To prawie brzmi jak wtyczka.

Jedną z rzeczy, które przychodzą na myśl, jest konieczność zainicjowania pliku .dll. Programista musi założyć, że obiekt i tak nie wróci. Możesz być w stanie skorzystać z tego ( bool isObjectLoaded() { return isInitialized; }), ale na pewno dostaniesz więcej raportów o błędach w porównaniu z czekami.

Myślałem o singlu.

Jeśli wywołasz singletona, powinien zwrócić poprawny obiekt, jeśli może go odzyskać. Jeśli nie może zwrócić poprawnego obiektu, powinieneś otrzymać wartość błędu / nullptr / pusty obiekt podstawowy. To nie zadziałałoby, gdyby obiekt mógł na ciebie umrzeć.

Ponadto, jeśli potrzebujesz wielu instancji obiektu, możesz zamiast tego użyć czegoś takiego jak Fabryka.

Joe Plante
źródło