设计模式实战之网络架构重构

152 阅读5分钟

前言

  • 之前一直在忙隐私政策,最近鲜有时间去审查以前的代码,这一查才发现自己以前写的网络框架,问题还挺多,就进行了重构
  • BasicLibrary Github地址:github.com/Peakmain/Ba…

旧代码分析

class RetrofitManager {
    companion object {
        //连接超时
        private const val CONNECT_TIMEOUT = 60L

        //阅读超时
        private const val READ_TIMEOUT = 10L

        //写入超时
        private const val WRITE_TIMEOUT = 10L


        fun buildOkHttpClient(): OkHttpClient {
            val builder = OkHttpClient.Builder()
                .connectTimeout(CONNECT_TIMEOUT, TimeUnit.SECONDS)//设置连接超时
                .readTimeout(READ_TIMEOUT, TimeUnit.SECONDS)//读取超时
                .writeTimeout(WRITE_TIMEOUT, TimeUnit.SECONDS)//写入超时
            builder.addInterceptor(HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY))
                .hostnameVerifier(HostnameVerifier { _, _ -> true })
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                val x509 = MyX509()
                builder.sslSocketFactory(getSSLFactory(x509), x509)
            }
            prevBuildOkHttpClient(builder)
            return builder.build()
        }

        /**
         * 空方法,在构建okHttpClient之前可设置一些参数
         */
        fun prevBuildOkHttpClient(builder: OkHttpClient.Builder) {

        }

        private fun getSSLFactory(x509TrustManager: X509ExtendedTrustManager): SSLSocketFactory {
            val trustAllCerts = arrayOf<TrustManager>(x509TrustManager)
            val sslContext = SSLContext.getInstance("SSL")
            sslContext.init(null, trustAllCerts, java.security.SecureRandom())
            return sslContext.socketFactory
        }

        fun <T> createService(service: Class<T>, block: (service: Class<T>) -> T): T {
            return block(service)
        }

        fun <T> createService(service: Class<T>, baseUrl: String): T {
            val retrofit = retrofit2.Retrofit.Builder().baseUrl(baseUrl).client(buildOkHttpClient())
                .addConverterFactory(GsonConverterFactory.create())
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create()).build()
            return retrofit.create(service)
        }

        fun <T> createData(observable: Observable<T>, apiStatus: ApiStatus<T>): Disposable {
            apiStatus.before()
            return observable.subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread()).subscribe({ t ->
                    apiStatus.success(t)
                }, { exception ->
                    exception.printStackTrace()
                    apiStatus.error(Exception(exception))
                })
        }

        fun <T> createData(
            observable: Observable<T>,
            before: () -> Unit,
            success: T.() -> Unit,
            error: (Exception) -> Unit = {}
        ): Disposable {
            before()
            return observable.subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread()).subscribe({ t ->
                    success(t)
                }, { throwable ->
                    error(Exception(throwable))
                })
        }

        fun <T> createBaseEntityData(
            observable: Observable<BaseEntity<T>>,
            apiStatus: ApiBaseStatus<T>
        ): Disposable {
            apiStatus.before()
            return observable.subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread()).subscribe({ t ->
                    checkResult(observable, t, apiStatus)
                }, { exception ->
                    checkError(exception, apiStatus)
                })
        }

        private fun <T> checkError(exception: Throwable, apiStatus: ApiBaseStatus<T>) {
            exception.printStackTrace()
            apiStatus.error(Exception(exception))
        }

        private fun <T> checkResult(
            observable: Observable<BaseEntity<T>>,
            t: BaseEntity<T>,
            apiStatus: ApiBaseStatus<T>
        ) {
            apiStatus.baseData(t)
            when (t.result) {
                ErrorEnum.SUCCESS.code -> {
                    if (t.data != null) {
                        apiStatus.success(t.data)
                    } else {
                        apiStatus.isEmpty()
                    }
                }
                ErrorEnum.TOKEN_ERROR.code -> {
                    //token失效
                    apiStatus.tokenError(observable, apiStatus)
                }
                else -> {
                    apiStatus.ktxRunOnUiThread {
                        error(Exception(t.detail))
                    }
                }

            }
        }
    }
}
功能和架构
  • 主要功能有两个:

    • createService用于创建Retrofit的Service实例
    • createData:用于进行网络请求,并利用rxjava进行主线程和工作线程切换
  • 代码整体其实也没毛病,用起来也是很方便,但是从设计模式角度来说,有不少问题

    • 单一职责:对类来说的,即一个类应该只负责一项职责。我们这里RetrofitManager负责职责有两个了,createService和createData
    • createService方法主要用于创建Service的实例,通常来说都是ok的,但是在某些情况,我不想用你内部创建Service的方法,我想自己创建,此时该如何解决
    • createData方法主要结合rxjava进行网络请求,apiStatus实际是个抽象类
interface BaseApiStatus<T> {
    fun before()

    fun success(t: T)

    fun isEmpty()

    fun loadMore(t: T, isRefresh: Boolean)

    fun error(exception: Exception)
}
abstract class ApiStatus<T>:BaseApiStatus<T>{
    override fun before() {
    }
    override fun isEmpty() {
    }

    override fun loadMore(t: T, isRefresh: Boolean) {
    }

}

我们会发现此扩展性很差,比如:假设此时我们想自定义ApiStatus,例如新增一个token报错,那么此时的需求的RetrofitManager并不能满足我们的需求

