What
understandable to humans, thereby maintainable. It does not effect the
functionality. In fact it should not effect the functionality.
Why
it difficult to understand. Refactoring also makes it easier to add new
functionality and for others to utilize it in their code. So it is
important to refactor code.
How
- file (of type — xml | property | json ..) and
- property names of values to extract
- dynamic map (replaces the dynamic map key (if) found in the property values, with corresponding map values.)
- Extract properties from given file (Extract method should support file types like — xml, property, json.)
- Replace placeholders with dynamic values
- Procedural
- Object Oriented
- Functional
Procedural
- main method reads file content and calls extract method.
- extract method parses the content based on type, using an if condition that calls the
specific parse method and extracts the properties and returns these
properties. - main method now call replace method to replace the placeholders with its dynamic values.
- Add another if else block in ‘extract’ method.
- Ease to write when extensions/modification are limited.
- This causes a bottleneck on the extract method. In a team of developers, constant updates to a single entity by all the developers will result in merge conflicts.
- The extract method will increase in size as extensions are added. This will make it less readable.
- Having business logic interspersed with your I/O operations will result in unit test cases that will need actual resource versus a mock.
- The resource I/O operations code cannot be reused in other parts of the code as it is tightly coupled to the business logic.
Object Oriented
ResourceHandler
. This defines methods extract () and replace ()
. extract()
needs to allow for custom implementation to support future enhancements. While replace()
needs to be common logic to be used irrespective of the custom extract implementation.- handle(). This contains the business logic, which for our purposes can be as simple as:
public Map handle() {
extract();
replace();
}
- protected abstract void extract() - contains logic to extract properties from file
- private void replace() - contains logic to replace properties from dynamic map
- XmlResource
- JsonResource
- PropertiesResource
- This structure allows to add new extensions without modifying the base ‘handler’ method, that is invoked to extract and replace properties. So child classes cannot corrupt the business logic.
- There is no more a bottleneck on the extract method as it was with the
procedural code. Now each custom extract method has it’s own
implementation in it’s own class. This will reduce the possible
merge-conflicts between multiple developers working on different custom extract implementation. - Object ‘ResourceHandler’ is closed for modification while being open for extension. This is one of the pillars of the SOLID
principle. For example if, ResourceHandler is shared as jar, it can
still be extended to support additional resource types, but the
underlying base logic is still restricted.
- The IO logic to extract properties for given type, is still coupled to the
business logic, due to the structure of the Base class. As the only
public method is handle, which calls the extract method and replace
method. So we cannot use extract in isolation.
Functional
specific implementation in to the class ExtractReplace that encapsulates
the business logic as follows:
class ExtractReplace {
private Map extracted_properties;
public ExtractReplace(File file, List properties,Map dynamic, ResourceHandler handler) {
Map props = handler.extract(file, properties);
extracted_properties = replace(props, dynamic);
}
private Map replace(Map properties, Map dynamic) {...}
@Override
public String toString() {
return extracted_properties.toString();
}
}
implementation of ResourceHandler Interface via the Lambda construct as
seen. This demonstrates the capability to define adhoc anonymous
implementations which do not need to be housed in a type of it’s own,
thereby reducing structural complexity.
class ExtractReplace{
private Map extracted_properties;
static class Builder {
private File file;
private List properties;
private Map dynamic;
private ResourceHandler handler;
static Builder getInstance(File file, List properties, Map dynamic){
this.file = file;
this.properties = properties;
this.dynamic = dynamic;
if (file.getName().endsWith(".json"))
this.handler = new JsonHandler();
else if(file.getName().endsWith(".xml"))
this.handler = new XmlHandler();
}
public ExtractReplace build() {
new ExtractReplace(this);
}
}
private ExtractReplace(Builder builder) {
Map props = builder.handler.extract(builder.file, builder.properties);
}
private Map replace(Map properties, Map dynamic) { ... }
@Override
public String toString(){
return extracted_properties.toString();
}
}
- Over and above the advantages that are provided by Object Oriented approach, this also separates the extraction logic from the business logic. Hence you can reuse this extract method.
- You can follow a structured approach by creating different implementation classes of ResourceHandler, which can be resused.
- You can also follow a adhoc anonymous approach using Lambda functions. This is useful if you are not reusing the extraction logic else where and want to reduce the structural complexity.
Final Notes
implementation. As you can see the code is extensible, easier to
consume, understand and extend.