美文网首页
Android架构分析【三】mvp-clean

Android架构分析【三】mvp-clean

作者: difcareer | 来源:发表于2017-12-07 17:58 被阅读150次

    架构简介中,最重要的是Clean Architecture的思想。

    Clean Architecture

    原版的介绍中,有一张很不错的图:


    乍一看这张图,就像洋葱,一层包一层,其核心理念就是向内依赖。也就是说:外层依赖内层,内层不能依赖外层,层层向内依赖。越往内,越稳定。在这个核心理念下,定义了每一层的名称和职责,见图,不复述。

    示例代码在代码结构上又分为三个层次:



    相比mvp,最大的变化就是增加了Domain Layer以及Use Case。Use Case定义了对Entity的使用范例。其中包含了业务逻辑,这在mvp架构中是应该在Presenter中的,现在剥离出来了。好处是结构更清晰,代码复用更高了。

    光这么描述,没啥感觉,还是来看看代码。

    代码分析

    首先拉取分支:

    git checkout -b todo-mvp-clean origin/todo-mvp-clean
    

    我们只分析项目中的添加task的业务逻辑即可,其他业务逻辑类似。

    先看看代码结构:


    同样的,最核心的实体是model: Task。围绕Task有若干包含业务逻辑的usecase:DeleteTask、GetTask、SaveTask。 model和usecase共同构建了domain Layer(当然model可能只有一个,但是针对特定业务可能有不同的usecase,所以不一定要usecase和model在每个业务中都同时存在)。

    我们看一个具体的usecase:

    public class GetTask extends UseCase<GetTask.RequestValues, GetTask.ResponseValue> {
    
        private final TasksRepository mTasksRepository;
    
        public GetTask(@NonNull TasksRepository tasksRepository) {
            mTasksRepository = checkNotNull(tasksRepository, "tasksRepository cannot be null!");
        }
    
        @Override
        protected void executeUseCase(final RequestValues values) {
            mTasksRepository.getTask(values.getTaskId(), new TasksDataSource.GetTaskCallback() {
                @Override
                public void onTaskLoaded(Task task) {
                    if (task != null) {
                        ResponseValue responseValue = new ResponseValue(task);
                        getUseCaseCallback().onSuccess(responseValue);
                    } else {
                        getUseCaseCallback().onError();
                    }
                }
    
                @Override
                public void onDataNotAvailable() {
                    getUseCaseCallback().onError();
                }
            });
        }
    
        public static final class RequestValues implements UseCase.RequestValues {
    
            private final String mTaskId;
    
            public RequestValues(@NonNull String taskId) {
                mTaskId = checkNotNull(taskId, "taskId cannot be null!");
            }
    
            public String getTaskId() {
                return mTaskId;
            }
        }
    
        public static final class ResponseValue implements UseCase.ResponseValue {
    
            private Task mTask;
    
            public ResponseValue(@NonNull Task task) {
                mTask = checkNotNull(task, "task cannot be null!");
            }
    
            public Task getTask() {
                return mTask;
            }
        }
    }
    

    可以看到,其中使用Data Layer层的接口获取数据。

    再来看看Presenter:

    public class AddEditTaskPresenter implements AddEditTaskContract.Presenter {
    
        private final AddEditTaskContract.View mAddTaskView;
    
        private final GetTask mGetTask;
    
        private final SaveTask mSaveTask;
    
        private final UseCaseHandler mUseCaseHandler;
    
        @Nullable
        private String mTaskId;
    
        private boolean mIsDataMissing;
    
        /**
         * Creates a presenter for the add/edit view.
         *
         * @param taskId      ID of the task to edit or null for a new task
         * @param addTaskView the add/edit view
         * @param shouldLoadDataFromRepo whether data needs to be loaded or not (for config changes)
         */
        public AddEditTaskPresenter(@NonNull UseCaseHandler useCaseHandler, @Nullable String taskId,
                @NonNull AddEditTaskContract.View addTaskView, @NonNull GetTask getTask,
                @NonNull SaveTask saveTask, boolean shouldLoadDataFromRepo) {
            mUseCaseHandler = checkNotNull(useCaseHandler, "useCaseHandler cannot be null!");
            mTaskId = taskId;
            mAddTaskView = checkNotNull(addTaskView, "addTaskView cannot be null!");
            mGetTask = checkNotNull(getTask, "getTask cannot be null!");
            mSaveTask = checkNotNull(saveTask, "saveTask cannot be null!");
            mIsDataMissing = shouldLoadDataFromRepo;
    
            mAddTaskView.setPresenter(this);
        }
    
        @Override
        public void start() {
            if (!isNewTask() && mIsDataMissing) {
                populateTask();
            }
        }
    
        @Override
        public void saveTask(String title, String description) {
            if (isNewTask()) {
                createTask(title, description);
            } else {
                updateTask(title, description);
            }
        }
    
        @Override
        public void populateTask() {
            if (isNewTask()) {
                throw new RuntimeException("populateTask() was called but task is new.");
            }
    
            mUseCaseHandler.execute(mGetTask, new GetTask.RequestValues(mTaskId),
                    new UseCase.UseCaseCallback<GetTask.ResponseValue>() {
                        @Override
                        public void onSuccess(GetTask.ResponseValue response) {
                            showTask(response.getTask());
                        }
    
                        @Override
                        public void onError() {
                            showEmptyTaskError();
                        }
                    });
        }
    
        private void showTask(Task task) {
            // The view may not be able to handle UI updates anymore
            if (mAddTaskView.isActive()) {
                mAddTaskView.setTitle(task.getTitle());
                mAddTaskView.setDescription(task.getDescription());
            }
            mIsDataMissing = false;
        }
    
        private void showSaveError() {
            // Show error, log, etc.
        }
    
        private void showEmptyTaskError() {
            // The view may not be able to handle UI updates anymore
            if (mAddTaskView.isActive()) {
                mAddTaskView.showEmptyTaskError();
            }
        }
    
        @Override
        public boolean isDataMissing() {
            return mIsDataMissing;
        }
    
        private boolean isNewTask() {
            return mTaskId == null;
        }
    
        private void createTask(String title, String description) {
            Task newTask = new Task(title, description);
            if (newTask.isEmpty()) {
                mAddTaskView.showEmptyTaskError();
            } else {
                mUseCaseHandler.execute(mSaveTask, new SaveTask.RequestValues(newTask),
                        new UseCase.UseCaseCallback<SaveTask.ResponseValue>() {
                            @Override
                            public void onSuccess(SaveTask.ResponseValue response) {
                                mAddTaskView.showTasksList();
                            }
    
                            @Override
                            public void onError() {
                                showSaveError();
                            }
                        });
            }
        }
    
        private void updateTask(String title, String description) {
            if (isNewTask()) {
                throw new RuntimeException("updateTask() was called but task is new.");
            }
            Task newTask = new Task(title, description, mTaskId);
            mUseCaseHandler.execute(mSaveTask, new SaveTask.RequestValues(newTask),
                    new UseCase.UseCaseCallback<SaveTask.ResponseValue>() {
                        @Override
                        public void onSuccess(SaveTask.ResponseValue response) {
                            // After an edit, go back to the list.
                            mAddTaskView.showTasksList();
                        }
    
                        @Override
                        public void onError() {
                            showSaveError();
                        }
                    });
        }
    }
    

    现在的Presenter不再直接和Data Layer交互,而是使用各种usecase对model进行操作。同样的,其依然负责对view的回调。

    总结

    至此,本示例的代码结构分析清楚了,在原来mvp的基础上,根据clean的向内依赖的理念,扩展出Domain Layer层,于是就形成了:Presentation Layer 依赖 Domain Layer, Domain Layer 依赖 Data Layer的层次结构。

    相关文章

      网友评论

          本文标题:Android架构分析【三】mvp-clean

          本文链接:https://www.haomeiwen.com/subject/qkdjixtx.html