之前用一張圖分析了Google給出的MVP架構,但是在Google給出的所有案例裡面除了基本的MVP架構還有其它幾種架構,今天就來分析其中的Clean架構。同樣的,網上介紹Clean架構的文章很多,我也就不用文字過多敘述了,還是用一張類圖來分析一下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的對象,到這裡,我們就明白啦!原來也是子類實例化父類的方式。
上面我只是簡單粗略地講述了一下項目中部分模塊的代碼,僅僅是舉個例子,更多的東西需要大家自己用面向對象的思想去理解。我說這些的目的就是想告訴大家,充分運面向對象的思想就可以設計出很多看似複雜的架構和項目,但是不管再怎麼複雜的代碼也肯定是有跡可循的,我們只要抓住了這些設計思想的本質,多看幾遍代碼,一定會豁然開朗!