Recently, frameworks that allow a developer to implement functionality in a free form without having to inherit from interfaces appear very often. Examples can be seen in such as EJB 3, JUnit 4, JSF 2, Spring Framework 3, etc.
Under the cut will find a discussion of both approaches.
So, recently I had to implement a framework with a dynamic (run-time) connection of services that would return properties (a key-value map). But the problem was that for the users of this framework it might not be convenient to use only one method to form this map, possible scenarios could be like a transfer to the core object or a primitive value that should be automatically added to the final map.
One solution was to create interfaces with methods for each of the scenarios, or one interface with all possible variants of the methods. This approach has a drawback - the low readability of such code and excessive complexity, as well as the need to memorize many details of the framework and the inability to create one class to transfer all the necessary information to the kernel.
Another solution is to give the programmer complete freedom of action, i.e. eliminate the need to implement any interface, provide the ability to name methods in a free style and not regulate the return type. And the call of methods and data acquisition happens through reflexion (reflection).
')
So, I will give a couple of examples:
1. Implementation using the first approach.
Interfaces for implementation:
public interface ServiceAsMap { Map<String, Object> getValue(); } public interface ServiceAsObject { Object getValue(); }
And an example of implementations:
public class UserData implements ServiceAsMap { public Map<String, Object> getValue() { Map<String, Object> result = new HashMap<String, Object>(); result.put("user.home", "/home/dev"); result.put("user.name", "dev"); return result; } } public class UserDataObj implements ServiceAsObject { public Object getValue() { return context.getUser(); } }
As seen in the example, the method is not very convenient and not intuitive.
Now let's consider the second implementation example (let me remind you that no interfaces need to be implemented):
@Service public class UserData { public User getUser() { return context.getUser(); } public Map<String, String> getProperties() { Map<String, Object> result = new HashMap<String, Object>(); result.put("user.home", "/home/dev"); result.put("user.name", "dev"); return result; } @PropertyName("age") public int getUserAge() { return 25; } }
As you can see from the example, the names of the methods describe the actions performed and in the class of arbitrary methods, which makes it possible to realize everything you need in one place.
In the example, I used anotations that carry additional information for the kernel.
The only drawback of this approach is the use of a reflection that is not very smart, and the second code for executing and receiving data from such a structure is rather cumbersome, therefore potentially more errors can be (if you do not write the tests well).
Dear Hobrazhiteli, I am interested in discussing with you this question about the areas of application of the second approach and other nuances that can happen, and in general, your vision of a similar situation.
I do not advocate the use of freedom only through solving, there are many examples when inheriting from an interface is the only right decision and reflexion will only interfere.Thanks for attention.