Use XML if your data is a lot more nested and complicated than this.
In this particular case I would suggest using JSON (if you cannot use relational databases as you have mentioned yourself).
As for learning JSON it is very simple. There is nothing to it but learning the structure. Once you got the structure down you can pretty much create any type of data you want.
Here are some references to get started with JSON:
1) http://www.w3schools.com/json/
2) http://json.org/
3) https://code.google.com/p/json-simple/wiki/EncodingExamples
To start things off simple, you should create a simple loop taking each "logic" and performing each operation from start to finish. So have add a dependency for your EnrichmentDBService and directly request the logics for your input.
Then for each logics, you apply the change. To apply each change, you should use an enum or some sort of direct mapping between the type "javascript" or "groovy" and the appropiate ScriptEngine instance pertaining to that particular language.
Using this mapping, grab the correct ScriptEngine instance and execute its code. What gets returned you must set to an object using "key" value presumably.
Once you've finished all logics in this way you're done! Return the finished object.
If order is not crucial and there are many logics, you could consider using a thread pool to handle the tasks. I wouldn't suggest Chain of Responsibility because it seems like the actual work being done is the same here. However, I fear that what they want from you is that you interpret the values returned as a sort of mask for you to apply to the actual data.
If, for instance, '888-888-8888' is a mask for applying a number, then you'd need some sort of baseline rule for this. Does this mean whenever you see '8', you represent this as a digit? What about '1234 new address'? Is '1234' supposed to be replaced with the street number? Why wouldn't you interpret this as a literal '1234'?
These questions you cannot answer yourself. The one providing the specifications should tell you this, and you need to look for potential inconsistencies and bring them to the attention of the one performing the analysis. Do this until all cases are covered and there are no inconsistencies before you begin coding.
Good luck!
Best Answer
This is actually a really important question and it is often done wrongly as it is not given enough importance even though it is a core part of pretty much every application. Here are my guidelines:
Your config class, which contains all the settings should be just a plain old data type, struct/class:
It should not need to have methods and should not involve inheritance (unless it is the only choice you have in your language for implementing a variant field - see next paragraph). It can and should use composition to group the settings into smaller specific configuration classes (e.g. subConfig above). If you do it this way it will be ideal to pass around in unit tests and the application in general as it will have minimal dependencies.
You'll likely need to use variant types, in case configs for different setups are heterogeneous in structure. It is accepted that you'll need to put a dynamic cast in at some point when you read the value to cast it to the right (sub-)configuration class, and no doubt this will be dependent on another config setting.
You should not be lazy about typing in all the settings as fields by just doing this:
This is tempting as it means you can write a generalised serialisation class that doesn't need to know with what fields it's dealing, but it is wrong and I'll explain why in a moment.
Serialisation of the config is done in a completely separate class. Whatever API or library you use to do this, the body of your serialisation function should contain entries that basically amount to being a map from the path/key in the file to the field on the object. Some languages provide good introspection and can do this for you out of the box, others you'll have to explicitly write the mapping, but the key thing is you should only have to write the mapping once. E.g. consider this extract I adapted from the the c++ boost program options parser documentation:
Note that the last line basically says "optimization" maps to Config::opt and also that there is a declaration of the type that you expect. You want the reading of the config to fail if the type is not what you expect, if the parameter in the file is not really a float or an int, or doesn't exist. I.e. Failure should occur when you read the file because the problem is with the format/validation of the file and you should throw an except/return code and report the exact problem. You should not delay this to later in the program. That's why you should not be tempted to have a catch all Dictionary style Conf as mentioned above which won't fail when the file is read - as casting is delayed until the value is needed.
You should make the Config class read-only in some fashion - setting the contents of the class once when you create it and initialise it from file. If you need to have dynamic settings in your application that change, as well as const ones that don't, you should have a separate class to handle the dynamic ones rather than trying to allow bits of your config class to be not read-only.
Ideally you read in the file in one place in your program i.e. you only have one instance of a "
ConfigReader
". However, if you're struggling with getting the Config instance passed around to where you need it, it is better to have a second ConfigReader than it is to introduce a global config (which I'm guessing is what the OP means by "static"), which brings me to my next point:Avoid the seductive siren song of the singleton: "I'll save you having to pass that class class around, all your constructors will be lovely and clean. Go on, it'll be so easy." Truth is with a well designed testable architecture you'll hardly need to pass the Config class, or parts of it down through that many classes of your application. What you'll find, in your top level class, your main() function or whatever, you'll unravel the conf into individual values, which you'll provide to your component classes as arguments which you then put back together (manual dependency injection). A singleton/global/static conf will make unit testing your application much harder to implement and understand - e.g. it will confuse new developers to your team who will not know they have to set global state to test stuff. It will also prevent you from running unit tests in parallel.
If your language supports properties you should use them for this purpose. The reason is it means it will be very easy to add 'derived' configuration settings that depend on one or more other settings. e.g.
If your language doesn't natively support the property idiom, it may have a workaround to achieve the same effect, or you simply create a wrapper class that provides the bonus settings. If you can't otherwise confer the benefit of properties it is otherwise a waste of time to write manually and use getters/setters simply for the purpose of pleasing some OO-god. You'll be better off with a plain old field.
You might need a system to merge and take multiple configs from different places in order of precedence. That order of precedence should be well-defined and understand by all developers/user e.g. consider windows registry HKEY_CURRENT_USER/HKEY_LOCAL_MACHINE. You should do this functional style so that you can keep your configs read only i.e.:
rather than:
I should finally add that of course if your chosen framework/language provides its own built-in, well-known configuration mechanisms you should consider the benefits of using that instead of rolling your own.
So. Lots of aspects to consider - get it right and it will profoundly affect your application architecture, reducing bugs, making things easily testable and sort of forcing you to use good design elsewhere.