Вступление
В предыдущей статье я написал о разных способах оформления интерфейсов к компонентам и сокрытия их реализации в C++.
В этой статье расскажу вкратце, как в Java отделить интерфейс от реализации, а реализацию скрыть.
Я не буду рассматривать компоненты разных там Java EE, я рассмотрю самые обычные jar-ники-библиотеки.
Итак.
Что мы имеем
В Java нет функций, есть только классы, соответственно в Java экспортируются классы.
Чтобы класс был экспортирован, нужно чтобы он был объявлен как public.
Итак, чтобы написать интерфейс библиотеки, нужна, собственно, сама библиотека и объявление экспортируемых классов как public.
Что-то типа того:
package com.test;
public class Test {
private final String string = "Test";
public String GetString()
{
return string;
}
}
Компилируем, получаем jar-ник, который затем может использовать.
Ну как-то так:
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() );
}
}
Какие в таком подходе недостатки.
Хоть скомпилированный jar-ник и скрывает реализацию, но у нас остаются доступными все public-методы классов библиотеки. Например если у нас jar-ник состоит из нескольких пакетов (Package-й), и мы использовали в нем вызовы методов между ними, то получится, что разработчик, использующий вашу библиотеку, сможет получить доступ к методам, которые вы не хотели открывать для использования. Т.е. мы опять же открываем реализацию. Так же мы не имеем текстов интерфейсов, которые бы могли отдать разработчикам для изучения.
Что делать в таком случае?
Чтобы полностью скрыть реализацию воспользуется одними из самых удобных инструментов Java — интерфейсами и рефлексией.
Что получилось
Например, у нас есть компонент с таким интерфейсом:
package com.iface;
public interface Component {
public void Method1();
public void Method2();
public void Method3();
public void Method4();
}
Экземпляры компонента создаем фабрикой, которая реализует вот такой интерфейс:
package com.iface;
public interface Factory {
public Component getComponent();
}
Экземпляр фабрики создаем статически. Ссылку на фабрику сохраняем. Оформляем классом FactoryCreator следующим образом:
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;
}
}
Итак, как это работает и что у нас получилось.
Все интерфейсы оформляем в одной библиотеке, реализацию полностью оформляем в другой библиотеке. В единственном классе библиотеки интерфейсов статически получаем класс реализации фабрики по его имени. Затем по имени же получаем статический метод этого класса, который создает нам экземпляр фабрики. Вызываем этот метод и сохраняем экземпляр фабрики. Вызов getInstance() просто отдаст нам этот экземпляр. Можно изобрести много таких реализаций, но общая идея отсюда понятна. Для разработки и компиляции достаточно библиотеки интерфейсов. Так же, для удобства изучения, можно полностью раскрыть для разработчиков исходники интерфейсов.
Для запуска приложения нужно только указать java-машине, что помимо загрузки jar-ника с интерфейсами, нужно загрузить jar-ник с реализацией.
Резюме
Используя предложенный подход можно получить много плюсов в разработке. Ну взять хотя бы такую ситуацию. Мы ведем разработку сложной системы, в которой есть много взаимосвязанных компонентов.
Для параллельной разработки очень удобно разработать интерфейсы и сложить их в отдельные библиотеки. Тогда можно легко распараллелить разработку самих компонентов, их тестов, и компонентов, которые с ними связаны. Все будет прекрасно компилиться и собираться, хотя реализация при этом может быть еще не готова.
Автор: alex_shabalin