Подтвердить что ты не робот

Android MVP - Следует избегать использования ссылок R.string в презентаторе?

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

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, потому что я все еще хочу бросить их в файлы макета и получить выгоду от интернационализации и т.д. Я хочу сделать немой unit test, который может работать с этим презентатор, не требуя, чтобы Android SDK генерировал файлы R.java. Это слишком много, чтобы спросить?

4b9b3361

Ответ 1

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

Итак, в вашем случае:

Просмотр/действие onCreate() calls → presenter.onCreate();

Вызов Presenter onCreate() → view.setTextLabel() или все, что вы хотите в представлении.

Всегда отключайте Android SDK от презентаторов.

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

Ответ 2

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

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

и получим это как показано

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

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

Обновление

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

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

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

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

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

Ответ 3

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

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

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

Эмпирическое правило не является или, по крайней мере, ограничивает пакет импорта android в классе презентатора. Эта лучшая практика помогает вам легче тестировать класс презентатора, потому что ведущий теперь просто простой 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();
   }
}

Класс презентатора: Здесь вы храните представление и модель внутри для вызова позже. Также класс презентатора будет реализовывать интерфейс 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();
   }
}

Наконец: Поместите эти вещи вместе в "Действия", "Фрагменты". Здесь вы инициализируете представление, менеджер и назначаете все для презентатора.

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);
   }
}

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

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