Goodle Clean设计架构

23  * @param <P> the response type
24 */
25 public abstract class UseCase<Q extends UseCase.RequestValues, P extends UseCase.ResponseValue> {
26
27 private Q mRequestValues;
28
29 private UseCaseCallback<P> mUseCaseCallback;
30
31 public void setRequestValues(Q requestValues) {
32 mRequestValues = requestValues;
33 }
34
35 public Q getRequestValues() {
36 return mRequestValues;
37 }
38
39 public UseCaseCallback<P> getUseCaseCallback() {
40 return mUseCaseCallback;
41 }
42
43 public void setUseCaseCallback(UseCaseCallback<P> useCaseCallback) {
44 mUseCaseCallback = useCaseCallback;
45 }
46
47 void run() {
48 executeUseCase(mRequestValues);
49 }
50
51 protected abstract void executeUseCase(Q requestValues);
52
53 /**
54 * Data passed to a request.
55 */
56 public interface RequestValues {
57 }
58
59 /**
60 * Data received from a request.
61 */
62 public interface ResponseValue {
63 }
64
65 public interface UseCaseCallback<R> {
66 void onSuccess(R response);
67 void onError();
68 }
69 }

  实体基类UseCase的设计用了泛型和接口,仅仅设计了两个字段mRequestValues和mUseCaseCallback。其中,mRequestValues代表数据请求参数,用泛型进行了封装,它其实也是一个类的对象;mUseCaseCallback代表请求结果,同样的,它也是一个类的对象,只不过这个类是用接口的形式进行抽象和封装的。同时,UseCase中定义抽象方法executeUseCase()作为实体操作的入口。

  接下来,我们随便看一个UseCase的实现类,就拿ActivateTask来说,ActivateTask继承了UseCase,其实现代码如下:

 1 /*
2 * Copyright 2016, The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 package com.example.android.architecture.blueprints.todoapp.tasks.domain.usecase;
18
19 import android.support.annotation.NonNull;
20
21 import com.example.android.architecture.blueprints.todoapp.UseCase;
22 import com.example.android.architecture.blueprints.todoapp.data.source.TasksRepository;
23
24 import static com.google.common.base.Preconditions.checkNotNull;
25
26 /**
27 * Marks a task as active (not completed yet).
28 */
29 public class ActivateTask extends UseCase<ActivateTask.RequestValues, ActivateTask.ResponseValue> {
30
31 private final TasksRepository mTasksRepository;
32
33 public ActivateTask(@NonNull TasksRepository tasksRepository) {
34 mTasksRepository = checkNotNull(tasksRepository, "tasksRepository cannot be null!");
35 }
36
37 @Override
38 protected void executeUseCase(final RequestValues values) {
39 String activeTask = values.getActivateTask();
40 mTasksRepository.activateTask(activeTask);
41 getUseCaseCallback().onSuccess(new ResponseValue());
42 }
43
44 public static final class RequestValues implements UseCase.RequestValues {
45
46 private final String mActivateTask;
47
48 public RequestValues(@NonNull String activateTask) {
49 mActivateTask = checkNotNull(activateTask, "activateTask cannot be null!");
50 }
51
52 public String getActivateTask() {
53 return mActivateTask;
54 }
55 }
56
57 public static final class ResponseValue implements UseCase.ResponseValue { }
58 }

  可以看到,在ActivateTask 中,实现了父类UseCase的两个接口RequestValues 和ResponseValue ,这两个类将分别作为最终的实体请求对象类和返回结果对象类,同时,UseCase中的抽象方法executeUseCase()也被实现。因为实现的代码里面加入了泛型和接口,所以看起来会比较复杂,但是说到底无非就是继承和实现的关系,仅此而已。通过这种面向接口的设计方式,可以让我们的代码看起来结构更清晰、更统一。

  接下来,我们可以看一下这个项目中的任务执行类UseCaseThreadPoolScheduler,同样,UseCaseThreadPoolScheduler的设计采用了面向接口的方式,它实现了seCaseScheduler接口,UseCaseScheduler和UseCaseThreadPoolScheduler的实现分别如下:

 1 /*
2 * Copyright 2016, The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 package com.example.android.architecture.blueprints.todoapp;
18
19 /**
20 * Interface for schedulers, see {@link UseCaseThreadPoolScheduler}.
21 */
22 public interface UseCaseScheduler {
23
24 void execute(Runnable runnable);
25
26 <V extends UseCase.ResponseValue> void notifyResponse(final V response,
27 final UseCase.UseCaseCallback<V> useCaseCallback);
28
29 <V extends UseCase.ResponseValue> void onError(
30 final UseCase.UseCaseCallback<V> useCaseCallback);
31 }
 1 /*
2 * Copyright 2016, The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 package com.example.android.architecture.blueprints.todoapp;
18
19 import android.os.Handler;
20
21 import java.util.concurrent.ArrayBlockingQueue;
22 import java.util.concurrent.Executors;
23 import java.util.concurrent.ThreadPoolExecutor;
24 import java.util.concurrent.TimeUnit;
25
26 /**
27 * Executes asynchronous tasks using a {@link ThreadPoolExecutor}.
28 * <p>
29 * See also {@link Executors} for a list of factory methods to create common
30 * {@link java.util.concurrent.ExecutorService}s for different scenarios.
31 */
32 public class UseCaseThreadPoolScheduler implements UseCaseScheduler {
33
34 private final Handler mHandler = new Handler();
35
36 public static final int POOL_SIZE = 2;
37
38 public static final int MAX_POOL_SIZE = 4;
39
40 public static final int TIMEOUT = 30;
41
42 ThreadPoolExecutor mThreadPoolExecutor;
43
44 public UseCaseThreadPoolScheduler() {
45 mThreadPoolExecutor = new ThreadPoolExecutor(POOL_SIZE, MAX_POOL_SIZE, TIMEOUT,
46 TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(POOL_SIZE));
47 }
48
49 @Override
50 public void execute(Runnable runnable) {
51 mThreadPoolExecutor.execute(runnable);
52 }
53
54 @Override
55 public <V extends UseCase.ResponseValue> void notifyResponse(final V response,
56 final UseCase.UseCaseCallback<V> useCaseCallback) {
57 mHandler.post(new Runnable() {
58 @Override
59 public void run() {
60 useCaseCallback.onSuccess(response);
61 }
62 });
63 }
64
65 @Override
66 public <V extends UseCase.ResponseValue> void onError(
67 final UseCase.UseCaseCallback<V> useCaseCallback) {
68 mHandler.post(new Runnable() {
69 @Override
70 public void run() {
71 useCaseCallback.onError();
72 }
73 });
74 }
75
76 }

  可以看出,UseCaseThreadPoolScheduler实现了UseCaseScheduler中的三个抽象方法。

  接下来,我们再看看UseCaseHandler这个类,在UseCaseHandler中,通过子类实例化父类的形式,用UseCaseThreadPoolScheduler实例化了UseCaseScheduler对象。UseCaseHandler的代码如下:

/*
* Copyright 2016, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/ package com.example.android.architecture.blueprints.todoapp; import com.example.android.architecture.blueprints.todoapp.util.EspressoIdlingResource; /**
* Runs {@link UseCase}s using a {@link UseCaseScheduler}.
*/
public class UseCaseHandler { private static UseCaseHandler INSTANCE; private final UseCaseScheduler mUseCaseScheduler; public UseCaseHandler(UseCaseScheduler useCaseScheduler) {
mUseCaseScheduler = useCaseScheduler;
} public <T extends UseCase.RequestValues, R extends UseCase.ResponseValue> void execute(
final UseCase<T, R> useCase, T values, UseCase.UseCaseCallback<R> callback) {
useCase.setRequestValues(values);
useCase.setUseCaseCallback(new UiCallbackWrapper(callback, this)); // The network request might be handled in a different thread so make sure
// Espresso knows
// that the app is busy until the response is handled.
EspressoIdlingResource.increment(); // App is busy until further notice mUseCaseScheduler.execute(new Runnable() {
@Override
public void run() { useCase.run();
// This callback may be called twice, once for the cache and once for loading
// the data from the server API, so we check before decrementing, otherwise
// it throws "Counter has been corrupted!" exception.
if (!EspressoIdlingResource.getIdlingResource().isIdleNow()) {
EspressoIdlingResource.decrement(); // Set app as idle.
}
}
});
} public <V extends UseCase.ResponseValue> void notifyResponse(final V response,
final UseCase.UseCaseCallback<V> useCaseCallback) {
mUseCaseScheduler.notifyResponse(response, useCaseCallback);
} private <V extends UseCase.ResponseValue> void notifyError(
final UseCase.UseCaseCallback<V> useCaseCallback) {
mUseCaseScheduler.onError(useCaseCallback);
} private static final class UiCallbackWrapper<V extends UseCase.ResponseValue> implements
UseCase.UseCaseCallback<V> {
private final UseCase.UseCaseCallback<V> mCallback;
private final UseCaseHandler mUseCaseHandler; public UiCallbackWrapper(UseCase.UseCaseCallback<V> callback,
UseCaseHandler useCaseHandler) {
mCallback = callback;
mUseCaseHandler = useCaseHandler;
} @Override
public void onSuccess(V response) {
mUseCaseHandler.notifyResponse(response, mCallback);
} @Override
public void onError() {
mUseCaseHandler.notifyError(mCallback);
}
} public static UseCaseHandler getInstance() {
if (INSTANCE == null) {
INSTANCE = new UseCaseHandler(new UseCaseThreadPoolScheduler());
}
return INSTANCE;
}
}

  从上面的代码中,我们可以看到,声明的变量mUseCaseScheduler是UseCaseScheduler的对象,但是在构建UseCaseHandler对象的时候,传入的参数却是UseCaseThreadPoolScheduler对象,即用UseCaseThreadPoolScheduler实例化了UseCaseScheduler对象。然后,对mUseCaseScheduler的所有操作都转化成了对UseCaseThreadPoolScheduler对象的操作。

  然后,我们仔细看UseCaseHandler的实现的代码,我们会发现其实对实体进行操作的入口就是execute()方法!因为这个方法里面调用了UseCase的run(),而UseCase的run()最终调用了UseCase的executeUseCase()。通过刚刚的分析,我们应该知道,我们实际上操作的实体应该是UseCase的实现类,而不是UseCase类本身,那么这中间是通过什么方式将对UseCase的操作转移到UseCase的实现类上面的呢?我们会发现UseCaseHandler的execute()传入了UseCase对象作为参数,好的,那么我们就看看execute()是在哪里被调用的吧!

  经过追踪,我们看到在TasksPresenter类中调用了此方法,调用处的代码如下:

 1 @Override