单一问题解决

首先我们来单一的问题,既然说一个类只负责一项职责,那我们把CreateService这个方法单独拎出去,不就解决

class CommonRetrofitService {
    fun <T> createService(service: Class<T>, baseUrl: String): T {
        val retrofit = retrofit2.Retrofit.Builder().baseUrl(baseUrl)
            .client(buildOkHttpClient())
            .addConverterFactory(GsonConverterFactory.create())
          .addCallAdapterFactory(RxJava2CallAdapterFactory.create()).build()
        return retrofit.create(service)
    }

}

RetrofitManager直接使用

class RetrofitManager{
    fun <T> createService(service: Class<T>, baseUrl: String): T {
        return CommonRetoriftService().createService(service, baseUrl)
    }
}

策略设计模式

上面代码虽然解决了单一问题,但是却只能用于CommonRetoriftService这一种,如果我们现在自定义一个BaseRetrofitService来创建Service的实例,应该怎么做,我们可以再建一个BaseRetrofitService,然后修改RetrofitManager

class RetrofitManager{
    fun <T> createService(service: Class<T>, baseUrl: String): T {
        return CommonRetoriftService().createService(service, baseUrl)
    }
     fun <T> createBaseService(service: Class<T>, baseUrl: String): T {
        return BaseRetrofitService().createService(service, baseUrl)
    }
}
  • 可是对于客户端来说,总不能把源码下载下来进行修改吧,当然也可以哈,但是一般来说,不会这么做。那应该怎么做呢?
  • 我们的需求是客户端可以自定义创建Service,那么我们就可以提供一个接口给用户,让用户去实现他要实现的逻辑,再让用户将策略发给我们就完成了
  • 这里用到的设计模式是策略设计模式,大家直接看类图 策略设计模式.png
  • 那首先想到的是提供一个接口给用户
interface IRetrofitStrategy {
    fun <T> createService(service: Class<T>, baseUrl: String): T
}

默认情况下我们可以自己创建一个策略,当用户不指定策略的时候,使用默认的策略

class CommonRetrofitStrategy : IRetrofitStrategy {
    override fun <T> createService(service: Class<T>, baseUrl: String): T {
        val retrofit = retrofit2.Retrofit.Builder().baseUrl(baseUrl)
            .client(buildOkHttpClient())
            .addConverterFactory(GsonConverterFactory.create())
            .addCallAdapterFactory(RxJava2CallAdapterFactory.create()).build()
        return retrofit.create(service)
    }

}

我们此时指定一个默认的策略,并提供策略给用户,具体实现有用户决定

class RetrofitManager {
    companion object {
        private var mStrategy: IRetrofitStrategy = CommonRetrofitStrategy()
        fun executeStrategy(strategy: IRetrofitStrategy) {
            this.mStrategy = strategy
        }

        fun <T> createService(service: Class<T>, block: (service: Class<T>) -> T): T {
            return block(service)
        }

        fun <T> createService(service: Class<T>, baseUrl: String): T {
            return mStrategy.createService(service, baseUrl)
        }
    }
}

桥接设计模式

  • 上面我们已经对createService通过策略设计模式进行重构了,那么接下来我们来看看createData如何重构
  • 我们直接看类图 桥接设计模式.png
  • 我们一共两个变化模块
    • 一个是ApiStatus,用户可能会自定义一些状态回调
    • 一个是RetrofitData,用户可能会去自定义创建网络请求
  • 我们将ApiStatus和RetrofitData单独分离出来,让他们单独处理 ApiStatus
interface BaseApiStatus<T> {
    fun before()

    fun success(t: T)

    fun isEmpty()

    fun loadMore(t: T, isRefresh: Boolean)

    fun error(exception: Exception)
}
abstract class ApiStatus<T>:BaseApiStatus<T>{
    override fun before() {
    }
    override fun isEmpty() {
    }

    override fun loadMore(t: T, isRefresh: Boolean) {
    }

}

RetrofitData

abstract class AbstractRetrofitData<T> {

    abstract fun createData(
        observable: Observable<T>
    ): Disposable
}
class CommonRetrofitData<T> :
    AbstractRetrofitData<T>{

    override fun createData(observable: Observable<T>): Disposable {
        apiStatus.before()
        return observable.subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread()).subscribe({ t ->
                apiStatus.success(t)
            }, { exception ->
                exception.printStackTrace()
                apiStatus.error(Exception(exception))
            })
    }
}

此时我们需要将两个单独变化的模块用桥建立关系,其实很简单,就是抽象类持有ApiStatus接口即可

abstract class AbstractRetrofitData<T>(apiStatus: BaseApiStatus<T>) {
    protected var mBaseApiStatus: BaseApiStatus<T> = apiStatus
    abstract fun createData(
        observable: Observable<T>
    ): Disposable
}

RetrofitManager持有AbstractRetrofitData即可

class RetrofitManager {
    ....
   fun <T> createData(
        observable: Observable<T>,
        retrofitData: AbstractRetrofitData<T>
       ): Disposable {
        return retrofitData.createData(observable)
    }
}

结尾

  • 当我们在空闲的时候,还是要多抽时间去审查自己写的代码,多思考能不能换种方式去实现达到更好的解耦和效果
  • 这是我的github:github.com/peakmain ,欢迎大家点赞哦