Kontynuacja kolejnego pytania ( Podjęcie decyzji projektowej dotyczącej odczytu danych modelu z pliku wejściowego ).
Chciałbym zadać kolejne pytanie dotyczące konstruktora lub wzorca fabryki. (Czytałem, że konstruktor jest bardziej złożony niż fabryka i może na razie nie muszę go używać). Oto dane, które muszę przeczytać:
TABLE: "DEGREES OF FREEDOM" X=Yes Y=Yes Z=Yes R1=Yes R2=Yes R3=Yes TABLE: "ANALYSIS OPTIONS" Solver=Advanced SolverProc=Advanced TABLE: "COORDINATE SYSTEMS" Name=GLOBAL Type=Cartesian X=0 Y=0 Z=0 TABLE: "GRID LINES" CoordSys=GLOBAL AxisDir=X GridID=A XRYZCoord=-720 LineColor=Gray8Dark Visible=Yes CoordSys=GLOBAL AxisDir=X GridID=B XRYZCoord=-432 LineColor=Gray8Dark Visible=Yes CoordSys=GLOBAL AxisDir=X GridID=C XRYZCoord=-144 LineColor=Gray8Dark Visible=Yes CoordSys=GLOBAL AxisDir=X GridID=D XRYZCoord=144 LineColor=Gray8Dark Visible=Yes CoordSys=GLOBAL AxisDir=X GridID=E XRYZCoord=432 LineColor=Gray8Dark Visible=Yes CoordSys=GLOBAL AxisDir=X GridID=F XRYZCoord=720 LineColor=Gray8Dark Visible=Yes ...
Jest o wiele więcej takich tabel. Niektóre tabele mają relacje nadrzędne i podrzędne (każdy układ współrzędnych ma własną linię siatki). Stworzyłem struktury, które reprezentują każdą tabelę, na przykład:
struct ActiveDOF { bool Ux; bool Uy; bool Uz; bool Rx; bool Ry; bool Rz; }; struct GridLine { enum Direction{X, Y, Z}; enum Type{PRIMARY, SECONDARY}; enum Location{START, END}; std::string coodSystem; Direction direction; std::string gridID; float XRYZ; Type lineType; QColor color; bool visible; Location bubleLoc; bool allVisible; float bubleSize; }; struct CoordinateSystem { std::string name; std::string type; QVector3D center; // center x, center y, cetner z QVector3D about; // aboutx, about y, about z std::vector<GridLine> gridLines; };
te struktury danych są włączone do klasy modelu, co stworzy ogromną klasę, ponieważ istnieje 50 dziwnych struktur danych, takich jak ta:
class Model { private: ActiveDOF activeDOF; CoordinateSystem coordinateSystem; .... public: Model() {} ... }
każda tabela musi mieć swoją metodę ustawiania i pobierania. Ten projekt mnie niepokoi, ponieważ jeśli zdecyduję się go zmienić, będzie to bardzo czasochłonne. Doceniam każdą sugestię. Myślę, że zawarte tutaj informacje również postawią w lepszym świetle wcześniejsze pytanie.
Teraz, biorąc pod uwagę sytuację, nie wiem, gdzie powinien znaleźć się konstruktor lub metoda fabryki. Spojrzałem na niektóre kody i wykresy UML, ale nie byłem w stanie zrozumieć, jak zaimplementować fabrykę lub konstruktora do tworzenia struktur wymaganych do mojego projektu. muszę mieć dostęp do każdej tabeli po nazwie, ponieważ mogą one podlegać zmianom wewnątrz modelu, więc na razie unikam tworzenia z każdej z nich podklasy wirtualnej klasy bazowej, abym mógł je przechowywać w kontenerze.
Poza tym, czy ma sens, aby zamiast deklarować instancję struktury danych, powinienem zachować do nich wskaźnik? jeśli wszystkie struktury danych pochodzą z wirtualna klasa bazowa o nazwie Record, model będzie wyglądał mniej więcej tak:
class Model { private: ActiveDOF* activeDOF; CoordinateSystem* coordinateSystem; .... std::Vector<Record*> data; public: Model() {} ... }
Myślę, że przydzielanie i przemieszczanie pamięci dla nich jest dodatkową pracą, ale może pomóc w zarządzaniu danymi i dodatkowym wpisywaniu? Czy mam rację, zakładając, że?
Komentarze
Odpowiedź
Próbujesz rozwiązać problem z dostępem do danych i wymaga rozwiązania w zakresie dostępu do danych.
Najpierw przeanalizujmy proponowane przez Ciebie rozwiązania i dlaczego nie rozwiązują one Twoich problemów.
Wzorzec fabryczny
Wzorzec projektowania fabrycznego (nazywany również wzorcem metody fabrycznej) jest przydatny, gdy chcesz wykorzystać polimorfizm i całkowicie oddzielić konstrukcję obiektu od klas implementujących.
W programowaniu opartym na klasach wzorzec metody fabrycznej to wzorzec kreacyjny , który wykorzystuje metody fabryczne do rozwiązywania problemu tworzenia obiektów bez konieczności określania dokładna klasa obiektu, który zostanie utworzony. Odbywa się to przez tworzenie obiektów przez wywołanie metody fabrycznej — podanej w interfejs i zaimplementowane przez klasy potomne lub zaimplementowane w klasie bazowej i opcjonalnie nadpisane przez klasy pochodne — zamiast wywoływania konstruktora .
(wyróżnienie, moje)
I dalej omawia problemy, które rozwiązuje ten wzorzec:
Wzorzec projektowy Factory Method rozwiązuje problemy takie jak:
- Jak można utworzyć obiekt aby podklasy mogły przedefiniować, której klasy należy utworzyć instancję?
- W jaki sposób klasa może odroczyć tworzenie instancji do podklas?
Problemy, które opisujesz w swoim pytaniu, różnią się od tych. Nie masz do czynienia z podklasami ani polimorfizmem. Wzorzec fabryki nie jest rozwiązaniem.
Wzorzec konstruktora
Wzorzec konstruktora (z Wikipedii ):
Konstruktor to wzorzec projektowy zaprojektowany w celu zapewnienia elastycznego rozwiązania różnych problemów związanych z tworzeniem obiektów w programowaniu obiektowym. Celem wzorca projektowego Builder jest oddzielenie konstrukcji złożonego obiektu od jego reprezentacji.
(podkreślenie, moje)
Tutaj znowu widzimy niezgodność między opisywanym problemem a zamierzonym użyciem tego wzorca.
Wzorzec projektowy Builder rozwiązuje takie problemy, jak:
- W jaki sposób klasa (ten sam proces konstrukcji) może tworzyć różne reprezentacje obiekt złożony ?
- W jaki sposób klasa, która obejmuje tworzenie złożony obiekt uprościć?
(wyróżnienie, moje)
Kluczem jest tutaj to, że konstrukcja obiektu (obiekt = 1 obiekt) jest złożona. Może to być spowodowane dużą liczbą argumentów konstruktora lub złożonością montażu jego zależności.
Indywidualnie każda struktura lub klasa jest dość prosta, więc wzorzec konstruktora również nie jest dobrze dopasowany.
Wzorce dostępu do danych (rozwiązanie)
Problemy, które faktycznie próbujesz rozwiązać, dotyczą sposobu uzyskiwania dostępu do danych i być może szukasz Obiekt dostępu do danych .
W oprogramowaniu komputerowym obiekt dostępu do danych (DAO) jest obiektem, który zapewnia abstrakcyjny interfejs do pewnego typu bazy danych lub innego mechanizmu utrwalania. Poprzez mapowanie wywołań aplikacji do warstwy trwałości , DAO udostępnia określone operacje na danych bez ujawniania szczegółów bazy danych.
W twoim przypadku zamień „bazę danych” na „plik tekstowy”. Powiązanym wzorcem projektowym jest Wzorzec projektowy repozytorium , który dzieli dostęp do danych na 3 główne rozwiązania:
- Repozytorium publiczny interfejs „dostępu do danych”
- Brama — potrafi rozmawiać z bazą danych Oracle lub czytać / zapisywać do niej plik tekstowy. Obejmuje to serializację obiektów i struktur do SQL lub formatu danych oczekiwanego w pliku tekstowym.
- Fabryka — odwzorowuje dane zwrócone przez bramę na obiekty i struktury używany przez resztę aplikacji
Własność pamięci
Ponieważ nie masz zalet / ograniczeń automatycznego zarządzania pamięcią, własność pamięci przydzielonej te obiekty i struktury są zdecydowanie problemem. Tutaj musisz podjąć decyzję:
- Czy obiekt dostępu do danych jest odpowiedzialny za czyszczenie tej pamięci?
- Czy obiekt dostępu do danych spodziewasz się, że wywołujący (kod klienta) weźmie odpowiedzialność za zwolnienie tej pamięci?
Jeśli obiekt dostępu do danych jest tworzony podczas uruchamiania aplikacji i żyje do zamknięcia aplikacji, dostęp do danych obiekt mógłby przejąć na własność tę pamięć.
Jeśli obiekt dostępu do danych jest tworzony na żądanie, a następnie usuwany, kod klienta musi upewnić się, że ta pamięć jest czysta ned up.
Zdefiniuj to w komentarzach lub dokumentacji obiektu dostępu do danych i zastosuj to w przeglądzie kodu.
Komentarze
- Podoba mi się to podejście. Wygląda znacznie bardziej elastycznie. Wszystko, o co poproszę, ujawni moją ignorancję w tej sprawie. Czy powinienem sam wdrożyć DAO? Jak to rozumiem, powinienem napisać Gateway and Factory? Czy brama jest taka sama jak wzorzec mostu GOF?
- Powiedziałbym, że brama we wzorcu repozytorium jest inną odmianą wzorca mostu. W rzeczywistości wygląda na to, że podstawą wzorca repozytorium jest bardziej zorganizowany i skoordynowany wzorzec mostu, który koncentruje się szczególnie na dostępie do danych. I tak, ' prawdopodobnie będziesz musiał samodzielnie napisać te komponenty.
- korzystając z opublikowanych przez Ciebie linków, dotarłem do en.wikipedia.org/wiki/ODB_(C%2B%2B) , Jak to ODB pasuje do obrazu? Mogę napisać program konwertujący moje dane wejściowe na sqllite, a następnie używać ODB do trwałości? czy to dźwięk? Muszę powiedzieć, że właśnie natknąłem się na ODB i spojrzałem na dwa przykłady, więc nie jest to uzasadniony komentarz.
- Pomoc w pisaniu kodu nie jest tematem tej witryny, jednak ' m na pewno możesz przekonwertować tekst na sqlite i użyć istniejącej biblioteki.
Odpowiedź
Konstruktor zostałby „zbudowany” wewnątrz niezmiennika fabrycznego (metody), aby odczytać plik tekstowy napisałbyś 1 fabrykę z metodą odczytu wspomnianego pliku. Teraz, jeśli musisz stopniowo budować model na podstawie danych, które czytasz (myśl o danych zagnieżdżonych), wzorzec konstruktora wewnątrz metody fabryki będzie ci dobrze służył. Jednak do załadowania danych prawdopodobnie wystarczy jeden model.
Komentarze
- Pseudokod może być bardzo pomocny. Nigdy wcześniej nie korzystałem z wzorców fabryki ani konstruktora.
Odpowiedź
Biorąc pod uwagę rozmiar i złożoność struktury, Kreator wydaje się najbardziej odpowiedni, zwłaszcza jeśli model jest (lub można go uczynić) niezmiennym. Będziesz musiał zaakceptować pewne ścisłe powiązanie między konstruktorem a modelem, ale jest to warte zachodu, zwłaszcza jeśli będziesz musiał budować modele w wielu miejscach. Kreator umożliwia również hermetyzację walidacji.
Jeśli jednak twój model jest zmienny i jest tworzony tylko w jednym miejscu, wtedy fabryka może być bardziej odpowiednia i celowa, a YAGNI wchodzi do gry.
W odpowiedzi na Twoje drugie pytanie , nigdy nie należy przechowywać surowych wskaźników w C ++. Faktem jest, że std::unique_ptr
nie ma wad i trywializuje niszczenie obiektów. Kiedy ten wektor wyjdzie poza zakres, nie zwolni pamięci odniesienia tak, jak napisano, ale z unique_ptr, tak.
Utworzyłbym wektor dla dużych ilości powtarzających się elementów, zwłaszcza jeśli ilość prawdopodobnie ulegnie zmianie, ale w przypadku komponentów średniej wielkości, które nigdy nie są udostępniane, równie dobrze możesz uwzględnić je bezpośrednio w strukturze.
Model
(lub różne wyprowadzenia wspólnej klasy bazowejModelBase
lubIModel
) i chcesz scentralizować decyzję o tym, które konkretne wyprowadzenie zostanie utworzone. Nie ' nie widzę tego problemu w Twoim pytaniu, więc jaki jest ' pomysł na wykorzystanie tutaj fabryki?