Автор оригинала: Dhrubajyoti Bhattacharjee.
1. введение
В этом уроке мы обсудим Принцип разделения интерфейса, один из ОСНОВНЫХ принципов . Представляя “я” в “ТВЕРДОМ ТЕЛЕ”, сегрегация интерфейса просто означает, что мы должны разбить большие интерфейсы на более мелкие.
Таким образом, гарантируя, что реализующие классы не должны реализовывать нежелательные методы.
2. Принцип сегрегации интерфейса
Этот принцип был впервые определен Робертом К. Мартином следующим образом: ” Клиенты не должны зависеть от интерфейсов, которые они не используют “.
Цель этого принципа состоит в том, чтобы уменьшить побочные эффекты использования больших интерфейсов, разбив интерфейсы приложений на более мелкие . Это похоже на Принцип Единой ответственности , где каждый класс или интерфейс служит одной цели.
Точный дизайн приложения и правильная абстракция-это ключ к принципу разделения интерфейса. Хотя это займет больше времени и усилий на этапе разработки приложения и может увеличить сложность кода, в конце концов мы получим гибкий код.
Мы рассмотрим некоторые примеры в последующих разделах, где у нас есть нарушение принципа, а затем исправим проблему, правильно применив принцип.
3. Пример интерфейса и реализации
Давайте рассмотрим ситуацию, когда у нас есть Платеж интерфейс, используемый реализацией Банковский платеж :
public interface Payment { void initiatePayments(); Object status(); List
И реализация:
public class BankPayment implements Payment { @Override public void initiatePayments() { // ... } @Override public Object status() { // ... } @Override public List
Для простоты давайте проигнорируем фактическую бизнес-реализацию этих методов.
Это очень ясно — до сих пор реализующий класс 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
Теперь, поскольку интерфейс Payment изменился и было добавлено больше методов, все реализующие классы теперь должны реализовать новые методы. Проблема в том, что их реализация нежелательна и может привести ко многим побочным эффектам. Здесь класс Loan Payment implementation должен реализовать initiate Payments() без какой-либо фактической необходимости в этом. Итак, принцип нарушен.
Итак, что происходит с нашим банковским платежом классом:
public class BankPayment implements Payment { @Override public void initiatePayments() { // ... } @Override public Object status() { // ... } @Override public List
Обратите внимание, что реализация Банковский платеж теперь внедрила новые методы. И поскольку он в них не нуждается и не имеет для них логики, он просто выбрасывает UnsupportedOperationException . Именно здесь мы начинаем нарушать принцип.
В следующем разделе мы рассмотрим, как мы можем решить эту проблему.
5. Применение Принципа
В последнем разделе мы намеренно загрязнили интерфейс и нарушили принцип. В этом разделе мы рассмотрим, как добавить новую функцию для оплаты кредита, не нарушая принцип.
Давайте разберем интерфейс для каждого типа оплаты. Нынешняя ситуация:
Обратите внимание на диаграмму классов и ссылки на интерфейсы в предыдущем разделе, что методы status() и get Payments() требуются в обеих реализациях. С другой стороны, initiate Payments() требуется только в Банковском платеже , а методы initiate Loan Settlement() и initiateRePayment() предназначены только для LoanPayment .
Разобравшись с этим, давайте разберем интерфейсы и применим Принцип разделения интерфейсов. Таким образом, теперь у нас есть общий интерфейс:
public interface Payment { Object status(); List
И еще два интерфейса для двух типов платежей:
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
И, наконец, наша пересмотренная Оплата кредита реализация:
public class LoanPayment implements Loan { @Override public void intiateLoanSettlement() { // ... } @Override public void initiateRePayment() { // ... } @Override public Object status() { // ... } @Override public List
Теперь давайте рассмотрим новую диаграмму классов:
Как мы видим, интерфейсы не нарушают принцип. Реализации не должны предоставлять пустые методы. Это сохраняет код чистым и снижает вероятность ошибок.
6. Заключение
В этом уроке мы рассмотрели простой сценарий, в котором мы впервые отклонились от Принципа разделения интерфейса и увидели проблемы, вызванные этим отклонением. Затем мы показали, как правильно применять принцип, чтобы избежать этих проблем.
В случае, если мы имеем дело с загрязненными устаревшими интерфейсами, которые мы не можем изменить, шаблон адаптера может пригодиться.
Принцип разделения интерфейсов является важной концепцией при проектировании и разработке приложений. Соблюдение этого принципа помогает избежать раздутых интерфейсов с множеством обязанностей. Это в конечном итоге помогает нам также следовать Принципу Единой ответственности.
Как всегда, код доступен на GitHub .