It's always difficult to judge an approach based on a screencast, since the problems picked for demos are typically so small that applying principles like SOLID quickly makes it look like the solution is completely overengineered.
I'd say SOLID principles are almost always useful. Once you become proficient with them, using them doesn't seem like something you have to deliberately think about. It just becomes natural. I've seen many throwaway one-off apps become more than that, so I'm now afraid of saying that I'm going to throw something away, because you just never know.
The approach I usually take is that if I'm writing a simple app for a particular task, I'll sometimes forgo big name principles in favour of a few lines of code that work. If I find that I'm coming back to that app to make further changes, I will take the time to make it SOLID (at least somewhat, since a 100% application of the principles is rarely feasible).
In bigger apps, I start small and as the program evolves, I apply SOLID principles where possible. This way I don't attempt to design the whole thing upfront down to the last enum. That, to me, is the sweet spot where YAGNI and SOLID coexist.
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
S = Single Responsibility Principle
So I'd expect to see a well organised folder/file structure & Object Hierarchy. Each class/piece of functionality should be named that its functionality is very obvious, and it should only contain logic to perform that task.
If you saw huge manager classes with thousand of lines of code, that would be a sign that single responsibility wasn't being followed.
O = Open/closed Principle
This is basically the idea that new functionality should be added through new classes that have a minimum of impact on/require modification of existing functionality.
I'd expect to see lots of use of object inheritance, sub-typing, interfaces and abstract classes to separate out the design of a piece of functionality from the actual implementation, allowing others to come along and implement other versions along side without affecting the original.
L = Liskov substitution principle
This has to do with the ability to treat sub-types as their parent type. This comes out of the box in C# if you are implementing a proper inherited object hierarchy.
I'd expect to see code treating common objects as their base type and calling methods on the base/abstract classes rather than instantiating and working on the sub-types themselves.
I = Interface Segregation Principle
This is similar to SRP. Basically, you define smaller subsets of functionality as interfaces and work with those to keep your system decoupled (e.g. a
FileManager
might have the single responsibilty of dealing with File I/O, but that could implement aIFileReader
andIFileWriter
which contained the specific method definitions for the reading and writing of files).D = Dependency Inversion Principle.
Again this relates to keeping a system decoupled. Perhaps you'd be on the lookout for the use of a .NET Dependency Injection library, being used in the solution such as
Unity
orNinject
or a ServiceLocator system such asAutoFacServiceLocator
.