Skip to content

Instantly share code, notes, and snippets.

@OleksandrKucherenko
Created December 10, 2017 21:53
Show Gist options
  • Save OleksandrKucherenko/ab51236de034261bf8de35104f19c285 to your computer and use it in GitHub Desktop.
Save OleksandrKucherenko/ab51236de034261bf8de35104f19c285 to your computer and use it in GitHub Desktop.
MVP pattern, basic interfaces
/** Copyright 2017, Oleksandr Kucherenko */
package olku.android.mvp;
import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.*;
import java.lang.annotation.Retention;
import java.util.HashMap;
import java.util.Map;
import rx.functions.Func1;
import static java.lang.annotation.RetentionPolicy.SOURCE;
/** High level scope of all Model, View, Presenter and Interactor's. */
public interface Mvp {
/**
* Model/View/Presenter/Interactor design patter. Inherit the interface for marking class as a Interactor for pattern.
*
* @see <a href="http://fernandocejas.com/2014/09/03/architecting-android-the-clean-way/">Clean Architecture</a>
*/
interface Interactor {
}
/**
* Model/View/Presenter/Interactor design patter. Inherit the interface for marking class as a View for pattern.
*
* @see <a href="http://fernandocejas.com/2014/09/03/architecting-android-the-clean-way/">Clean Architecture</a>
*/
@MainThread
interface View {
/** Navigate user by specified deeplink. */
@AutoProxy.Yield(Returns.DIRECT)
boolean dispatchDeepLink(@NonNull final Uri deepLink);
}
/** Sub-component of the view interface. Just a marker for class/interface. */
@MainThread
interface Controller {
}
/**
* Model/View/Presenter/Interactor design patter. Inherit the interface for marking class as a Presenter for pattern.
*
* @see <a href="http://fernandocejas.com/2014/09/03/architecting-android-the-clean-way/">Clean Architecture</a>
*/
@SuppressWarnings({"unused"})
interface Presenter {
/** Resume all. Activity/Fragment goes foreground. */
void resume();
/** Pause all. Activity/Fragment goes background. */
void pause();
/** Inherit this interface if you plan to save/restore state of the presenter. */
interface WithState {
/** restore presenter state from bundle instance. */
void restore(@NonNull final Bundle state);
/** save presenter state to bundle. */
void save(@NonNull final Bundle state);
}
}
/**
* Model/View/Presenter/Interactor design patter. Inherit the interface for marking class as a Model for pattern.
*
* @see <a href="http://fernandocejas.com/2014/09/03/architecting-android-the-clean-way/">Clean Architecture</a>
*/
interface Model {
}
/** All known event types. */
@Retention(SOURCE)
@IntDef({
Events.WHEEL_EXPANDING /*, ...*/
})
@interface Events {
/** Wheel expanding. */
int WHEEL_EXPANDING = 1;
/** used for unit testing only. */
int UNKNOWN = -1;
/** Capture critical to application error. */
int GOOGLE_SERVICES_ERROR = -2;
/** Generic message. Used for forwarding captured error/exception. */
int ERROR_CAPTURED = -3;
/** Utility event. Used for notifying that fragmentDialog closed by user. */
int DIALOG_CLOSED = -4;
/** Application installed */
int INSTALLED = -100;
/** Application first run. */
int FIRST_RUN = -101;
/** Upgrade from older version */
int UPGRADED = -102;
}
/** EventBus abstraction. Used for cross presenter communications. */
class Event {
/** Event unique identifier. */
public final int id;
/** additional info associated with event. */
@Nullable
public final Object tag;
/** extended key value data associated with event. */
@NonNull
public final Map<String, Object> eventData = new HashMap<>();
public Event(@Events final int id) {
this(id, null);
}
public Event(@Events final int id, @Nullable final Object tag) {
this.id = id;
this.tag = tag;
}
@NonNull
public Event withEventData(final String name, final Object value) {
eventData.put(name, value);
return this;
}
}
/** All MvpModule classes should inherit it for type safe casting. */
interface Module {
}
/**
* Class designed for simplified injection of Mocked MvpModule into fragments/activities and any other UI
* elements without deep overrides of classes in Unit Tests.
*/
class Factory {
/** Registered creators of MvpModules. */
@VisibleForTesting /* package */ static final Map<Class<? extends View>,
Func1<? super View, ? extends Module>> registered = new HashMap<>();
/** Find reflection class interface that inherits the Mvp.View interface. */
@NonNull
@SuppressWarnings("unchecked")
static Class<? extends View> findViewClazz(final Class<? extends View> clazz) {
for (Class<?> cl : clazz.getInterfaces()) {
final Class<?>[] interfaces = cl.getInterfaces();
final boolean isNotEmpty = null != interfaces && interfaces.length == 1;
if (isNotEmpty && interfaces[0].isAssignableFrom(View.class))
return (Class<? extends View>) cl;
}
return clazz;
}
/** Factory method that allows easy replacement of the MvpModule for UI elements. */
@SuppressWarnings("unchecked")
public static <M extends Module, V extends View> M peek(@NonNull final V view, @NonNull final Func1<V, M> creator) {
// first: try the class itself
final Class<? extends View> clazz = view.getClass();
final Func1<? super View, ? extends Module> mock = registered.get(clazz);
// mock is available
if (null != mock)
return (M) mock.call(view);
// second: try the inheritor of Mvp.View interface
final Class<? extends View> viewClazz = findViewClazz(clazz);
final Func1<? super View, ? extends Module> mockView = registered.get(viewClazz);
if (null != mockView)
return (M) mockView.call(view);
// third: call the provided creator
return creator.call(view);
}
/** Register module creator in factory. */
public static void register(@NonNull final Class<? extends View> clazz,
@NonNull final Func1<? super View, ? extends Module> creator) {
registered.put(clazz, creator);
}
/** Unregister module creator in factory. */
public static void unregister(@NonNull final Class<? extends View> clazz) {
registered.remove(clazz);
}
}
}
@OleksandrKucherenko
Copy link
Author

all Presenters should inherit Mvp.Presenter,
Model - Mvp.Model
View - Mvp.View
Interactor - Mvp.Interactor

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment