Pregunta

I would like to ask about best-practice how to hold and change a global state of application. I have a Parser (which now is a singleton) which among others have properties like

private String mode
private List<String> loadedResources

Then I have about ten modules with a very simple interface

ModuleOutput parse(String line, Path context) throws ModuleException;

Some modules do need to (some modules do not) get or set mode of the already processed file.

if (Parser.getInstance().getMode().equals("something") { ... }

or

Parser.getInstance().setMode("something");

which changes a state of the application and parser itself and other modules work with it.

Two modules (of ten) also want to add to loadedResources

if (!Parser.getInstance().getLoadedResources().contains(resource)) {
     Parser.getInstance().getLoadedResources().add(resource);
} else {
     // resource already loaded ...
}

but others not.

I do not like storing the global data in singleton and have modules reaching for it (because of tests and so on) but I do not even like that I would need to send all eventually needed information to modules which can process them... and then create big objects to return which would tell the Parser how to change the state (as well as the "globally needed" data can change over time), however it would probably be much cleaner. What are your suggestions? Thanks in advance.

¿Fue útil?

Solución

I would just create a ParsingContext object, and move there everything that can be changed/shared by the modules. Then I would be passing this ParsingContext into each call of "ModuleOutput parse". That means that ParsingContext would contain the state of the parsing and resources. This way you cleanly define what can the modules share and change. Note that instance of this ParsingContext is far cleaner to Singleton from the scope point of view. The scope of singleton is global variable. The scope of ParsingContext is only calls of the "parse" method and Parser itself.

To answer the question on higher level. I think there is no best practice to share global state of the application. The aim is always to have no state if possible.

You may ask how to share state between plugin and the framework. My opinion is that you should use an object that is shared by smallest ammount of the objects as possible (plugins and the framework entry point). No Singleton, because by creating Singleton, you expose this state to the rest of the application as well.

Another thought is that I would minimize "coupling" and explicit state. For example your implementation of modules contain method getPriority(). This way, you are able to order your plugins. This forces developer to consider all the possible modules (even those he didn't implement) and set the priority accordingly. Instead I would go for some sort of implicit ordering. For example by creating several well-defined phases of the parser and require the developer to "bind" the module to this phase. By doing this the developer only considers small amount of predefined parser phases instead of arbitrary ordering of modules.

Also, looking at the implementation, I would consider implementing State design pattern for the Parser class and Chain of Responsibility pattern for the modules. However it's hard to tell whether it is achieveable, I would have to dig deeper into the source code and write some unit tests to test out the parsing functionality.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top