Introduction
In the previous
article, I wrote about the different ways of designating interfaces to components and hiding their implementation in C ++.
In this article I will tell you briefly how to separate the interface from the implementation in Java and hide the implementation.
I will not consider components of Java EE different there, I will consider the most common jar-nick libraries.
So.
What we have
There are no functions in Java, there are only classes, respectively, classes are exported to Java.
For a class to be exported, you need to declare it as public.
So, to write the library interface, you need, in fact, the library itself and the declaration of the exported classes as public.
Something like that:
package com.test; public class Test { private final String string = "Test"; public String GetString() { return string; } }
Compile, we get a jar-nickname, which can then be used.
Well, something like this:
')
package com.sample; import com.test.Test; public class NewClass { private static Test test = new Test(); public static void Main() { System.out.println( test.GetString() ); } }
What are the disadvantages of this approach?
Although the compiled jar-nickname hides the implementation, but we still have access to all public methods of the library classes. For example, if our jar-nickname consists of several packages (Package), and we used method calls between them in it, it turns out that a developer using your library will be able to access methods that you didn’t want to open . Those. we again open the implementation. Also, we do not have interface texts that could be given to developers for study.
What to do in this case?
To completely hide the implementation will use one of the most convenient tools of Java - interfaces and reflection.
What happened
For example, we have a component with this interface:
package com.iface; public interface Component { public void Method1(); public void Method2(); public void Method3(); public void Method4(); }
We create instances of the component with a factory that implements the following interface:
package com.iface; public interface Factory { public Component getComponent(); }
We create a factory instance statically. Link to the factory save. We make the FactoryCreator class as follows:
package com.iface; import java.lang.reflect.Method; public class FactoryCreator { private static Factory instance = null; private static Throwable ex = null; static { try { Class cl = Class.forName("com.test.FactoryImpl"); Method method = cl.getMethod("createInstance", new Class[0]); Object obj = method.invoke(cl, new Object[0]); if(obj instanceof Factory) { instance = (Factory) obj; } ex = new Exception("Cannot init Factory instance."); } catch (Throwable throwable) { ex = throwable; } } public static Factory getInstance() throws Throwable { if(instance != null) { return instance; } throw ex; } }
So, how it works and what we did.
All interfaces are designed in one library, the implementation is completely made out in another library. In the only class of the library of interfaces, we statically obtain the class of implementation of the factory by its name. Then by name we get a static method of this class, which creates an instance of the factory for us. Call this method and save an instance of the factory. Calling getInstance () will simply give us this instance. Many such implementations can be invented, but the general idea is clear from here. A library of interfaces is enough to develop and compile. Also, for convenience of studying, it is possible to open completely for developers source codes of interfaces.
To start the application, you only need to specify the java-machine, that in addition to loading the jar-nick with interfaces, you need to load the jar-nick with the implementation.
Summary
Using the proposed approach, you can get a lot of advantages in the development. Well, take at least such a situation. We are developing a complex system in which there are many interrelated components.
For parallel development, it is very convenient to develop interfaces and put them into separate libraries. Then you can easily parallelize the development of the components themselves, their tests, and the components that are associated with them. Everything will be fine to compile and assemble, although the implementation may not be ready yet.