• 一张图看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, 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  * Use cases are the entry points to the domain layer.
    21  *
    22  * @param <Q> the request type
    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的对象,到这里,我们就明白啦!原来也是子类实例化父类的方式。

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

  • 相关阅读:
    AIO: 新版AllInOne Code Framework 2009411新鲜出炉
    AIO: 最新AllInOne Code Framework 2009427 问世了!
    Best Practice: A Summary of Normal Way to Debug Memory Leak
    AllInOne Code Framework (http://cfx.codeplex.com)
    AllInOne Code Framework(AIO) Team 祝博客园所有同仁节日快乐!另:AIO 最新 Release 再次问世了!
    Trick: 巧用.NET Reflector, SOS Debugging找出和某一个TransactionScope绑定的SqlConnection objects以及SqlConnection中开着的SqlDataReader objects (Find all SqlConnection objects associated with a TransactionScope)
    AllInOne Code Framework: 微软一站式开发技术框架解决方案 2009614 新增sample code简介
    AllInOne Code Framework: 微软一站式开发技术框架解决方案 200978 新增sample code简介
    AIO: AllInOne Code Framework 2009510 最新问世!
    poj1737 Connected Graph[转] ***
  • 原文地址:https://www.cnblogs.com/1992monkey/p/5585233.html
Copyright © 2020-2023  润新知