2 public void activateTask(@NonNull Task activeTask) {
3 checkNotNull(activeTask, "activeTask cannot be null!");
4 mUseCaseHandler.execute(mActivateTask, new ActivateTask.RequestValues(activeTask.getId()),
5 new UseCase.UseCaseCallback<ActivateTask.ResponseValue>() {
6 @Override
7 public void onSuccess(ActivateTask.ResponseValue response) {
8 mTasksView.showTaskMarkedActive();
9 loadTasks(false, false);
10 }
11
12 @Override
13 public void onError() {
14 mTasksView.showLoadingTasksError();
15 }
16 });
17 }

  可以看到,我们传入的参数实际上是UseCase的实现类ActivateTask的对象,到这里,我们就明白啦!原来也是子类实例化父类的方式。

  上面我只是简单粗略地讲述了一下项目中部分模块的代码,仅仅是举个例子,更多的东西需要大家自己用面向对象的思想去理解。我说这些的目的就是想告诉大家,充分运面向对象的思想就可以设计出很多看似复杂的架构和项目,但是不管再怎么复杂的代码也肯定是有迹可循的,我们只要抓住了这些设计思想的本质,多看几遍代码,一定会豁然开朗!

Goodle Clean设计架构的更多相关文章

  1. 一张图看Goodle Clean设计架构

    之前用一张图分析了Google给出的MVP架构,但是在Google给出的所有案例里面除了基本的MVP架构还有其它几种架构,今天就来分析其中的Clean架构.同样的,网上介绍Clean架构的文章很多,我 ...

  2. Android App的设计架构:MVC,MVP,MVVM与架构经验谈

    相关:http://www.cnblogs.com/wytiger/p/5996876.html 和MVC框架模式一样,Model模型处理数据代码不变在Android的App开发中,很多人经常会头疼于 ...

  3. Kubernetes系列02—Kubernetes设计架构和设计理念

    本文收录在容器技术学习系列文章总目录 1.Kubernetes设计架构 Kubernetes集群包含有节点代理kubelet和Master组件(APIs, scheduler, etc),一切都基于分 ...

  4. 13 JSP、MVC开发模式、EL表达式和JSPL标签+软件设计架构---学习笔记

    1.JSP (1)JSP概念:Java Server Pages 即java服务器端页面可以理解为:一个特殊的页面,其中既可以指定定义html标签,又可以定义java代码用于简化书写!!! (2)原理 ...

  5. (转)Kubernetes设计架构

    转:https://www.kubernetes.org.cn/kubernetes设计架构 Kubernetes集群包含有节点代理kubelet和Master组件(APIs, scheduler, ...

  6. Android App的设计架构:MVC,MVP,MVVM与架构AAAAA

    1. 架构设计的目的1.1 通过设计使程序模块化,做到模块内部的高聚合和模块之间的低耦合.1.2 这样做的好处是使得程序在开发的过程中,开发人员只需要专注于一点,提高程序开发的效率,并且更容易进行后续 ...

  7. MVC:开发模式&&三层架构:软件设计架构

    MVC:开发模式 jsp演变历史 早期只有servlet,只能使用response输出标签数据,非常麻烦 后来又jsp,简化了Servlet的开发,如果过度使用jsp,在jsp中即写大量的java代码 ...

  8. 解决Tomcat10.0.12源码编译问题进而剖析其优秀分层设计架构

    概述 Tomcat.Jetty.Undertow这几个都是非常有名实现Servlet规范的应用服务器,Tomcat本身也是业界上非常优秀的中间件,简单可将Tomcat看成是一个Http服务器+Serv ...

  9. 微信、陌陌等著名IM软件设计架构详解

    对微信.陌陌等进行了分析,发出来分享一下(时间有些久了) 电量:对于移动设备最大的瓶颈就是电量了.因为用户不可能随时携带电源,充电宝.所以必须考虑到电量问题.那就要检查我们工程是不是有后台运行,心跳包 ...

