Fabriksmönster eller byggmönster? vilken är lämplig för att läsa Finite Element Model-data från en textfil?

En uppföljning av en annan fråga ( Att fatta ett designbeslut om att läsa modelldata från en inmatningsfil ).

Jag vill ställa en ny fråga angående byggmästare eller fabriksmönster. (Jag läste att byggaren är mer komplex än fabriken, och jag kanske inte behöver använda byggaren för nu). Så här är de uppgifter jag måste läsa:

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 ... 

Det finns många fler tabeller som dessa. Vissa tabeller har förhållanden mellan föräldrar och barn (varje koordinatsystem har en egen rutnät). Jag har gjort strukturer som representerar varje tabell, så här:

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; }; 

dessa datastrukturer ingår i modellklassen, vilket kommer att göra en enorm klass eftersom det finns 50 udda datastrukturer så här:

class Model { private: ActiveDOF activeDOF; CoordinateSystem coordinateSystem; .... public: Model() {} ... } 

varje tabell måste ha sin inställningsmetod och get-metod. Denna design oroar mig för om jag bestämmer mig för att ändra den kommer det att bli mycket tidskrävande. Jag uppskattar alla förslag. Jag tror att informationen här också kommer att sätta den tidigare frågan i ett bättre ljus.

Nu vet jag inte vart byggaren eller fabriksmetoden ska gå, med tanke på situationen. Jag tittade på några koder och UML-diagram men kunde inte förstå hur jag skulle implementera fabriken eller byggaren för att skapa strukturer som krävs för min design. Jag måste ha tillgång till varje tabell med namn, eftersom de kan komma att ändras inuti modellen, så för närvarande undviker jag att göra var och en av dem till en underklass av en virtuell basklass, så att jag kan lagra dem i en behållare.

Är det också vettigt att istället för att förklara en instans av datastrukturen, ska jag hålla en pekare åt dem? om alla datastrukturer härrör från en virtuell basklass som heter Record, då kommer modellen att vara ungefär så här:

class Model { private: ActiveDOF* activeDOF; CoordinateSystem* coordinateSystem; .... std::Vector<Record*> data; public: Model() {} ... } 

Jag tycker att det är extra arbete att allokera, ta bort minne åt dem, men det kan hjälpa till med att hantera data och fortsätta att skriva extra? Har jag rätt i att anta det?

Kommentarer

  • I ’ mn inte säker på att svaret är något av dina val. Fabriksmönstret används vanligtvis i kombination med polymorfism. Byggmönstret används när objektkonstruktionen är komplex. Du använder ’ t med polymorfism, och varje struktur är ganska enkel. detta verkar bara som en rak upp ” hur gör jag dataåtkomst ” fråga.
  • ” Fabrik ” och ” Builder ” löser olika problem . ” Fabrik ” passar om du har olika härledningar av klassen Model olika härledningar av en gemensam basklass ModelBase eller IModel), och du vill centralisera beslutet om vilken konkret härledning som blir omedelbar. Jag ser inte ’ detta problem i din fråga, så vad ’ är din idé bakom att använda en fabrik här?

Svar

Du försöker lösa ett datatillgångsproblem och det kräver en datatillgångslösning.

Låt oss först undersöka dina föreslagna lösningar och varför de inte löser de problem du har.

Fabriksmönstret

Fabriksmönstret (även kallat fabriksmetodsmönstret) är användbart när du vill använda polymorfism och vill separera objektkonstruktionen helt från implementeringsklasserna.

Wikipedia :

I klassbaserad programmering, fabriksmetodsmönstret är ett skapande mönster som använder fabriksmetoder för att hantera problemet med att skapa objekt utan att behöva ange exakt klass för objektet som kommer att skapas. Detta görs genom att skapar objekt genom att anropa en fabriksmetod — antingen specificerad i ett gränssnitt och implementerat av underklasser, eller implementerat i en basklass och eventuellt åsidosatt av härledda klasser — snarare än genom att ringa en konstruktör .

(betoning, min)

Och längre ner utarbetas det problemen som detta mönster löser:

Designmönstret från Factory Method löser problem som:

  • Hur kan ett objekt skapas så att underklasser kan omdefiniera vilken klass att starta?
  • Hur kan en klass skjuta upp instantiering till underklasser?

Problemen du beskriver i din fråga skiljer sig från dessa. Du har inte att göra med underklasser eller polymorfism. Fabriksmönstret är inte en lösning.

Byggmönstret

Byggmönstret (från Wikipedia ):

Builder är ett designmönster som är utformat för att ge en flexibel lösning på olika objektskapande problem i objektorienterad programmering. Syftet med Builder-designmönstret är att skilja konstruktionen av ett komplext objekt från dess representation.

(betoning, min)

Här ser vi återigen en skillnad mellan det problem du beskriver och den avsedda användningen av detta mönster.

Builder designmönstret löser problem som:

  • Hur kan en klass (samma konstruktionsprocess) skapa olika representationer av en komplext objekt ?
  • Hur kan en klass som inkluderar att skapa en komplexa objekt förenklas?

(betoning, min)

