Мой шаблон реализации для MVP действителен?
Я новичок в Android и MVP в целом, и я занимался программированием для iOS в течение последних 1,5 лет, поэтому я нахожу шаблоны делегатов легко усваиваемыми. Я реализовал MVP таким образом, что представление соответствует протоколу докладчика, что позволяет докладчику игнорировать определенный тип представления, но позволяет ему знать, что определенные методы являются заданными, и, таким образом, можно вызывать "представление". Я читал различные руководства MVP и все учебники Mosby, и я не уверен, что согласен с некоторыми из них. Шаблон, который я реализовал, кошерный? Я хотел бы получить некоторую обратную связь, чтобы я не продолжал идти в плохом направлении, если это действительно то, что я делаю...
Например,
Базовый Ведущий:
public abstract class Presenter<V, S> implements BasePresenterInterface<V, S> {
public interface PresenterProtocol extends BasePresenterProtocol {
}
private WeakReference<V> mAttachedView = null;
private S mService = null;
/**
* Interface Overrides
*/
@Override
public void attachView(V view) {
boolean viewDoesNotConform = !viewDoesConform(view);
if (viewDoesNotConform) {
Log.d("DEBUG", "Cannot attach View that does not conform to PresenterProtocol");
return;
}
mAttachedView = new WeakReference<>(view);
((BasePresenterProtocol) getAttachedView()).onViewAttached();
}
@Override
public void detachView() {
mAttachedView = null;
}
@Override
public boolean viewDoesConform(V view) {
Class<?> klass = view.getClass();
boolean conforms = BasePresenterInterface.BasePresenterProtocol.class.isAssignableFrom(klass);
return conforms;
}
@Override
public boolean viewIsAttached() {
return mAttachedView != null;
}
@Override
public V getAttachedView() {
return mAttachedView.get();
}
@Override
public S getService() {
return mService;
}
@Override
public void setService(S service) {
mService = service;
}
}
Затем я делю это на следующее:
PhotoRecyclerPresenter:
public class PhotoRecyclerPresenter extends Presenter<PhotoRecyclerPresenter.PhotoRecyclerPresenterProtocol, PhotoService> {
public interface PhotoRecyclerPresenterProtocol extends Presenter.PresenterProtocol {
void onPhotosLoaded(List<TestPhoto> photoList);
void onItemSelected(TestPhoto photo);
void onShowDetail(TestPhoto photo);
}
private static PhotoRecyclerPresenter mSharedInstance;
private PhotoRecyclerPresenter() {
setService(new PhotoService());
}
/**
* External Methods
*/
public void getPhotos() {
boolean noAttachedView = !viewIsAttached();
if (noAttachedView) {
Log.d("DEBUG", "No view attached");
return;
}
getService().getAPI()
.getPhotos()
.observeOn(AndroidSchedulers.mainThread())
.subscribe(photoList -> getAttachedView().onPhotosLoaded(photoList));
}
/**
* Internal Methods
*/
public static PhotoRecyclerPresenter getSharedInstance() {
boolean firstInstance = mSharedInstance == null;
if (firstInstance) {
setSharedInstance(new PhotoRecyclerPresenter());
}
return mSharedInstance;
}
public static void setSharedInstance(PhotoRecyclerPresenter instance) {
mSharedInstance = instance;
}
public void didSelectItem(TestPhoto photo) {
getAttachedView().showDetail(photo);
}
}
И это общается с видом:PhotoRecyclerFragment:
public class PhotoRecyclerFragment extends Fragment implements PhotoRecyclerPresenter.PhotoRecyclerPresenterProtocol {
private RecyclerView mRecyclerView;
private RecyclerView.LayoutManager mLayoutManager;
private Activity mParentActivity;
private PhotoRecyclerPresenter mPresenter;
private PhotoRecyclerAdapter mAdapter;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_recycler, container, false);
mParentActivity = getActivity();
mRecyclerView = (RecyclerView) rootView.findViewById(R.id.recyclerView);
mLayoutManager = new LinearLayoutManager(mParentActivity);
mAdapter = new PhotoRecyclerAdapter(this);
mRecyclerView.setLayoutManager(mLayoutManager);
mRecyclerView.setAdapter(mAdapter);
mPresenter = PhotoRecyclerPresenter.getSharedInstance();
mPresenter.attachView(this);
return rootView;
}
@Override
public void onDestroyView() {
super.onDestroyView();
mPresenter.detachView();
mAdapter.clear();
}
/**
* PhotoRecyclerPresenterProtocol Methods
*/
@Override
public void onItemSelected(TestPhoto photo) {
mPresenter.didSelectItem(photo);
}
@Override
public void onPhotosLoaded(List<TestPhoto> photoList) {
mAdapter.loadPhotos(photoList);
}
@Override
public void onViewAttached() {
mPresenter.getPhotos();
}
@Override
public void onViewDetached() {
}
@Override
public void onShowDetail(TestPhoto photo) {
Intent detailIntent = new Intent(mParentActivity, PhotoDetailActivity.class);
mParentActivity.startActivity(detailIntent.putExtra(Intent.EXTRA_UID, photo.getPhotoId()));
}
}
Это позволяет мне определить набор требований, которым должно соответствовать представление, чтобы использовать одноэлементного презентатора, сохраняя при этом презентатора независимо от того, какие представления используют его, если они соответствуют его протоколу. Пока что в моем тренировочном проекте это работает нормально, но я не могу найти какие-либо ресурсы, где то, что я делаю, рекомендуется в плане MVP, и у меня достаточно сомнений в себе, что я решил спросить мой первый вопрос Stackru Может ли кто-нибудь, кто имеет опыт работы с MVP, пролить свет на это?
Кроме того, если я спрашиваю не в том месте, не стесняйтесь указывать мне в правильное место, чтобы опубликовать это.
Спасибо:)
1 ответ
С моей точки зрения, вы делаете то же самое, что и Мосби. Единственная разница - это имя интерфейса (или протокола в target-c) мира. Вы называете это PresenterProtocol
пока Мосби называет это MvpView
, Оба делают одну и ту же работу: предлагая Presenter
API методов, которые презентатор может вызывать для управления представлением.
Единственное, что не имеет смысла, это иметь метод viewDoesConform()
, В Java у вас есть тип безопасности. Вы можете использовать тип дженериков V
вашего докладчика, чтобы убедиться, что ваш фрагмент реализует протокол докладчика. просто измените его на V extends BasePresentersProtocol
Кроме того, я думаю, что не имеет смысла иметь "общий экземпляр" (он же Singleton pattern) докладчика. Я думаю, что было бы более разумно иметь "общий экземпляр" PhotoService. Но обратите внимание, что при этом ваш код больше не тестируется (модульные тесты). Вам следует поискать в Google Dependency Injection или Inverse of Control, чтобы понять, как писать модульный, многоразовый и тестируемый код. Я не говорю о структурах внедрения зависимостей, таких как Dagger, Spring или Guice. Вы просто должны понять идею внедрения зависимости. Вы можете писать классы, следуя этому принципу, полностью без интегрирования зависимостей (т. Е. Используя параметры конструктора).
Примечание: вы никогда не отмените подписку на PhotoService. В зависимости от того, как реализован PhotoService, у вас может быть утечка памяти, потому что наблюдаемая PhotoService имеет ссылку на презентатора, что предотвращает сборку мусора презентатором и PhotoService (в зависимости от конкретной реализации).
Изменить: Мосби определяет протокол для просмотра. Загляните в раздел "Начало работы" на веб-сайте проекта. HelloWorldView
определяет два метода: showHello()
а также showGoodbye()
(наполненный HelloWorldActivity
) а также HelloWorldPresenter
вызывает эти два метода для управления представлением. HelloWorldPresenter
также отменяет асинхронные запросы, чтобы избежать утечек памяти. Вы должны сделать это тоже. В противном случае ваш докладчик может получить мусор только после завершения httpcall модернизации.