Проблема взрыва интерфейса - PullRequest
1 голос
/ 31 марта 2010

Я реализую экран, используя шаблон MVP, с добавлением дополнительных функций на экран, я добавляю все больше и больше методов в интерфейс IScreen / IPresenter, следовательно, интерфейс IScreen / IPresenter становится все больше и больше, что я должен справиться с этой ситуацией?

Ответы [ 4 ]

4 голосов
/ 31 марта 2010

Нет определенного ограничения на количество артефактов (методов, констант, перечислений и т. Д.) - скажем, N - в интерфейсе, так что мы можем сказать , если интерфейс X имеет больше чем N артефакты, это раздутый .

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

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

http://javaboutique.internet.com/tutorials/coupcoh/

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

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

Давайте предположим, что ваш интерфейс назван A ради аргумента. Тогда,

Шаг 1: Рассмотрите возможность группировки методов интерфейса по аргументам: есть ли подмножество методов, которые работают с аргументами того же типа? Если так, они значительно отличаются от других групп методов?

interface A
{
  void method1();
  void method2(someArgType x);
  someOtherType y method3();

  ...

  void doSomethingOn( someType t );
  boolean isUnderSomeCondition( someType t )
  someType replaceAndGetPrev( someType t, someFields ... )
}

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

Шаг 2:

Как только вы извлекаете интерфейс B, он выглядит так?

interface B
{
  void doSomethingOn( someType t );
  ...
  boolean isUnderSomeCondition( someType t )
  ...
  someType replaceAndGetPrev( someType t, someFields ... )
}

То есть он представляет методы, которые делают вещи в некотором типе?

Если это так, ваш интерфейс имитирует работу процедурного модуля на ADT (в данном случае someType ) - ничего плохого в , если вы используете процедурный или мультипарадигмальный язык .

В пределах разумного и прагматичного, в ОО вы минимизируете процедуры, которые делают вещи с другими объектами. Вы вызываете методы в этих объектах , чтобы сделать что-то для себя от вашего имени. Или, точнее, вы даете им понять, что нужно что-то делать внутри.

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

class Bclass
{
  someType t;

  Bclass(){ t=new someType();} 
  ...
  void doSomethingOn();
  ...
  boolean isUnderSomeCondition()
  ...
  someType replaceAndGetPrev( someFields ... )
}

Шаг 3: Определите взаимосвязи между интерфейсами и классами, пересчитанными из A.

Если B представляет вещи, которые могут существовать, только когда A делает (A является контекстом для B, например, запрос сервлета существует в контексте сервлета в языке Java EE), затем пусть B определяет метод, который возвращает A (например, B.getContext () или что-то в этом роде).

Если B представляет вещи, которыми управляет A (A является совокупностью вещей, включая B), тогда пусть A определяет метод, который возвращает B (B A.getBThingie ())

Если между A и B нет таких отношений, и у них нет ничего общего, кроме как они были сгруппированы вместе, то есть вероятность , что исходный интерфейс был плохо связан

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

Надеюсь, это поможет.

пс. Кроме того, я бы также не пытался приспособить ваши интерфейсы и классы к традиционным шаблонам UNLESS , которые служат для конкретной прикладной / бизнес-цели. Я должен бросить это там на всякий случай. Слишком много людей сходят с ума от книги GoF, пытаясь приспособить свои классы к шаблонам, а не спрашивают: «Какую проблему я решаю с этим?»

1 голос
/ 31 марта 2010

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

Каждый интерфейс строго «отвечает» только за одну вещь .

Я пытаюсь рассмотреть эти сущности "маленьких" людей, которые взаимодействуют друг с другом, чтобы выполнить определенную задачу.

(извините, если это немного философствовать)

1 голос
/ 31 марта 2010

Какой вариант Model-View-Presenter вы используете? Я обнаружил, что Пассивное представление редко включает перекрытия между интерфейсами просмотра и презентатора - обычно они меняются в разное время.

Обычно интерфейс представления по сути является моделью представления, возможно, что-то вроде этого (стиль C #):

public interface IEditCustomerView {
    string FirstName { get; set; }
    string LastName  { get; set; }
    string Country   { get; set; }
    List<Country> AvailableCountries { get; set; }
    // etc.
}

Реализация представления обычно имеет обработчики для пользовательских жестов, которые обычно представляют собой тонкие оболочки, вызывающие докладчика:

public class EditCustomerView {
    // The save button's 'click' observer
    protected void SaveCustomer() {
       this.presenter.SaveCustomer();
    }
}

В презентере обычно есть метод для каждого пользовательского жеста, но нет данных, поскольку он получает его непосредственно из представления (которое обычно передается презентатору в конструкторе, хотя вы можете передавать его при каждом вызове метода, если это больше подходит):

public interface IEditCustomerPresenter {
    void Load();
    void SaveCustomer();
}
1 голос
/ 31 марта 2010

Можете ли вы разбить ваш интерфейс на подинтерфейсы, представляющие разделы экрана? Например, если ваш экран разделен на группы, такие как раздел навигации, или раздел формы, или раздел панели инструментов, то ваш IPresenter / IScreen может иметь геттеры для интерфейсов для этих разделов, и эти разделы могут содержать соответствующие методы для каждого раздела. Ваш основной IPresenter / IScreen все равно будет иметь методы, относящиеся ко всему интерфейсу.

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

РЕДАКТИРОВАТЬ Например:

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

В примере, который работает подобным образом, реализация MainPresenter *1015* знает как о своей модели, так и о ее представлении и его подчиненных. Каждый SubPresenter управляет своим собственным видом и моделью. Любые операции над тем, что логически принадлежит этому подразделу, должны быть в SubPresenter. Если ваш экран устроен таким образом, что существуют такие логические единицы, как эта, такая настройка должна работать хорошо. Каждый SubPresenter должен иметь возможность вернуть SubView для MainPresenter для подключения к MainView в зависимости от ситуации.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...