Nyckeln här är konstruktionen av ett objekt (ett objekt = 1 objekt) är komplext. Detta kan bero på ett stort antal konstruktörargument, eller på grund av komplexiteten i att montera dess beroenden.

Varje struktur eller klass är individuellt ganska enkel, så byggmönstret passar inte heller.

Dataåtkomstmönster (lösningen)

Problemen du faktiskt försöker lösa är hur du utför datatillgång, och du kanske letar efter en Dataåtkomstobjekt .

I datorprogramvara är ett dataåtkomstobjekt (DAO) är ett objekt som ger ett abstrakt gränssnitt till någon typ av databas eller annan uthållighetsmekanism. Genom att mappa applikationsanrop till uthållighetsskiktet , DAO tillhandahåller vissa specifika datahantering utan att avslöja detaljer i databasen.

I ditt fall ersätter du ”databas” med ”textfil”. Ett relaterat designmönster är Förvarets designmönster , som delar dataåtkomst i tre huvudlösningar:

  • Förvar — det offentliga gränssnittet för ”dataaccess stuff”
  • Gateway — vet hur man pratar med en Oracle-databas eller läser / skriver till en textfil. Detta skulle inkludera serieisering av objekt och strukturer till SQL eller det dataformat som förväntas i textfilen.
  • Fabrik — kartlägger data som returneras av gatewayen till objekten och strukturerna används av resten av din applikation

Ägarskap till minne

Eftersom du inte har fördelen / begränsningen av automatisk minneshantering, äger du minnet som tilldelats dessa objekt och strukturer är definitivt ett problem. Här måste du fatta ett beslut:

  • Är dataåtkomstobjektet ansvarigt för att städa upp minnet?
  • Har dataåtkomstobjektet förvänta dig att den som ringer (klientkod) tar ansvar för att frigöra minnet?

Om dataåtkomstobjektet skapas när applikationen startar och lever tills applikationen stängs av, kommer datatillgången objektet kan äga detta minne.

Om dataåtkomstobjektet skapas på begäran och sedan kasseras måste klientkoden se till att minnet är rent ned upp.

Definiera detta i kommentarerna eller dokumentationen för dataåtkomstobjektet och genomdriv detta i kodgranskning.

Kommentarer

  • Jag gillar den här metoden. Ser mycket mer flexibel ut. Allt annat jag frågar kommer att avslöja min okunnighet om saken. Ska jag implementera DAO själv? Så som jag förstår det borde jag skriva Gateway and Factory? Är gatewayen samma som bro-GOF-mönster?
  • Jag skulle vilja säga att gatewayen i förvarmönstret är en annan variant av bromönstret. I själva verket ser det ut som att grunden till förvarsmönstret är ett mer organiserat och samordnat bromönster fokuserat specifikt på datatillgång. Och ja, du ’ kommer troligtvis att behöva skriva dessa komponenter själv.
  • efter länkarna du skickade fick jag en.wikipedia.org/wiki/ODB_(C%2B%2B) , Hur denna ODB passar in i bilden? Jag kan skriva ett program för att konvertera min textinmatning till sqllite och sedan använda ODB för uthållighet? är detta ljud? Jag måste säga att jag bara snubblat över ODB och tittat på två exempel, så det här är ingen informerad kommentar.
  • Hjälp med att skriva kod är av ämnet för den här webbplatsen, men jag ’ är säker på att du kan konvertera texten till sqlite och använda ett befintligt bibliotek.

Svar

En byggare skulle ”byggas” inuti en fabriksinställning (metod) och därigenom läsa en textfil, du skulle skriva en fabrik med en metod för att läsa nämnda fil. Nu, om du behöver bygga din modell stegvis från de data du läser (tänk kapslade data), skulle byggmönstret i fabriksmetoden tjäna dig bra. En enstaka modell räcker dock troligen för att ladda dina data i.

Kommentarer

  • Vissa pseudokoder kan vara till stor hjälp. Jag har aldrig använt fabriks- eller byggmönster förut.

Svar

Med tanke på din strukturs storlek och komplexitet, en byggare verkar mest lämplig, särskilt om din modell är (eller kan göras) oföränderlig. Du måste acceptera en viss tät koppling mellan byggaren och modellen, men det är värt besväret, speciellt om du behöver bygga modeller på flera ställen. En byggare låter dig också inkapsla valideringen.

Men om din modell kan ändras och bara skapas på ett ställe, kan en fabrik vara mer lämplig och ändamålsenlig och YAGNI kommer till spel.


Som svar på din andra fråga , bör du aldrig lagra råa pekare i C ++. Faktum är att std::unique_ptr inte har några nackdelar och det bagatelliserar objektförstörelsen. När den här vektorn går utanför räckvidden kommer den inte att frigöra minnet referenser som skrivna, men med en unik_ptr skulle det.

Jag skulle skapa en vektor för stora mängder upprepande element, särskilt om kvantiteten sannolikt kommer att ändras, men för medelstora komponenter som aldrig delas kan du lika gärna inkludera dem direkt i strukturen.

Lämna ett svar

Din e-postadress kommer inte publiceras. Obligatoriska fält är märkta *