Fabriekspatroon of bouwerspatroon? welke is geschikt voor het lezen van eindige-elementenmodelgegevens uit een tekstbestand?

Een vervolg op een andere vraag ( Een ontwerpbeslissing nemen over het lezen van modelgegevens uit een invoerbestand ).

Ik wil een andere vraag stellen over bouwer of fabriekspatroon. (Ik heb gelezen dat de bouwer complexer is dan de fabriek, en misschien hoef ik de bouwer voorlopig niet te gebruiken). Dus hier zijn de gegevens die ik moet lezen:

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

Er zijn veel meer van dergelijke tabellen. Sommige tabellen hebben bovenliggende, onderliggende relaties (elk coördinatensysteem heeft een eigen rasterlijn). Ik heb structuren gemaakt die elke tabel vertegenwoordigen, zoals deze:

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

deze datastructuren zijn opgenomen in de modelklasse, wat een enorme klasse zal maken omdat er 50 vreemde datastructuren zoals deze:

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

elke tabel moet zijn set-methode en get-methode hebben. Dit ontwerp baart me zorgen, want als ik besluit het te veranderen, zal het erg tijdrovend zijn. Ik waardeer elke suggestie. Ik denk dat de informatie hier ook de eerdere vraag in een beter daglicht zal stellen.

Nu weet ik niet waar de bouwer of de fabrieksmethode naartoe moet, gezien de situatie. Ik heb wat code en UML-grafieken bekeken, maar ik kon niet begrijpen hoe ik de fabriek of bouwer moest implementeren om structuren te maken die nodig zijn voor mijn ontwerp. Ik moet toegang hebben tot elke tabel op naam, omdat ze binnen het model kunnen veranderen, dus voorlopig vermijd ik om ze allemaal een subklasse van een virtuele basisklasse te maken, zodat ik ze in een container kan opslaan.

Is het ook logisch dat in plaats van een instantie van de datastructuur te declareren, ik er een pointer naar moet houden? als alle datastructuren zijn afgeleid van een virtuele basisklasse genaamd Record, dan zal het model er ongeveer zo uitzien:

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

Ik denk dat het extra werk is om geheugen voor hen toe te wijzen, te disloceren, maar het kan helpen bij het beheer van de gegevens en doorgaan met typen? Heb ik gelijk als ik dat aanneem?

Opmerkingen

  • I ‘ min Ik weet niet zeker of het antwoord een van uw keuzes is. Het fabriekspatroon wordt meestal gebruikt in combinatie met polymorfisme. Het bouwerpatroon wordt gebruikt wanneer de objectconstructie complex is. U gebruikt geen ‘ t polymorfisme, en elke structuur is vrij eenvoudig. dit lijkt een regelrechte ” hoe kan ik gegevenstoegang ” vraag.
  • ” Fabriek ” en ” Builder ” lossen verschillende problemen op . ” Fabriek ” zou geschikt zijn als u verschillende afleidingen heeft van de Model klasse (of verschillende afleidingen van een gemeenschappelijke basisklasse ModelBase of IModel), en je wilt de beslissing centraliseren over welke concrete afleiding wordt geïnstantieerd. Ik zie dit probleem niet in uw vraag ‘, dus wat is uw idee van ‘ om hier een fabriek te gebruiken?

Answer

U probeert een probleem met gegevenstoegang op te lossen, en het vereist een oplossing voor gegevenstoegang.

Laten we eerst eens kijken naar uw voorgestelde oplossingen en waarom ze de problemen die u heeft niet oplossen.

Het fabriekspatroon

Het fabrieksontwerppatroon (ook wel het fabrieksmethode-patroon genoemd) is handig als je polymorfisme wilt gebruiken en je objectconstructie volledig wilt scheiden van de implementatieklassen.

Wikipedia :

