About components and interfaces. Java

Introduction

In a previous article, I wrote about different ways of designing interfaces to components and hiding their implementation in C ++.
In this article, I will briefly describe how to separate an interface from an implementation in Java and hide an implementation.
I will not consider the components of different Java EE there, I will consider the most common jar-nicknames.
So.

What we have

There are no functions in Java, there are only classes; accordingly, classes are exported to Java.
For a class to be exported, it must be declared as public.
So, to write the library interface, you need, in fact, the library itself and the declaration of 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 then can use.
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, all public methods of the library classes remain available to us. For example, if our jar nickname consists of several packages (Package), and we used method calls between them, it turns out that the developer using your library will be able to access methods that you did not want to open for use . 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, he will use one of the most convenient Java tools - 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 the component instances by the factory, which implements the following interface:
package com.iface;
public interface Factory {
    public Component getComponent();
}

We create a factory instance statically. We keep the link to the factory. We style 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.
We make out all the interfaces in one library, we completely complete the implementation in another library. In a single interface library class, we statically obtain the factory implementation class by its name. Then by name we get the static method of this class, which creates an instance of the factory for us. We call this method and save the factory instance. Calling getInstance () will just give us this instance. Many such implementations can be invented, but the general idea is clear from here. A library of interfaces is sufficient for development and compilation. Also, for ease of study, you can fully disclose the source code for the interface for developers.
To run the application, you only need to tell the java machine that in addition to loading the jar nickname with the interfaces, you need to load the jar nickname with the implementation.

Summary

Using the proposed approach, you can get many advantages in development. Well, take at least such a situation. We are developing a complex system in which there are many interconnected 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 compile and assemble perfectly, although the implementation may not yet be ready.

Also popular now: