Рубрики
Без рубрики

Принцип разделения интерфейсов в Java

Краткое и практическое руководство по принципу разделения интерфейсов в Java.

Автор оригинала: Dhrubajyoti Bhattacharjee.

1. введение

В этом уроке мы обсудим Принцип разделения интерфейса, один из ОСНОВНЫХ принципов . Представляя “я” в “ТВЕРДОМ ТЕЛЕ”, сегрегация интерфейса просто означает, что мы должны разбить большие интерфейсы на более мелкие.

Таким образом, гарантируя, что реализующие классы не должны реализовывать нежелательные методы.

2. Принцип сегрегации интерфейса

Этот принцип был впервые определен Робертом К. Мартином следующим образом: ” Клиенты не должны зависеть от интерфейсов, которые они не используют “.

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

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

Мы рассмотрим некоторые примеры в последующих разделах, где у нас есть нарушение принципа, а затем исправим проблему, правильно применив принцип.

3. Пример интерфейса и реализации

Давайте рассмотрим ситуацию, когда у нас есть Платеж интерфейс, используемый реализацией Банковский платеж :

public interface Payment { 
    void initiatePayments();
    Object status();
    List getPayments();
}

И реализация:

public class BankPayment implements Payment {

    @Override
    public void initiatePayments() {
       // ...
    }

    @Override
    public Object status() {
        // ...
    }

    @Override
    public List getPayments() {
        // ...
    }
}

Для простоты давайте проигнорируем фактическую бизнес-реализацию этих методов.

Это очень ясно — до сих пор реализующий класс BankPayment нуждается во всех методах в интерфейсе Payment . Таким образом, это не нарушает принцип.

4. Загрязнение интерфейса

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

Чтобы разработать эту новую функцию, мы добавим новые методы в интерфейс Payment :

public interface Payment {
 
    // original methods
    ...
    void intiateLoanSettlement();
    void initiateRePayment();
}

Далее у нас будет Оплата кредита реализация:

public class LoanPayment implements Payment {

    @Override
    public void initiatePayments() {
        throw new UnsupportedOperationException("This is not a bank payment");
    }

    @Override
    public Object status() {
        // ...
    }

    @Override
    public List getPayments() {
        // ...
    }

    @Override
    public void intiateLoanSettlement() {
        // ...
    }

    @Override
    public void initiateRePayment() {
        // ...
    }
}

Теперь, поскольку интерфейс Payment изменился и было добавлено больше методов, все реализующие классы теперь должны реализовать новые методы. Проблема в том, что их реализация нежелательна и может привести ко многим побочным эффектам. Здесь класс Loan Payment implementation должен реализовать initiate Payments() без какой-либо фактической необходимости в этом. Итак, принцип нарушен.

Итак, что происходит с нашим банковским платежом классом:

public class BankPayment implements Payment {

    @Override
    public void initiatePayments() {
        // ...
    }

    @Override
    public Object status() {
        // ...
    }

    @Override
    public List getPayments() {
        // ...
    }

    @Override
    public void intiateLoanSettlement() {
        throw new UnsupportedOperationException("This is not a loan payment");
    }

    @Override
    public void initiateRePayment() {
        throw new UnsupportedOperationException("This is not a loan payment");
    }
}

Обратите внимание, что реализация Банковский платеж теперь внедрила новые методы. И поскольку он в них не нуждается и не имеет для них логики, он просто выбрасывает UnsupportedOperationException . Именно здесь мы начинаем нарушать принцип.

В следующем разделе мы рассмотрим, как мы можем решить эту проблему.

5. Применение Принципа

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

Давайте разберем интерфейс для каждого типа оплаты. Нынешняя ситуация:

Обратите внимание на диаграмму классов и ссылки на интерфейсы в предыдущем разделе, что методы status() и get Payments() требуются в обеих реализациях. С другой стороны, initiate Payments() требуется только в Банковском платеже , а методы initiate Loan Settlement() и initiateRePayment() предназначены только для LoanPayment .

Разобравшись с этим, давайте разберем интерфейсы и применим Принцип разделения интерфейсов. Таким образом, теперь у нас есть общий интерфейс:

public interface Payment {
    Object status();
    List getPayments();
}

И еще два интерфейса для двух типов платежей:

public interface Bank extends Payment {
    void initiatePayments();
}
public interface Loan extends Payment {
    void intiateLoanSettlement();
    void initiateRePayment();
}

И соответствующие реализации, начиная с Банковского платежа :

public class BankPayment implements Bank {

    @Override
    public void initiatePayments() {
        // ...
    }

    @Override
    public Object status() {
        // ...
    }

    @Override
    public List getPayments() {
        // ...
    }
}

И, наконец, наша пересмотренная Оплата кредита реализация:

public class LoanPayment implements Loan {

    @Override
    public void intiateLoanSettlement() {
        // ...
    }

    @Override
    public void initiateRePayment() {
        // ...
    }

    @Override
    public Object status() {
        // ...
    }

    @Override
    public List getPayments() {
        // ...
    }
}

Теперь давайте рассмотрим новую диаграмму классов:

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

6. Заключение

В этом уроке мы рассмотрели простой сценарий, в котором мы впервые отклонились от Принципа разделения интерфейса и увидели проблемы, вызванные этим отклонением. Затем мы показали, как правильно применять принцип, чтобы избежать этих проблем.

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

Принцип разделения интерфейсов является важной концепцией при проектировании и разработке приложений. Соблюдение этого принципа помогает избежать раздутых интерфейсов с множеством обязанностей. Это в конечном итоге помогает нам также следовать Принципу Единой ответственности.

Как всегда, код доступен на GitHub .