[1] Add virtual "getters" & "setters" to your properties, I had to hack another control library, because I need this feature:
abstract class Control
{
// internal fields for properties
protected int _Width;
protected int _Height;
protected int _BackColor;
protected int _X;
protected int _Y;
protected bool Visible;
protected int BorderWidth;
protected int BorderColor;
// getters & setters virtual !!!
public virtual int getWidth(...) { ... }
public virtual void setWidth(...) { ... }
public virtual int getHeight(...) { ... }
public virtual void setHeight(...) { ... }
public virtual int getBackColor(...) { ... }
public virtual void setBackColor(...) { ... }
public virtual int getX(...) { ... }
public virtual void setX(...) { ... }
public virtual int getY(...) { ... }
public virtual void setY(...) { ... }
public virtual int getBorderWidth(...) { ... }
public virtual void setBorderWidth(...) { ... }
public virtual int getBorderColor(...) { ... }
public virtual void setBorderColor(...) { ... }
public virtual bool getVisible(...) { ... }
public virtual void setVisible(...) { ... }
// properties WITH virtual getters & setters
public int Width { get getWidth(); set setWidth(value); }
public int Height { get getHeight(); set setHeight(value); }
public int BackColor { get getBackColor(); set setBackColor(value); }
public int X { get getX(); set setX(value); }
public int Y { get getY(); set setY(value); }
public int BorderWidth { get getBorderWidth(); set setBorderWidth(value); }
public int BorderColor { get getBorderColor(); set setBorderColor(value); }
public bool Visible { get getVisible(); set setVisible(value); }
// other methods
public Rectangle ClipRectange { get; protected set; }
abstract public void Draw();
} // class Control
/* concrete */ class MyControl: Control
{
public override bool getVisible(...) { ... }
public override void setVisible(...) { ... }
} // class MyControl: Control
I know this suggestion is more "verbose" or complex, but, its very useful in real world.
[2] Add an "IsEnabled" property, do not confuse with "IsReadOnly":
abstract class Control
{
// internal fields for properties
protected bool _IsEnabled;
public virtual bool getIsEnabled(...) { ... }
public virtual void setIsEnabled(...) { ... }
public bool IsEnabled{ get getIsEnabled(); set setIsEnabled(value); }
} // class Control
Means you may have to show your control, but don't show any information.
[3] Add a "IsReadOnly" property, do not confuse with "IsEnabled":
abstract class Control
{
// internal fields for properties
protected bool _IsReadOnly;
public virtual bool getIsReadOnly(...) { ... }
public virtual void setIsReadOnly(...) { ... }
public bool IsReadOnly{ get getIsReadOnly(); set setIsReadOnly(value); }
} // class Control
That means the control can display information, but cannot be changed by the user.
You may start from a different point of view to apply "Single Responsibility Principle" here. What you have shown to us is (more or less) only the data model of your application. SRP here means: make sure your data model is responsible only for keeping data - no less, no more.
So when you are going to read your XML file, create a data model from it and write SQL, what you should not do is implement anything into your Table
class which is XML or SQL specific. Your want your data flow look like this:
[XML] -> ("Read XML") -> [Data model of DB definition] -> ("Write SQL") -> [SQL]
So the only place where XML specific code should be placed is a class named, for instance, Read_XML
. The only place for SQL specific code should be a class like Write_SQL
. Of course, maybe you are going to split those 2 tasks into more sub-tasks (and split your classes into multiple manager classes), but your "data model" should not take any responsibility from that layer. So don't add a createStatement
to any of your data model classes, since this gives your data model responsibility for the SQL.
I don't see any problem when you are describing that a Table is responsible for holding all it's parts, (name, columns, comments, constraints ...), that is the idea behind a data model. But you described "Table" is also responsible for the memory management of some of its parts. That's a C++ specific issue, which you would not face so easily in languages like Java or C#. The C++ way of getting rid of those responsibility is using smart pointers, delegating ownership to a different layer (for example, the boost library or to your own "smart" pointer layer). But beware, your cyclic dependencies may "irritate" some smart pointer implementations.
Something more about SOLID: here is nice article
http://cre8ivethought.com/blog/2011/08/23/software-development-is-not-a-jenga-game
explaining SOLID by a small example. Let's try to apply that to your case:
you will need not only classes Read_XML
and Write_SQL
, but also a third class which manages the interaction of those 2 classes. Lets call it a ConversionManager
.
Applying DI principle could mean here: ConversionManager should not create instances of Read_XML
and Write_SQL
by itself. Instead, those objects can be injected through the constructor. And the constructor should have a signature like this
ConversionManager(IDataModelReader reader, IDataModelWriter writer)
where IDataModelReader
is an interface from which Read_XML
inherits, and IDataModelWriter
the same for Write_SQL
. This makes a ConversionManager
open for extensions (you very easily provide different readers or writers) without having to change it - so we have an example for the Open/Closed principle. Think about it what you will have to change when you want to support another database vendor -ideally, you don't have to change anything in your datamodel, just provide another SQL-Writer instead.
Best Answer
Regarding point 2: The constraints are stored in the Node that you add to the GridPane. Every Node (and every visible object in the scene graph descends from Node), has a HashMap called "properties".
Application developers can put arbitrary data in there if you wish, but each layout class also defines some properties that are used to control layout. So when you put a Node into a GridPane, if you want it to span multiple columns you set at "gridpane-column-span" property on the node, with the appropriate value.
Conceptually, I think of it a bit like CSS. You add a CSS class to a node, and that affects it's styling and layout based on where it is inserted into the scene graph (or document flow, for HTML). Similarly, you add a layout constraint to a node, and the layout component you insert the node into interprets those constraints when laying out it's child nodes.
I actually really like the model that JavaFX uses - any arbitrary node can contain any arbitrary layout constraint. It's up to the layout component to interpret them. That means you can create your own layout components to do custom layouts, and if necessary create your own custom layout constraints, and they will all work properly with any existing Node.
On point 3: the BorderPane is different in that there are very explicitly 5 "zones" into which you could place nodes. So I guess they gave it a "proper" API for convenience.