In klassengebaseerd programmeren, het patroon van de fabrieksmethode is een creatiepatroon dat fabrieksmethoden gebruikt om het probleem van het maken van objecten op te lossen zonder de exacte klasse van het object dat wordt gemaakt. Dit wordt gedaan door objecten te maken door een fabrieksmethode aan te roepen — ofwel gespecificeerd in een interface en geïmplementeerd door onderliggende klassen, of geïmplementeerd in een basisklasse en optioneel overschreven door afgeleide klassen — in plaats van een constructor aan te roepen .

(nadruk, de mijne)

En verderop gaat het in op de problemen die dit patroon oplost:

Het ontwerppatroon van de fabrieksmethode lost problemen op zoals:

  • Hoe kan een object worden gemaakt zodat subklassen kunnen herdefiniëren welke klasse moet worden geïnstantieerd?
  • Hoe kan een klasse instantiatie uitstellen naar subklassen?

De problemen die u in uw vraag beschrijft, verschillen van deze. Je hebt niet te maken met subklassen of polymorfisme. Het fabriekspatroon is geen oplossing.

Het Builder-patroon

Het Builder-patroon (van Wikipedia ):

De Builder is een ontwerppatroon dat is ontworpen om een flexibele oplossing te bieden voor verschillende problemen met het maken van objecten bij objectgeoriënteerd programmeren. De bedoeling van het Builder-ontwerppatroon is om de constructie van een complex object te scheiden van zijn representatie.

(nadruk, van mij)

Hier zien we opnieuw een discrepantie tussen het probleem dat u beschrijft en het beoogde gebruik van dit patroon.

Het Builder-ontwerppatroon lost problemen op zoals:

  • Hoe kan een klasse (hetzelfde constructieproces) verschillende representaties van een complex object ?
  • Hoe kan een klasse die een complex object vereenvoudigd worden?

(nadruk, de mijne)

De sleutel hier is dat de constructie van een object (een object = 1 object) complex is. Dit kan te wijten zijn aan een groot aantal constructorargumenten, of aan de complexiteit van het samenstellen van zijn afhankelijkheden.

Individueel is elke structuur of klasse vrij eenvoudig, dus het bouwerpatroon past ook niet goed.

Gegevenstoegangspatronen (de oplossing)

De problemen die u eigenlijk probeert op te lossen, zijn hoe u gegevenstoegang moet uitvoeren, en u bent misschien op zoek naar een Data Access Object .

In computersoftware, een data access object (DAO) is een object dat een abstracte interface biedt aan een type database of ander persistentiemechanisme. Door app-aanroepen toe te wijzen aan de persistentielaag , biedt de DAO enkele specifieke gegevensbewerkingen zonder details van de database vrij te geven.

Vervang in jouw geval “database” door “tekstbestand”. Een gerelateerd ontwerppatroon is het Repository Design Pattern , dat gegevenstoegang opsplitst in 3 hoofdoplossingen:

  • Repository — de openbare interface voor “data access stuff”
  • Gateway — weet hoe hij met een Oracle-database moet praten of hoe hij moet lezen / schrijven een tekstbestand. Dit omvat het serialiseren van objecten en structs naar SQL of het gegevensformaat dat wordt verwacht in het tekstbestand.
  • Factory — wijst gegevens toe die door de gateway worden geretourneerd aan de objecten en structuren gebruikt door de rest van uw applicatie

Eigendom van geheugen

Aangezien u niet het voordeel / de beperking heeft van automatisch geheugenbeheer, is het eigendom van het toegewezen geheugen deze objecten en structuren is zeker een punt van zorg. Hier moet u een beslissing nemen:

  • Is het gegevenstoegangsobject verantwoordelijk voor het opschonen van dit geheugen?
  • Heeft de gegevenstoegang bezwaar verwachten dat de beller (clientcode) de verantwoordelijkheid neemt voor het vrijmaken van dit geheugen?

