Уровень абстракции (Java)

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

Другие приложения смогут взаимодействовать с моим проектом и совершать вызовы, которые в конечном итоге сведутся к передаче на сервер, который я использую.

Вот в чем проблема. У меня не так много опыта в этой области, и я действительно не знаю, как сделать так, чтобы это не превратилось в бутерброд смерти. Вот цепочка примерно того, как это должно выглядеть (и чего я пытаюсь достичь).

/*
Software that is dependent on mine
    |
Public API layer (called by other software)
    |
Abstraction between API and my own internal code (this is the issue)
    |
Internal code (this gets replaced per-implementation, as in, each implementation needs its own layer of this, so it's a different package of entirely different classes for each implementation)
    |
The software I'm actually using to write this (which is called by the internal code)
*/

Слой абстракции (очевидно, тот, что в самом центре) — это то, что я изо всех сил пытаюсь собрать.

Теперь я застрял только на одном глупом аспекте. Как я могу сделать уровень абстракции чем-то, что не является серией

public void someMethod() {
    if(Implementation.getCurrentImplementation() == Implementation.TYPE1) {
        // whatever we need to do for this specific implementation
    else {
        throw new NotImplementedException();
    }
}

(простите псевдокод; также представьте ту же ситуацию, но для переключателя/кейса, поскольку это, вероятно, лучше, чем цепочка if для каждого метода) для каждого метода в каждом классе уровня абстракции.

Это кажется очень элементарным, но я не могу придумать логического решения для решения этой проблемы. Если я не ясно объяснил свою точку зрения, пожалуйста, объясните, что мне нужно уточнить. Может, я неправильно обо всем этом думаю?


person Community    schedule 22.07.2016    source источник
comment
Зачем нужен средний уровень? Как я это вижу, 2-й уровень (общедоступный API) представляет собой интерфейс (или набор интерфейсов), а 4-й уровень (внутренний код) представляет реализации интерфейса или конкретные классы, которые зависят от реализации. Все, что нужно, — это разовая конфигурация для выбора подходящего класса реализации, а все остальное будет работать из коробки.   -  person shmosel    schedule 23.07.2016
comment
@shmosel Возможно, это не так. Это представляло слой, потому что я не уверен, что это правильное решение моей проблемы. Я не был слишком уверен в том, что я должен делать на этом уровне, если он вообще есть.   -  person    schedule 23.07.2016


Ответы (1)


Почему бы не использовать инверсию управления?

У вас есть набор абстракций, вы создаете несколько реализаций, а затем настраиваете свой общедоступный API для использования одной из реализаций.

Ваш API защищен набором интерфейсов, которые наследуют реализации. Вы можете добавить новые реализации позже, не изменяя код API, и вы можете переключаться даже во время выполнения.


Я больше не знаю, является ли инверсия управления инъекцией зависимостей или DI является формой Ioc, но ... просто вы снимаете ответственность за управление зависимостями с вашего компонента.

Здесь у вас будет

  • Слой API (интерфейс, который использует клиент)
  • реализации (бесконечно)
  • оболочка (которая делает IoC, принося реализацию)

Уровень API:

// my-api.jar
public interface MyAPI {
    String doSomething();
}

public interface MyAPIFactory {
    MyAPI getImplementationOfMyAPI();
}

реализации:

// red-my-api.jar
public class RedMyAPI implements MyAPI {
    public String doSomething() {
        return "red";
    }
}

// green-my-api.jar
public class GreenMyAPI implements MyAPI {
    public String doSomething() {
        return "green";
    }
}

// black-my-api.jar
public class BlackMyAPI implements MyAPI {
    public String doSomething() {
        return "black";
    }
}

Некоторые оболочки предоставляют способ настроить правильную реализацию. Здесь вы можете скрыть свой корпус коммутатора на заводе или загрузить импл из конфига.

// wrapper-my-api.jar
public class NotFunnyMyAPIFactory implements MyAPIFactory {
    private Config config;

    public MyAPI getImplementationOfMyAPI() {
        if (config.implType == GREEN) {
            return new GreenMyAPI();
        } else if (config.implType == BLACK) {
            return new BlackMyAPI();                
        } else if (config.implType == RED) {
            return new RedMyAPI();                
        } else { 
           // throw...
        }
    }
}

public class ReflectionMyAPIFactory implements MyAPIFactory {
    private Properties prop;

    public MyAPI getImplementationOfMyAPI() {
        return (MyAPI) Class.forName(prop.get('myApi.implementation.className'))
    }
}

// other possible strategies

Фабрика позволяет использовать несколько стратегий для загрузки класса. В зависимости от решения вам нужно только добавить новую зависимость и изменить конфигурацию (и перезагрузить приложение... или нет), чтобы изменить реализацию.

Возможно, вы также захотите проверить производительность.

Если вы используете Spring, вы можете использовать интерфейс только в своем коде, и вы внедряете правильную реализацию из класса конфигурации (Spring — это контейнер DI). Но нет необходимости использовать Spring, вы можете сделать это непосредственно в основной точке входа (вы вводите из ближайшей точки входа).

  • My-api.jar не имеет зависимостей (или, может быть, некоторых по отношению к внутренним слоям).
  • Все jar для реализации зависят от my-api.jar и от вашего внутреннего кода.
  • Баночка-обертка зависит от my-api.jar и от некоторых jar-файлов.

Таким образом, клиент загружает банку, которую он хочет, использует фабрику, которую он хочет, или конфигурацию, которая внедряет реализацию, и использует ваш код. Это также зависит от того, как вы раскрываете свой API.

person chikincrow    schedule 22.07.2016
comment
Инверсия управления? Однако я не понимаю, как это сделать на практике. - person ; 23.07.2016
comment
Теперь гораздо тщательнее, спасибо. :) Благодаря некоторому сотрудничеству между вами и дружелюбным лицом в IRC, я смог собрать его воедино. - person ; 23.07.2016