简体   繁体   English

我的MVP实施模式有效吗?

[英]Is my implementation pattern for MVP valid?

I'm new to Android and MVP in-general, and I've been doing iOS programming for the last 1.5 years, so I find delegate patterns easy to digest. 我是Android和MVP的新手,在过去的1.5年中我一直在进行iOS编程,因此我发现易于理解的委托模式。 I've implemented MVP in such a way that the view conforms to a presenter's protocol, which lets the presenter disregard the view's specific type, but lets it know that certain methods are a given and thus okay to call on the "view." 我已经以视图符合演示者协议的方式实现了MVP,这使演示者可以忽略视图的特定类型,但可以知道某些方法是给定的,因此可以调用“视图”。 I've been reading various MVP guides, and all of the Mosby tutorials, and I'm not sure I agree with some of it. 我一直在阅读各种MVP指南以及所有的Mosby教程,但不确定我是否同意其中的一些内容。 Is the pattern I've implemented kosher? 我实现了犹太洁食模式吗? I'd like some feedback so I don't keep heading in a bad direction, if that is indeed what I'm doing... 我想要一些反馈,所以我不会一直朝着错误的方向前进,如果那确实是我正在做的事情...

For example, 例如,

Base Presenter: 基本演示者:

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

I then subclass this into the following: 然后,将其细分为以下内容:

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

And it communicates with the view: PhotoRecyclerFragment: 它与视图通信: 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()));
    }
}

This lets me define a set of requirements a view needs to conform to in order to utilize the singleton presenter, while keeping the presenter agnostic about what views use it, as long as they conform to its protocol. 这使我可以定义视图的一组要求,以便利用单例演示者,同时使演示者不了解使用它的视图,只要它们符合其协议即可。 So far in my practice project it seems to work fine, but I can't seem to find any resources where what I'm doing is recommended as far as MVP goes, and I have enough self-doubt that I figured I'd ask my first StackOverflow question. 到目前为止,在我的实践项目中,它似乎可以正常工作,但是就MVP而言,我似乎找不到推荐我正在做什么的资源,而且我有足够的自我怀疑,我想我会问我的第一个StackOverflow问题。 Can anyone who has experience with MVP shed some light on this? 拥有MVP经验的人可以对此有所了解吗?

Also, if I'm asking in the wrong place, feel free to point me to the correct place to post this. 另外,如果我在错误的位置提问,请随时将我指向正确的位置以发布此内容。

Thanks :) 谢谢 :)

From my point of view you are doing the same thing that Mosby does. 从我的角度来看,您正在做与Mosby相同的事情。 The only difference is the name of the interface (or protocol in objective-c) world. 唯一的区别是接口(或Objective-C中的协议)世界的名称。 You call it PresenterProtocol while Mosby call it MvpView . 您将其称为PresenterProtocol而Mosby将其MvpView Both are doing the same job: Offering the Presenter an Api of methods the presenter can call to manipulate the view. 两者都在完成相同的工作:向Presenter提供Presenter者可以调用以操纵视图的方法的Api。

The only thing that doesn't make sense is to have a method viewDoesConform() . 唯一没有意义的是使用方法viewDoesConform() In Java you have type safety. 在Java中,您具有类型安全性。 You can use the generics type V of your Presenter to ensure that your fragment is implementing the Presenter's protocol. 您可以使用Presenter的泛型类型V来确保您的片段正在实现Presenter的协议。 just change it to V extends BasePresentersProtocol 只需将其更改为V extends BasePresentersProtocol

Furthermore I think that it doesn't make sense to have a "shared instance" (aka Singleton pattern) of the presenter. 此外,我认为拥有演示者的“共享实例”(也称为Singleton模式)是没有意义的。 I think it would make more sense to have a "shared instance" of the PhotoService. 我认为拥有PhotoService的“共享实例”会更有意义。 But But please note also that by doing so your code is not testable (unit tests) anymore. 但是,但也请注意,这样做将使您的代码不再可测试(单元测试)。 You should google for Dependency injection or Inverse of Control to understand how to write modular, reusable and testable code. 您应该通过google进行依赖注入或控制逆,以了解如何编写模块化,可重用和可测试的代码。 I'm not talking about dependency injection frameworks like Dagger , spring or guice. 我不是在谈论像Dagger,spring或guice这样的依赖注入框架。 You just should understand the idea behind dependency injection. 您只应该了解依赖注入背后的想法。 You can write classes following this principle completely without dependency injection frameworks (ie using constructor parameters). 您可以完全遵循此原理编写类,而无需依赖注入框架(即使用构造函数参数)。

Side note: you never unsubscribe your presenter from PhotoService. 旁注:您永远不会从PhotoService退订演示者。 Depending on how PhotoService is implemented you may have a memory leak because PhotoService observable has a reference to the presenter which prevents the presenter and PhotoService (depending on your concrete implementation) from being garbage collected. 根据PhotoService的实现方式,您可能会发生内存泄漏,因为Observable的PhotoService具有对演示者的引用,该引用可防止演示者和PhotoService(取决于您的具体实现)被垃圾回收。

Edit: Mosby defines the protocol for the View. 编辑:Mosby定义了View的协议。 Have a look at the getting started section on the project website. 查看项目网站上的入门部分。 The HelloWorldView defines two methods: showHello() and showGoodbye() (implented by the HelloWorldActivity ) and HelloWorldPresenter calls these two methods to manipulate the View. HelloWorldView定义了两个方法: showHello()showGoodbye() (由HelloWorldActivity ),并且HelloWorldPresenter调用这两个方法来操纵View。 The HelloWorldPresenter also cancels the async requests to avoid memory leaks. HelloWorldPresenter还会取消异步请求,以避免内存泄漏。 You should do that too. 您也应该这样做。 Otherwise your presenter can only be garbage collected after the retrofit httpcall has completed. 否则,只有在改造httpcall完成后才能对您的演示者进行垃圾回收。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM