Should I worry that passing in an XML configuration file class view violates the demeter law?

I use a tool to automatically create a class view of a hierarchically organized XML file. An XML file is a settings file in which my application must have access (read-only).

If I pass the top level node (e.g. AppSettings ) to a class that needs to access one or more parameters, I can easily get code that looks something like this:

 var windowSize = AppSettings.Views.Windows.Dashboard.Size; 

This seems to be a serious violation of the Law of Demeter, but I wonder if I will take care. I could make great efforts only to pass only the exact settings that I need for each class, but I had problems with how these multiple points would interfere with me in this case.

Tightly connects my code with my XML file, which may cause maintenance problems or other problems in the future, or is this an example where it makes sense to not religiously follow the design principle of OOP?

+4
source share
4 answers

Yes, you have to take care, for a very pragmatic reason!

The classes in which you want to use your settings should absolutely not depend on how these settings are saved.

Imagine that in the future you want to support multiple themes for your application. As a result, you will have not one, but many opportunities for the size of your panel, for example:

 AppSettings.Views.ThemeA.Windows.Dashboard.Size; AppSettings.Views.ThemeB.Windows.Dashboard.Size; 

Only one thing is still required in your user interface class, the value for its windowSize variable, it does not need to know which theme is currently being used.

It’s true, wherever you have the XML interface, you don’t want it to depend on the schema everywhere in your code, but only in one central place.

For example, you can put the settings in Map for internal use, for example:

 public class SettingsReader { public static final String VIEW_WINDOW_DASHBOARD_SIZE = "Views.Windows.Dashboard.Size"; private Map settings = new Hashmap(); public SettingsReader(AppSettings appSettings) { settings.put(VIEW_WINDOW_DASHBOARD_SIZE, appSettings.Views.Windows.Dashboard.Size); } public String getSettingValue(String key) { return settings.get(key); } } 

Then you have only one place to refactor to support the topic, for example:

 public class SettingsReader { public static final String VIEW_WINDOW_DASHBOARD_SIZE = "Views.Windows.Dashboard.Size"; private Map settings = new Hashmap(); public SettingsReader(AppSettings appSettings, String theme) { settings.put(VIEW_WINDOW_DASHBOARD_SIZE, appSettings.Views + theme + Windows.Dashboard.Size); } public String getSettingValue(String key) { return settings.get(key); } } 

The last note, simply because my combination of pseudo-codes and java code can be confusing for people, especially appSettings.Views + theme + Windows.Dashboard.Size : when working with the XML interface, xPath is usually very useful even when working with objects thanks to the good JXPath library (for java I do not know other languages).

+6
source

If you spit out dumb data, then there is no better way to do this.

I would like to try to work on a solution where you could push and pop contexts.

 PushContext(AppSettings) // do child contexts PushContext(Views) // more child contexts PushContext(Windows) // etc. PopContext() PopContext() PopContext() 

Usually different Pushes will be in different functions or files, but shown here for illustration. Regardless of whether you enter the Views context, you simply parse it as if you were at the root of the object.

If it's DumbData, you can also just pass in the type of thing that represents "Views" for the code that parses it. At the top level, your code will look like this:

 views.ParseSettings(AppSettings.Views); locale.ParseSettings(AppSettings.Locale); network.ParseSettings(AppSettings.Network); 

It will certainly be “cleaner” from LOD POV, but maybe it’s not worth it for the amount of settings you have. However, with the depth of visibility, it is probably implied that you have many settings, so dividing them into areas of responsibility (for loading and saving settings) is probably reasonable.

+1
source

All things are relative, it really depends on the size of the project, and if you care about maintenance.

If you care about maintenance, you do not want to impose any restrictions that your configuration source places on the rest of your code base.

The best way to achieve this is with code for the interfaces and hiding your implementation behind it. So your code has a contract with your configuration interface and doesn't care how the actual configuration loads.

 public interface IConfiguration { Size ViewSize { get; } } public class AppSettingsConfiguration : IConfiguration { public Size ViewSize { return AppSettings.Views.Windows.Dashboard.Size; } } 

All consumer codes must be encoded in accordance with the IConfiguration interface. This means that you can change the way you get your configuration with minimal impact.

+1
source

Automatic generations can be a problem for large projects.

If you use the generated code from one place (for example, one package), maybe there is no problem.

If you use the code:

 var windowSize = AppSettings.Views.Windows.Dashboard.Size; 

in many places, you might want to hide part of this connection by creating a method on AppSettings :

 getSize() { return Views.Windows.Dashboard.Size; } 

But if you need to do this with all classes, perhaps this is not viable.

The best solution depends on the size of your project (and if it intends to grow), the time you need to make, and the amount of code generated.

0
source

All Articles