2014-09-11 2 views
17

В попытке полностью отделить Android SDK от моих классов презентаторов, я пытаюсь найти лучший способ избежать доступа к идентификаторам ресурсов, которые мы обычно используем для R. Я думал, что могу просто создать интерфейс для доступа к таким вещам, как строковые ресурсы, но мне все еще нужны идентификаторы для ссылки на строки. Если бы я сделать что-то вроде ...Android MVP - Следует избегать использования ссылок R.string в презентаторе?

public class Presenter { 
    private MyView view = ...; 
    private MyResources resources = ...; 

    public void initializeView() { 
     view.setLabel(resources.getString(LABEL_RES_ID); 
    } 
} 

Я до сих пор есть LABEL_RES_ID, а затем сопоставить его R.string.label в моем мосту ресурсов. Это круто, потому что я могу поменять его, когда тестирует блок с чем-то другим, но я не хочу управлять другим сопоставлением с строковым значением.

Если я сдаюсь и просто использую значения R.string, мой ведущий снова привязан к моему представлению. Это не идеально? Есть ли более простое решение, которое люди используют, чтобы обойти это, чтобы не допустить их из ведущего. Я не хочу управлять строками в некотором роде вне того, что предлагает Android, потому что я все еще хочу бросить их в файлы макета и получить выгоду от интернационализации и т. Д. Я хочу сделать немой единичный тест, который может работать с этим ведущим без необходимости создания SDK для Android файлов R.java. Это слишком много, чтобы спросить?

+5

Чем больше я использую MVP с Android, тем больше я задаю эти вопросы. Ресурсы и объект Context действительно трудно искоренить. Вы нашли удовлетворительное решение? – loeschg

+0

взгляните на эту статью и образец проекта, который может помочь: https://medium.com/@m_mirhoseini/yet-another-mvp-article-part-1-lets-get-to-know-the-project- d3fd553b3e21 # .6y9ze7e55 –

ответ

15

Я считаю, что нет причин для вызова какого-либо кода для Android в Presenter (Но вы всегда можете это сделать).

Так что в вашем случае:

Посмотреть/активность OnCreate() звонки -> presenter.onCreate();

Презентатор onCreate() calls -> view.setTextLabel() или все, что вы хотите в представлении.

Всегда отключайте Android SDK от ведущих.

В Github, вы можете найти несколько примеров о MVP:

+6

Не могли бы вы указать аргумент, который идет в 'view.setTextLabel()'? Потому что, если вы это сделаете, это ответит на вопрос. В принципе, представьте, если у вас есть логика в вашем презентаторе для отображения либо сообщения 'R.string.a', либо' R.string.b'. Как бы вы передали эту строку, которая будет отображаться в представлении? –

+15

Ваш взгляд должен быть пассивным, а ваш ведущий активен, поэтому ведущий знает, что ему представить, и представление знает, как его представить. если вы хотите показать ошибку в своем представлении от своего ведущего, вам нужно сделать вызов типа: view.showContactLoadingError(); и ваше представление может настроить сообщение в зависимости от контекста представления. Я обновил образцы с помощью другого репо, которое я сделал. – PaNaVTEC

+1

Спасибо @PaNaVTEC, это именно тот ответ, который я искал, и это имеет смысл. –

5

лучше не использовать контекст и весь объект, который зависит от android sdk в презентаторе. Я отправляю идентификатор строки и просматриваю ее в строку. как this->

getview().setTitle(R.string.hello); 

и получить это на виду, как этот

@Override 
public void setTitle(int id){ 
String text=context.getString(id); 
//do what you want to do 
} 

При таком подходе вы можете проверить свой метод в ведущий. Это зависит от объекта R, но все в порядке. все классы MVP размещены в слое представления в uncle bob clean architecture, поэтому вы можете использовать объекты Android, такие как R-класс.но в области слоя, вы должны использовать только регулярные Java объекты

Update

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

getView().setTitle(myStringTools.resolve(HELLO)); 

Метод распознавания строк такой, и класс может быть предоставлен View и DI в презентаторы.

Public String resolve(int ourID){ 
return context.getString(resourceMap.getValue(ourID)); 
} 

Но я не рекомендую это в большинстве случаев из-за чрезмерной инженерии! вам никогда не нужен точный код представления на других платформах в большинстве случаев, поэтому: Лучшее решение было бы чем-то вроде насмешивания над этим классом R на других платформах, потому что R-класс уже похож на обертку. Вы должны написать свой собственный R на другой платформе.

+0

Все еще не кажется правильным. Вы все еще зависите от кода, созданного Android SDK. Я думаю, что у PaNaVTEC есть ответ выше, и, хотя кажется, что это «правильный» способ сделать это, похоже, очень много работы, чтобы получить эти ссылки из ведущего. Если вы ссылаетесь на файл R, похоже, что вы являетесь ведущим, теперь зависит от функциональности Android (по крайней мере, код, который генерируется из Android SDK). –

+0

@zoonsf вы не можете принимать решения по логике в своей деятельности. Нет проблем с использованием ресурсов, и вы даже можете использовать ресурсы в модульном тесте. MVP находится на внешней компоновке чистой архитектуры дяди Боба и зависит от android, поэтому использование ресурсов в презентаторе является обычным и правильным –

1

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

Я просто сообщаю структуру MVP здесь how to structure MVP project от моего собственного ответа.

Я часто ставил код бизнес-логики в Модельный слой (не путайтесь с моделью в базе данных). Я часто переименовываю как XManager, чтобы избежать путаницы (например, ProductManager, MediaManager ...), поэтому класс-презентатор просто использует для поддержания рабочего процесса.

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

Например, это мой рабочий процесс mvp.

Просмотреть класс: Это место, где вы храните все свое видение, такое как кнопка, textview ... и вы устанавливаете для всех этих компонентов на этом уровне все слушатели. Также в этом представлении вы определяете класс Listener для презентационных инструментов позже. Компоненты вашего представления вызовут методы в этом классе слушателя.

class ViewImpl implements View { 
    Button playButton; 
    ViewListener listener; 

