О компонентах и интерфейсах. Java

от автора

Вступление

В предыдущей статье я написал о разных способах оформления интерфейсов к компонентам и сокрытия их реализации в 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-ник с реализацией.

Резюме

Используя предложенный подход можно получить много плюсов в разработке. Ну взять хотя бы такую ситуацию. Мы ведем разработку сложной системы, в которой есть много взаимосвязанных компонентов.
Для параллельной разработки очень удобно разработать интерфейсы и сложить их в отдельные библиотеки. Тогда можно легко распараллелить разработку самих компонентов, их тестов, и компонентов, которые с ними связаны. Все будет прекрасно компилиться и собираться, хотя реализация при этом может быть еще не готова.

ссылка на оригинал статьи http://habrahabr.ru/post/161085/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *