A follow up to another question (Making a design decision about reading model data from an input file).
I wish to ask another question regarding builder or factory pattern. (I read that builder is more complex than factory, and I may not need to use builder for now). So here is the data I have to read:
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
...
There are many more tables like these. Some tables have parent, child relationships (Each coordinate system has grid line of its own). I have made structures that represent each table, like this:
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;
};
these data structures are incorporated to the model class, which will make a huge class since there are 50 odd data structures like this:
class Model
{
private:
ActiveDOF activeDOF;
CoordinateSystem coordinateSystem;
....
public:
Model() {} ...
}
each table has to have its set method and get method. This design worries me because if I decide to change it, it is going to be very time consuming. I appreciate any suggestion. I think the information here also will put the earlier question in a better light.
Now, I don't know where the builder or factory method should go, given the situation. I looked at some code and UML charts but I was not able to understand how to implement the factory, or builder to make structures required for my design. I have to have access to each table by name, because they might be subject to change inside the model, so for the time being I am avoiding making each of them a subclass of a virtual base class, so that I can store them in a container.
Also, does it make sense that instead of declaring an instance of the data struct, I should keep a pointer to them ?
if all data structures are derived from a virtual base class called Record, then the model will be something like this:
class Model
{
private:
ActiveDOF* activeDOF;
CoordinateSystem* coordinateSystem;
....
std::Vector<Record*> data;
public:
Model() {} ...
}
I think it is extra work to allocate, dislocate memory for them, but it can help in managing the data and keep extra typing ? am I right in assuming that ?
Best Answer
You are trying to solve a data access problem, and it requires a data access solution.
First, let's examine your proposed solutions, and why they don't solve the problems you have.
The Factory Pattern
The Factory Design Pattern (also called the Factory Method Pattern) is useful when you want to utilize polymorphism and you want to separate object construction completely from the implementing classes.
Wikipedia:
(emphasis, mine)
And further down it elaborates on the problems this pattern solves:
The problems you describe in your question are different from these. You aren't dealing with sub classes or polymorphism. The factory pattern isn't a solution.
The Builder Pattern
The Builder Pattern (from Wikipedia):
(emphasis, mine)
Here, again, we see a mismatch between the problem you are describing and the intended use of this pattern.
(emphasis, mine)
The key here is the construction of an object (an object = 1 object) is complex. This could be due to a large number of constructor arguments, or due to the complexity of assembling its dependencies.
Individually, each struct or class is pretty simple, so the builder pattern is not a good fit either.
Data Access Patterns (The Solution)
The problems you are actually trying to solve are how to perform data access, and you may be looking for a Data Access Object.
In your case, replace "database" with "text file". A related design pattern is the Repository Design Pattern, which breaks data access into 3 main solutions:
Ownership of Memory
Since you don't have the advantage/limitation of automatic management of memory, ownership of the memory allocated to these objects and structs is definitely a concern. Here you must make a decision:
If the data access object is created when the application starts, and lives until the application is shut down, the data access object could take ownership of this memory.
If the data access object is created on demand, and then disposed of, client code needs to make sure this memory is cleaned up.
Define this in the comments or documentation for the data access object, and enforce this in code review.