    public ViewImpl(ViewListener listener) { 
    // find all view 

    this.listener = listener; 

    playButton.setOnClickListener(new View.OnClickListener() { 
     listener.playSong(); 
    }); 
    } 

    public interface ViewListener { 
    playSong(); 
    } 
} 

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

class PresenterImpl extends Presenter implements ViewListener { 
    private View view; 
    private MediaManager mediaManager; 

    public PresenterImpl(View, MediaManager manager) { 
     this.view = view; 
     this.manager = manager; 
    } 

    @Override 
    public void playSong() { 
     mediaManager.playMedia(); 
    } 
} 

класс менеджер: Вот основной код бизнес-логики. Возможно, у одного ведущего будет много менеджеров (зависит от того, как усложнять представление). Часто мы получаем класс Context через некоторые рамки для инъекций, такие как Dagger.

Class MediaManagerImpl extends MediaManager { 
    // using Dagger for injection context if you want 
    @Inject 
    private Context context; 
    private MediaPlayer mediaPlayer; 

    // dagger solution 
    public MediaPlayerManagerImpl() { 
    this.mediaPlayer = new MediaPlayer(context); 
    } 

    // no dagger solution 
    public MediaPlayerManagerImpl(Context context) { 
    this.context = context; 
    this.mediaPlayer = new MediaPlayer(context); 
    } 

    public void playMedia() { 
    mediaPlayer.play(); 
    } 

    public void stopMedia() { 
     mediaPlayer.stop(); 
    } 
} 

Наконец: Положите эти вещи вместе в деятельности, Fragments ... Вот место инициализации вида, менеджер и назначить всех ведущий.

public class MyActivity extends Activity { 

    Presenter presenter; 

    @Override 
    public void onCreate() { 
     super.onCreate(); 

     IView view = new ViewImpl(); 
     MediaManager manager = new MediaManagerImpl(this.getApplicationContext()); 
     // or this. if you use Dagger 
     MediaManager manager = new MediaManagerImpl(); 
     presenter = new PresenterImpl(view, manager); 
    } 

    @Override 
    public void onStop() { 
    super.onStop(); 
    presenter.onStop(); 
    } 
} 

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

Короче говоря, в вашей ситуации, я предлагаю эту конструкцию:

class ViewImpl implements View { 
     Button button; 
     TextView textView; 
     ViewListener listener; 

     public ViewImpl(ViewListener listener) { 
     // find all view 

     this.listener = listener; 

     button.setOnClickListener(new View.OnClickListener() { 
      textView.setText(resource_id); 
     }); 
     } 
    } 

В случае, если вид логики является сложным, например, некоторые условия для установки значения. Поэтому я верну текст в DataManager. Например:

class Presenter { 
    public void setText() { 
     view.setText(dataManager.getProductName()); 
    } 
} 

class DataManager { 
    public String getProductName() { 
     if (some_internal_state == 1) return getResources().getString(R.string.value1); 
     if (some_internal_state == 2) return getResources().getString(R.string.value2); 
    } 
} 

Таким образом, вы никогда не ставили связанные с андроидом вещи в класс презентатора. Вы должны переместить это значение в класс View или класс DataManager в зависимости от контекста.

Это очень длинное сообщение, в котором подробно обсуждается MVP и как решить вашу конкретную проблему. Надеюсь, эта помощь :)

+0

Помимо того факта, что ведущий не должен иметь обратные вызовы android (странно, чтобы проверить это, и он перемещает спагетти Android в презентаторы) Мне нравится ваше решение. Хотя, что делать, если эти ценности не имеют никакой логики и их много? Использовать _R.string. * _ В диспетчере или презентаторе?Я бы предпочел сохранить их в Presenter, чтобы избежать ненужного слоя абстракции зависти от кода AKA. Но, с другой стороны, они генерируются Android ... Рекомендации по кодированию противостоят друг другу. – Przemo