随机推荐

  1. openstack、kvm CentOS升级内核

    openstack平台需要使用各种Linux发行版镜像,其制作方法主要有两种,要么是基于各大Linux发行版ISO光盘手动制作,要么是使用官方提供的制作好镜像进行修改 问题 FATAL: Module ...

  2. DataTable Select查询

    如果是Excel中数据,空数据为DBNull.Value 查询:dt.Select("name is null") 查询长度:dt.Select("len(convert ...

  3. elk 搭建

    elk 平台搭建: ELK平台搭建 系统环境 System: Centos release 6.7 (Final) ElasticSearch: 2.1.0 Logstash: 2.1.1 Kiban ...

  4. Vijos P1680距离

    题目 背景 简单的DP 描述 设有字符串X,我们称在X的头尾及中间插入任意多个空格后构成的新字符串为X的扩展串,如字符串X为”abcbcd”,则字符串“abcb_c_”,“_a_bcbcd_”和“ab ...

  5. iOS中谓词的使用

    Cocoa提供了一个类NSPredicate类,该类主要用于指定过滤器的条件,该对象可以准确的描述所需条件,对每个对象通过谓词进行筛选,判断是否与条件相匹配.谓词表示计算真值或假值的函数.在cocoa ...

  6. Android经常使用UI组件 - TextView

    TextView是Android里面用的最多的UI组件,一般使用在须要显示一些信息的时候,其不能输入,仅仅能初始设定或者在程序中改动. 实例:TextViewDemo 执行效果: 代码清单: 布局文件 ...

  7. HDU 5045 Contest(状压DP)

    Problem Description In the ACM International Collegiate Programming Contest, each team consist of th ...

  8. Linux chmod命令具体解释

    仅仅能文件属主或特权用户才干使用该功能来改变文件存取模式.mode能够是数字形式或以who opcode permission形式表示. who是可选的,默认是a(全部用户). 仅仅能选择一个opco ...

  9. UIAlertView、UIActionSheet兼容iOS8

    链接地址:http://blog.csdn.net/nextstudio/article/details/39959895?utm_source=tuicool 1.前言 iOS8新增了UIAlert ...

  10. Android的ProgressBar

    注意点: 必须在setContentView 前面设置,否则会报错. 重要的方法: progress.incrementProgressBy(int diff);//参数为进度数,进度满了为100.不 ...