你的位置:首页 > 操作系统

[操作系统]一张图看Goodle Clean设计架构


  之前用一张图分析了Google给出的MVP架构,但是在Google给出的所有案例里面除了基本的MVP架构还有其它几种架构,今天就来分析其中的Clean架构。同样的,网上介绍Clean架构的文章很多,我也就不用文字过多叙述了,还是用一张类图来分析一下Clean架构的这个案例吧。好了,先直接上图!

  

  上完图,再说一说我对Clean架构的一个理解吧。对比前一篇文章的MVP架构图可以看出,clean在一定程度上继承了mvp的设计思想,但是其抽象程度比mvp更高。初次看这个demo的时候,确实被震撼了一下——原来用Java可以这样写代码!!!跟之前用的一些项目框架和我自己平时写的一些代码对比一下,只能感叹clean的这种设计思想真不是一般的程序员可以想出来的。它对接口、抽象类和实现类之间的实现、继承、调用关系发挥到了一个比较高的层次,它并不是像我们平时写代码那样很直白地写下来,而是充分利用了面向对象的封装性、继承性和多态性,是对面向对象思想的一个高度理解。其实,要说clean复杂,它确实有些难理解,可是如果你真的理解了面向对象思想,那么又会觉得这样的设计完全在情理之中。

  举个例子,在这个案例里面,对实体类的设计就进行了高度的抽象与封装。首先,为所有的实体类设计了基类——UseCase,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, software11  * 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 and14  * limitations under the License.15 */16 17 package com.example.android.architecture.blueprints.todoapp;18 19 /**20  * Use cases are the entry points to the domain layer.21  *22  * @param <Q> the request type23  * @param <P> the response type24 */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, software11  * 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 and14  * 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   @Override38   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, software11  * 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 and14  * 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, software11  * 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 and14  * 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 common30  * {@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   @Override50   public void execute(Runnable runnable) {51     mThreadPoolExecutor.execute(runnable);52   }53 54   @Override55   public <V extends UseCase.ResponseValue> void notifyResponse(final V response,56       final UseCase.UseCaseCallback<V> useCaseCallback) {57     mHandler.post(new Runnable() {58       @Override59       public void run() {60         useCaseCallback.onSuccess(response);61       }62     });63   }64 65   @Override66   public <V extends UseCase.ResponseValue> void onError(67       final UseCase.UseCaseCallback<V> useCaseCallback) {68     mHandler.post(new Runnable() {69       @Override70       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           @Override13           public void onError() {14             mTasksView.showLoadingTasksError();15           }16         });17   }

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

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