Als het gegevenstoegangsobject wordt gemaakt wanneer de toepassing start en blijft bestaan totdat de toepassing wordt afgesloten, is de gegevenstoegang object zou eigenaar kunnen worden van dit geheugen.

Als het gegevenstoegangsobject op aanvraag wordt gemaakt en vervolgens wordt verwijderd, moet de clientcode ervoor zorgen dat dit geheugen schoon is ned up.

Definieer dit in de opmerkingen of documentatie voor het gegevenstoegangsobject, en dwing dit af in de codereview.

Opmerkingen

  • Ik hou van deze benadering. Ziet er veel flexibeler uit. Al het andere dat ik vraag, zal mijn onwetendheid over de kwestie aan het licht brengen. Moet ik DAO zelf implementeren? Zoals ik het begrijp, zou ik de Gateway en Factory moeten schrijven? Is de gateway hetzelfde als het bridge-GOF-patroon?
  • Ik zou zeggen dat de gateway in het repository-patroon een andere variatie is op het bridge-patroon. In feite lijkt het erop dat de basis van het repository-patroon een meer georganiseerd en gecoördineerd brugpatroon is dat specifiek gericht is op gegevenstoegang. En ja, je ‘ zal deze componenten waarschijnlijk zelf moeten schrijven.
  • na de links die je hebt gepost, kwam ik bij en.wikipedia.org/wiki/ODB_(C%2B%2B) , hoe past deze ODB in het plaatje? Ik kan een programma schrijven om mijn tekstinvoer naar sqllite te converteren en vervolgens ODB gebruiken voor persistentie? is dit geluid? Ik moet zeggen dat ik gewoon ODB tegenkwam en naar twee voorbeelden keek, dus dit is geen geïnformeerde opmerking.
  • Hulp bij het schrijven van code staat buiten het onderwerp van deze site, maar ik ‘ weet zeker dat je de tekst naar sqlite kunt converteren en een bestaande bibliotheek kunt gebruiken.

Answer

Een bouwer zou “gebouwd” worden in een fabrieksinvarient (methode), om daardoor een tekstbestand te lezen, je zou 1 fabriek schrijven met een methode om dat bestand te lezen. Als u nu uw model stapsgewijs moet bouwen op basis van de gegevens die u leest (denk aan geneste gegevens), zou het builderpatroon binnen de fabrieksmethode u goed van pas komen. Een enkel model is echter waarschijnlijk voldoende om uw gegevens in te laden.

Opmerkingen

  • Sommige pseudo-code kan erg nuttig zijn om te horen. Ik heb nog nooit fabrieks- of bouwerpatronen gebruikt.

Antwoord

Gezien de omvang en complexiteit van uw structuur, een bouwer lijkt het meest geschikt, vooral als uw model onveranderlijk is (of gemaakt kan worden). Je zult een nauwe koppeling tussen de bouwer en het model moeten accepteren, maar het is de moeite waard, vooral als je modellen op meerdere plaatsen moet bouwen. Een bouwer stelt je ook in staat om validatie in te kapselen.

Als uw model echter veranderlijk is en alleen op één plaats wordt gemaakt, dan is een fabriek wellicht geschikter en handiger en komt YAGNI in het spel.


In antwoord op uw andere vraag , zou je nooit onbewerkte verwijzingen in C ++ moeten opslaan. Het feit is dat std::unique_ptr geen nadelen heeft en het bagatelliseren van objectvernietiging. Als die vector buiten bereik raakt, zal het het geheugen niet vrijmaken referenties zoals geschreven, maar met een unique_ptr, zou het.

Ik zou een vector maken voor grote hoeveelheden herhalende elementen, vooral als de hoeveelheid waarschijnlijk zal veranderen, maar voor middelgrote componenten die nooit worden gedeeld, kun je ze net zo goed rechtstreeks in de struct opnemen.

Geef een reactie

Het e-mailadres wordt niet gepubliceerd. Vereiste velden zijn gemarkeerd met *