配色: 字号:
Retrofit源码设计模式解析
2016-12-03 | 阅:  转:  |  分享 
  
Retrofit源码设计模式解析



第一部分



Retrofit通过注解的方法标记HTTP请求参数,支持常用HTTP方法,统一返回值解析,支持异步/同步的请求方式,将HTTP请求对象化,参数化。真正执行网络访问的是Okhttp,Okhttp支持HTTP&HTTP2,因此,使用Retrofit可以支持REST、HTTPS及SPDY。





本文主要从设计模式的角度分享对Retrofit源码的一些理解。



外观模式

建造者模式

代理模式

简单工厂模式

工厂模式

抽象工厂模式

一、外观模式

在封装某些特定功能的子系统时,外观模式是一种很好的设计规范。即该子系统的外部与内部通信时通过一个统一的对象进行。Retrofit是整个库的一个入口类,Retrofit库的使用基本都是围绕着这个类。外观模式具有高内聚、低耦合的特性,对外提供简单统一的接口,隐蔽了子系统具体的实现、隔离变化。



Retrofit的外观模式的UML类图如下所示。



image





Retrofit对客户端模块(Client1、Client2……)提供统一接口,Retrofit类内部封装了ServiceMethod、CallAdapter和Converter等组件。并且,CallAdapter和Converter都是抽象为接口,用户可以扩展自定义的实现。正如官方文档中的示例:



Retrofitretrofit=newRetrofit.Builder()

.baseUrl("https://api.github.com/")

.build();

附:举个栗子,说明下外观模式在封装条形码/二维码扫描功能时的应用。



Android中的条形码/二维码扫描功能通常会基于zxing库进行封装,定义CaptureActivity,统一提供扫码功能,并返回扫描结果。客户端使用该封装只需两步:首先,通过Intent启动CaptureActivity;然后,在onActivityResult中处理扫描结果。



Intentintent=newIntent(MainActivity.this,CaptureActivity.class);

startActivityForResult(intent,REQUESTCODE);

if(requestCode==REQUESTCODE&&resultCode==RESULT_OK){

Bundlebundle=data.getExtras();

StringscanResult=bundle.getString(CaptureActivity.RESULT);

helloWorld.setText(scanResult);

}



客户端不需要处理任何跟摄像头控制、调焦、图片处理、条形码解析等相关的问题。CaptureActivity提供了所有扫描相关的功能。



二、建造者模式

设计模式分为三种类型:创建型模式、结构型模式和行为型模式。建造者模式属于创建型模式,将构建复杂对象的过程和它的部件解耦,使构建过程和部件的表示隔离。Retrofit内部包含Retrofit.Builder,Retrofit包含的域都能通过Builder进行构建。经典设计模式(《设计模式:可复用面向对象软件的基础》)中建造者模式有四种角色:



Product产品类——该类为一般为抽象类,定义Product的公共属性配置;

Builder建造类——该类同样为抽象类,规范Product的组建,一般由子类实现具体Product的构建过程;

ConcreteBuilder实际建造类——继承自Builder,构建具体的Product;

Director组装类——统一组装过程。

在Retrofit类中,Retrofit直接对应Product,并没有基于抽象Product进行扩展;Retrofit.Builder对应ConcreteBuilder,也没有基于抽象Builder进行扩展,同时省略了Director,并在Retrofit.Builder每个setter方法都返回自身,使得客户端代码可以链式调用,整个构建过程更加简单。



Retrofitretrofit=newRetrofit.Builder()

.baseUrl("https://api.github.com/")

.build();

举个栗子:笔者基于Retrofit封装适合自身业务的库时,由于需要配置基础URL、默认超时时间、拦截器以及是否添加转换器等,也采用相同的建造者模式。



复制代码

publicclassNetWork{



//基础URL设置

privateStringbaseUrl;

//默认超时时间

privatelongtimeout;



/

NetWork构建者

/

publicstaticclassBuilder{

//基础URL设置

privateStringbaseUrl;

//默认超时时间

privatelongtimeout=5;



/

baseUrl为必填项



@parambaseUrl基础Url

/

publicBuilder(StringbaseUrl){

this.baseUrl=baseUrl;

}



publicBuildertimeout(longtimeout){

this.timeout=timeout;

returnthis;

}



publicNetWorkbuild(){

returnnewNetWork(this);

}

}



/

构造器



@parambuilder构造builder

/

privateNetWork(Builderbuilder){

this.baseUrl=builder.baseUrl;

this.timeout=builder.timeout;

}

复制代码

三、代理模式

代理模式属于上述提到的结构型模式。当无法或不想直接访问某个对象,或者访问某个对象比较复杂的时候,可以通过一个代理对象来间接访问,代理对象向客户端提供和真实对象同样的接口功能。经典设计模式中,代理模式有四种角色:



Subject抽象主题类——申明代理对象和真实对象共同的接口方法;

RealSubject真实主题类——实现了Subject接口,真实执行业务逻辑的地方;

ProxySubject代理类——实现了Subject接口,持有对RealSubject的引用,在实现的接口方法中调用RealSubject中相应的方法执行;

Cliect客户端类——使用代理对象的类。

代理模式分为静态代理和动态代理,严格按照上述角色定义编写的代码属于静态代理,即在代码运行前ProxySubject代理类的class编译文件就已存在。Retrofit使用的是动态代理,是通过反射机制来动态生成方法接口的代理对象的。动态代理的实现是通过JDK提供的InvocationHandler接口,实现该接口重写其调用方法invoke。



复制代码

publicTcreate(finalClassservice){

Utils.validateServiceInterface(service);

if(validateEagerly){

eagerlyValidateMethods(service);

}

return(T)Proxy.newProxyInstance(service.getClassLoader(),newClass[]{service},

newInvocationHandler(){

privatefinalPlatformplatform=Platform.get();

@OverridepublicObjectinvoke(Objectproxy,Methodmethod,Object...args)

throwsThrowable{

//IfthemethodisamethodfromObjectthendefertonormalinvocation.

if(method.getDeclaringClass()==Object.class){

returnmethod.invoke(this,args);

}

if(platform.isDefaultMethod(method)){

returnplatform.invokeDefaultMethod(method,service,proxy,args);

}

ServiceMethodserviceMethod=loadServiceMethod(method);

OkHttpCallokHttpCall=newOkHttpCall<>(serviceMethod,args);

returnserviceMethod.callAdapter.adapt(okHttpCall);

}

});

}

复制代码

Proxy.newProxyInstance返回接口的动态代理类,InvocationHandler的invoke方法处理method分为三种情况:1)Object的方法,直接返回Object的实现;2)判断是否Java8支持的DefaultMethod;3)创建OkHttpCall,通过ServiceMethod转换为接口的动态代理类。



使用Retrofit的客户端通过create方法获取自定义HTTP请求的动态代理类,是客户端代码中最重要的部分之一。这里有三个重要组件:



ServiceMethod

OKHttpCall

ServiceMethod.callAdapter

ServiceMethod用于处理ApiService上定义的注解,参数等,得到这个ServiceMethod之后,传给OkHttpCall,这个OkHttpCall就是对Okhttp的网络请求封装的一个类。



复制代码

ServiceMethodloadServiceMethod(Methodmethod){

ServiceMethodresult;

synchronized(serviceMethodCache){

result=serviceMethodCache.get(method);

if(result==null){

result=newServiceMethod.Builder(this,method).build();

serviceMethodCache.put(method,result);

}

}

returnresult;

}

复制代码

关于同步,这里有两点值得学习:

采用synchronized将锁加在serviceMethodCache上,而不是加到方法上。(直接synchronized加到方法可能会引起Dos,当然,你可以说客户端不用考虑这种问题);

serviceMethodCache是用于缓存HTTP请求方法的,初始方法采用LinkedHashMap而不是普通的HashMap。

privatefinalMapserviceMethodCache=newLinkedHashMap<>();

LinkedHashMap中有一个字段accessOrder,表示是否按照访问顺序进行重排序。默认为false,表示按插入时间顺序排序,如果设置为true,则进行重排序。最近最少访问的元素会被放到队尾,最先删除,而最常访问的元素,则放到队头,最后删除。

把ServiceMethod传给OkHttpCall实际上就是把网络接口所需要的URL,参数等条件传给了OkHttpCall,就可以进行网络请求了。ServiceMethod中如果没有配置CallAdapter,则使用默认的DefaultCallAdapterFactory,得到的结果是Call



回到正题,你可能已经发现,create方法采用的代理模式和正常的代理模式并不一样,正常的代理模式只是对真实对象的一层控制,这个真实对象是实现对应的接口的,而这里并没有真实的对象,它把方法调用最终全部转发到OKHttp了。



四、简单工厂模式

本文后面的部分将集中在简单工厂模式、工厂模式和抽象工厂模式,它们都属于创建型模式,其主要功能都是将对象的实例化部分抽取出来。简单工厂模式也称为静态工厂模式,包含三种角色:



Factory工厂角色——负责实现创建所有实例的内部逻辑;

Product抽象产品角色——创建的所有对象的父类,负责描述所有实例所共有的公共接口;

ConcreteProduct具体产品角色——继承自Product,负责具体产品的创建。

简单工厂模式是一种很常见、很简单的设计模式,以Platform类为例,其首先包含静态域PLATFORM,并通过静态返回供客户端调用。



privatestaticfinalPlatformPLATFORM=findPlatform();



staticPlatformget(){

returnPLATFORM;

}

findPlatform其实就是一个静态工厂方法,根据Class.forName是否抛出ClassNotFoundException来判断不同的平台。



复制代码

privatestaticPlatformfindPlatform(){

try{

Class.forName("android.os.Build");

if(Build.VERSION.SDK_INT!=0){

returnnewAndroid();

}

}catch(ClassNotFoundExceptionignored){

}

try{

Class.forName("java.util.Optional");

returnnewJava8();

}catch(ClassNotFoundExceptionignored){

}

try{

Class.forName("org.robovm.apple.foundation.NSObject");

returnnewIOS();

}catch(ClassNotFoundExceptionignored){

}

returnnewPlatform();

}

复制代码

而Android、Java8、IOS相当于ConcreteProduct的角色,继承自抽象产品类Platform。



Java8:



staticclassJava8extendsPlatform{}

Android:



staticclassAndroidextendsPlatform{}

IOS:



staticclassIOSextendsPlatform{}

五、工厂模式

上述简单工厂模式中的工厂方法类只有一个Factory,仍以Platform为例,如果在增加一种平台:WindowsPhone,那么就需要修改findPlatform方法,添加WindowsPhone类的创建。



这种修改模式不符合“开闭原则”,即对扩展开放,对修改封闭。本着可扩展的原则,抽象Factory类的公共部分为抽象类,然后不同的平台工厂继承自抽象的Factory。需要不用的平台就调用不同的工厂方法,这就是工厂模式。即对简单工厂中的工厂类进行抽象:



Factory抽象工厂类——负责工厂类的公共部分;

ConcreteFactory具体工厂类——继承自Factory,实现不同特性的工厂。

如果按照工厂模式,通过PlatformFactory类抽象工厂方法,那么大概会是这样:



publicabstractclassPlatformFactory{

abstractPlatformfindPlatform();

}

Android、Java8、IOS或者可能新增的WindowsPhone工厂继承自PlatformFactory。



复制代码

publicclassAndroidFactoryextendsPlatformFactory{



@Override

PlatformfindPlatform(){

try{

Class.forName("android.os.Build");

if(Build.VERSION.SDK_INT!=0){

returnnewAndroid();

}

}catch(ClassNotFoundExceptionignored){

}

returnnewPlatform();

}

}

复制代码

客户端需要不同的平台对象就调用不同的工厂,但客户端如果调用错误,比如在Android上调用了IOS的工厂,那么就会得到一个Platform的实例,这并不符合要求,这种写法增加了客户端的难度,同时,需要引入抽象层,增加多个具体工厂类,维护成本也更大。



所以,在使用工厂设计模式时,一定需要衡量利弊,在特定的场景选择最合适的设计模式。那么,Retrofit中使用工厂模式的经典例子又是什么呢?CallAdapter!



复制代码

publicinterfaceCallAdapter{



TyperesponseType();

Tadapt(Callcall);



abstractclassFactory{

publicabstractCallAdapterget(TypereturnType,Annotation[]annotations,Retrofitretrofit);



protectedstaticTypegetParameterUpperBound(intindex,ParameterizedTypetype){

returnUtils.getParameterUwww.wang027.compperBound(index,type);

}



protectedstaticClassgetRawType(Typetype){

returnUtils.getRawType(type);

}

}

}

复制代码

CallAdapter是什么呢?见名知义,对Call进行适配,这里涉及到适配器模式,下节会着重说明。这里关注CallAdapter.Factory,CallAdapter.Factory对应上述角色中的Factory抽象工厂类,包含两个静态工具方法getParameterUpperBound、getRawType和抽象方法get。



get方法返回不同类型的CallAdapter,RxJavaCallAdapterFactory返回CallAdapter>,DefaultCallAdapterFactory返回CallAdapter>。



复制代码

publicfinalclassRxJavaCallAdapterFactoryextendsCallAdapter.Factory{



//省略代码

@Override

publicCallAdapterget(TypereturnType,Annotation[]annotations,Retrofitretrofit){



//省略代码

CallAdapter>callAdapter=getCallAdapter(returnType,scheduler);

//省略代码

returncallAdapter;

}



privateCallAdapter>getCallAdapter(TypereturnType,Schedulerscheduler){

//省略代码

}

}

复制代码

如果需要增加新的CallAdapter,继承自CallAdapter.Factory,覆盖get方法即可。符合面向对象软件设计的“开闭原则”。



六、抽象工厂模式

在配置Retrofit时,除了上述提到的CallAdapter,还需要addConverterFactory。Retrofit调用Okhttp时,将请求内容由T转换为okhttp3.RequestBody,将返回内容由okhttp3.ResponseBody转换为T,Converter是就是负责转换的类。Retrofit官方文档中给出了多种不同实现的转换器类,如下:



Gson:com.squareup.retrofit2:converter-gson

Jackson:com.squareup.retrofit2:converter-jackson

Moshi:com.squareup.retrofit2:converter-moshi

Protobuf:com.squareup.retrofit2:converter-protobuf

Wire:com.squareup.retrofit2:converter-wire

SimpleXML:com.squareup.retrofit2:converter-simplexml

Scalars(primitives,boxed,andString):com.squareup.retrofit2:converter-scalars

其中包含JSON转换的Gson、Jackson,负责PB解析的Protobuf,负责XML解析的SimpleXML,这些类具有相同点,均继承自Converter.Factory。



复制代码

publicinterfaceConverter{

Tconvert(Fvalue)throwsIOException;



abstractclassFactory{

publicConverterresponseBodyConverter(Typetype,Annotation[]annotations,

Retrofitretrofit){

returnnull;

}



publicConverterrequestBodyConverter(Typetype,

Annotation[]parameterAnnotations,Annotation[]methodAnnotations,Retrofitretrofit){

returnnull;

}



publicConverterstringConverter(Typetype,Annotation[]annotations,Retrofitretrofit){

returnnull;

}

}

}

复制代码

这里注意下Converter.Factory与CallAdapter.Factory的区别,CallAdapter.Factory只有一个抽象的get方法返回CallAdapter,Converter.Factory有三个方法,分别返回Converter、Converter和Converter



相比于工厂模式,具体工厂负责生产具体的产品,每一个具体工厂对应一种具体产品,工厂方法也具有唯一性,一般情况下,一个具体工厂中只有一个工厂方法或者一组重载的工厂方法。但是有时候我们需要一个工厂可以提供多个产品对象,而不是单一的产品对象,例如:上述Converter.Factory需要同时提供请求内容和返回内容的转换类,这时,就需要考虑抽象工厂模式。抽象工厂模式同样包含四种角色:



AbstractFactory:抽象工厂

ConcreteFactory:具体工厂

AbstractProduct:抽象产品

Product:具体产品

标准抽象工厂模式的UML图如下:(图片来自互联网)



AbatractFactory



这里以GsonConverterFactory为例进行说明。GsonConverterFactory对应ConcreteFactory具体工厂,表示Gson转换类的工厂,GsonConverterFactory继承自AbstractFactory抽象工厂——Converter.Factory,重写了requestBodyConverter方法和responseBodyConverter方法,相当于上图中的createProductA和createProductB。



复制代码

publicfinalclassGsonConverterFactoryextendsConverter.Factory{

//省略代码

@Override

publicConverterresponseBodyConverter(Typetype,Annotation[]annotations,

Retrofitretrofit){

TypeAdapteradapter=gson.getAdapter(TypeToken.get(type));

returnnewGsonResponseBodyConverter<>(gson,adapter);

}



@Override

publicConverterrequestBodyConverter(Typetype,

Annotation[]parameterAnnotations,Annotation[]methodAnnotations,Retrofitretrofit){

TypeAdapteradapter=gson.getwww.baiyuewang.netAdapter(TypeToken.get(type));

returnnewGsonRequestBodyConverter<>(gson,adapter);

}

}

复制代码

这里GsonRequestBodyConverter对应ProductA,GsonResponseBodyConverter对应ProductB。



finalclassGsonResponseBodyConverterimplementsConverter{

//省略代码

}

finalclassGsonRequestBodyConverterimplementsConverter{

//省略代码

}

Converter对应抽象产品AbstractProductA,Converter对应抽象产品AbstractProductB。



第二部分

一、适配器模式

在上篇说明CallAdapter.Factory使用工厂模式时,提到CallAdapter本身采用了适配器模式。适配器模式将一个接口转换成客户端希望的另一个接口,使接口本不兼容的类可以一起工作。



Call接口是Retrofit内置的发送请求给服务器并且返回响应体的调用接口,包括同步、异步请求,查询、取消、复制等功能。



复制代码

publicinterfaceCallextendsCloneable{

//同步执行请求

Responseexecute()throwsIOException;

//异步执行请求

voidenqueue(Callbackcallback);

//省略代码



//取消请求

voidcancel();

//复制请求

Callclone();

}

复制代码

而客户端可能希望更适合业务逻辑的接口回调,比如响应式的接口回调。那么,就需要对Call进行转换,CallAdapter就上场了。CallAdapter包含两个方法:



复制代码

publicinterfaceCallAdapter{

//返回请求后,转换的参数Type类型

TyperesponseType();

//接口适配

Tadapt(Callcall);

}

复制代码

如果客户端没有配置CallAdapter,Retrofit会采用默认的实现DefaultCallAdapterFactory直接返回Call对象,而如果配置了RxJava的RxJavaCallAdapterFactory实现,就会将Call转换为Observable,供客户端调用。



复制代码

staticfinalclassSimpleCallAdapterimplementsCallAdapter>{



//省略代码

@Override

publicObservableadapt(Callcall){

Observableobservable=Observable.create(newCallOnSubscribe<>(call))

.lift(OperatorMapResponseToBodyOrError.instance());

if(scheduler!=null){

returnobservable.subscribeOn(scheduler);

}

returnobservable;

}

}

复制代码

总结下,适配器模式包含四种角色:



Target:目标抽象类

Adapter:适配器类

Adaptee:适配者类

Client:客户端类

CallAdapter对应Target,其adapt方法返回客户端类Client需要的对象;RxJavaCallAdapterFactory的get方法返回SimpleCallAdapter对象(或ResultCallAdapter对象)实现了CallAdapter>,对应Adapter;Call对应Adaptee适配者类,包含需要被适配的方法。



另外,适配器模式有对象适配器和类适配器两种实现。类适配器中的Adapter需要继承自Adaptee,对象适配则是采用复合的方式,Adapter持有Adaptee的引用。类适配器模式会使Adaptee的方法暴露给Adapter,根据“复合优先于继承”的思想,推荐使用对象适配器模式。



值得说明的是,这里SimpleCallAdapter并没有通过域的方式持有Call,而是直接在CallAdapter的get方法中将Call以入参形式传入。虽然并不是教科书式的对象适配器模式,但使用却更加灵活、方便。



二、策略模式

完成一项任务,往往可以有多种不同的方式,每一种方式称为一个策略,我们可以根据环境或者条件的不同选择不同的策略来完成该项任务。针对这种情况,一种常规的做法是将多个策略写在一个类中,通过if…else或者switch等条件判断语句来选择具体的算法。这种方式实现简单、快捷,但维护成本很高,当添加新的策略时,需要修改源代码,这违背了开闭原则和单一原则。仍以CallAdapter为例,不同的CallAdapter代表着不同的策略,当我们调用这些不同的适配器的方法时,就能得到不同的结果,这就是策略模式。策略模式包含三种角色:



Context上下文环境——区别于Android的Context,这里代表操作策略的上下文;

Stragety抽象策略——即不同策略需要实现的方法;

ConcreteStragety策略实现——实现Stragety抽象策略。

在Retrofit中,配置Retrofit.Builder时addCallAdapterFactory,配置的类就对应Context;不同的CallAdapter都需要提供adapt方法,CallAdapter就对应Stragety抽象策略。RxJavaCallAdapterFactory的get方法返回SimpleCallAdapter对象(或ResultCallAdapter对象)就对应具体的策略实现。



这里可能会跟上篇中的工厂模式搞混,在说明工厂模式时,主要是强调的是:



publicabstractCallAdapterget(TypereturnType,Annotation[]annotations,Retrofitretrofit);

通过get方法返回不同的CallAdapter对象;策略模式强调的是这些不同CallAdapter对象的adapt方法的具体实现。



Tadapt(Callcall);

总结下:工厂模式强调的是生产不同的对象,策略模式强调的是这些不同对象的策略方法的具体实现,是在创建对象之后。



三、观察者模式

建立一种对象与对象之间的依赖关系,一个对象发生改变时将自动通知其他对象,其他对象将相应做出反应。在此,发生改变的对象称为观察目标,而被通知的对象称为观察者,一个观察目标可以对应多个观察者,而且这些观察者之间没有相互联系,可以根据需要增加和删除观察者,使得系统更易于扩展,这就是观察者模式的模式动机。



举个栗子:在Android编程中,常见的一种情况是界面上某个控件的状态对其它控件有约束关系,比如,需要根据某个EditText的输入值决定某个按钮是否可以点击,就需要此EditText是可观测的对象,而按钮是EditText的观测者,当EditText状态发生改变时,按钮进行相应的操作。



观察者模式包含四种角色:



Subject抽象主题——也就是被观察对象,Observable是JDK中内置的类(java.util.Observable),当需要定义被观察对象时,继承自Observable即可;

ConcreteSubject具体主题——具体被观察者,可以继承Observable实现,需要通知观察者时,调用notifyObservers;

Observer抽象观察者——Observer也是JDK内置的,定义了update方法;

ConcreteObserver具体观察者——实现Observer接口定义的update方法,以便在状态发生变化时更新自己。

publicinterfaceObserver{

voidupdate(Observableobservable,Objectdata);

}

复制代码

publicclassObservable{



Listobservers=newArrayList();



//省略代码

publicvoidnotifyObservers(Objectdata){

intsize=0;

Observer[]arrays=null;

synchronized(this){

if(hasChanged()){

clearChanged();

size=observers.size();

arrays=newObserver[size];

observers.toArray(arrays);

}

}

if(arrays!=null){

for(Observerobserver:arrays){

observer.update(this,data);

}

}

}

}

复制代码

所有与网络请求相关的库一定会支持请求的异步发送,通过在库内部维护一个队列,将请求添加到该队列,同时注册一个回调接口,以便执行引擎完成该请求后,将请求结果进行回调。Retrofit也不例外,Retrofit的网络请求执行引擎是OkHttp,请求类是OkHttpCall,其实现了Call接口,enqueue方法如下,入参为Callback对象。



voidenqueue(Callbackcallback);

在OkHttpCall的enqueue实现方法中,通过在okhttp3.Callback()的回调方法中调用上述入参Callback对象的方法,实现通知观察者。



复制代码

@Override

publicvoidenqueue(finalCallbackcallback){

//省略代码

call.enqueue(newokhttp3.Callback(){

@Override

publicvoidonResponse(okhttp3.Callcall,okhttp3.ResponserawResponse)

throwsIOException{

Responseresponse;

try{

response=parseResponse(rawResponse);

}catch(Throwablee){

callFailure(e);

return;

}

callSuccess(response);

}



@Override

publicvoidonFailure(okhttp3.Callcall,IOExceptione){

try{

callback.onFailure(OkHttpCall.this,e);

}catch(Throwablet){

t.printStackTrace();

}

}

privatevoidcallSuccess(Responseresponse){

try{

callback.onResponse(OkHttpCall.this,response);

}catch(Throwablet){

t.printStackTrace();

}

}

复制代码

总结下:Call接口对应Subject,定义被观察者的特性,包含enqueue等;OkHttpCall对应ConcreteSubject具体被观察者,Callback对应Observer抽象观察者,Callback的实现类对应ConcreteObserver具体观察者。



四、单例模式

单例模式可能是所有设计模式教程的第一个讲到的模式,也是应用最广泛的模式之一。Retrofit中也使用了大量的单例模式,比如BuiltInConverters的responseBodyConverter、requestBodyConverter等,并且使用了饿汉式的单例模式。由于这种单例模式应用最广,也是大家都清楚的,本节将扩展下单例模式的其它实现方式:



懒汉式单例模式:



复制代码

publicclassSingleton{



privatestaticSingletoninstance;



privateSingleton(){



}



publicstaticsynchronizedSingletongetInstance(){

if(instance==null){

instance=newSingleton();

}

returninstance;

}

}

复制代码

懒汉单例模式的优点是单例只要有在使用是才被实例化,缺点是美的调用getInstance都进行同步,造成不必要的同步开销。



DCL(DoubleCheckLock):



复制代码

publicclassSingleton{



privatestaticSingletoninstance;



privateSingleton(){



}



publicstaticSingletongetInstance(){

if(instance==null){

synchronized(Singleton.class){

if(instance==null){

instance=newSingleton();

}

}

}

returninstance;

}

}

复制代码

DCL是对懒汉单例模式的升级,getInstance方法对instance进行了两次判空,第一层判断是为了避免不必要的同步,第二层判断是为了在null时创建实例,这里涉及到对象实例化过程的原子问题。在Java中,创建对象并非原子操作,而是包含分配内存、初始化成员字段、引用指向等一连串操作,而多线程环境下,由于指令重排序的存在,初始化指令和引用指令可能是颠倒,那么可能当线程执行第一个判断不为null返回的对象,却是未经初始化的(别的对象创建Singleton时,初始化指令和引用指令颠倒了)。



静态内部类:



复制代码

publicclassSingleton{



privateSingleton(){



}



publicstaticSingletongetInstance(){

returnSingletonHolder.instance;

}



privatestaticclassSingletonHolder{

privatestaticfinalSingletoninstance=newSingleton();

}

}

复制代码

上述DCL也是可能失效的,具体可参考《有关“双重检查锁定失效”的说明》。采用静态内部类,加载Singleton类时并不会初始化instance,同时也能保证线程安全,单例对象的唯一性。



枚举单例:



publicenumSingleton{



INSTANCE;

}

枚举实例的创建默认是线程安全的,并且在任何情况下都只有一个实例。上述单例模式存在反序列化会重新创建对象的情况,而枚举不存在这个问题。但Android编程中,因为性能问题,不推荐使用枚举,所以,这种比较怪异的方式并不推荐。



使用容器实现单例模式:



复制代码

publicclassSingleton{



privatestaticMapobjectMap=newHashMap<>();



publicstaticvoidaddObject(Stringkey,Objectinstance){

if(!objectMap.containsKey(key)){

objectMap.put(key,instance);

}

}



publicstaticObjectgetObject(Stringkey){

returnobjectMap.get(key);

}

}

复制代码

严格的讲,这并不是标准的单例模式,但确实实现了单例的效果。



单例的核心原理是将构造函数私有化,通过静态方法获取唯一实例。而怎么获取唯一实例?在Java中可能存在线程安全、反序列化等问题,因此衍生出上述这几个版本。在实际使用时需要根据并发环境、JDK版本以及资源消耗等因素综合考虑。



五、原型模式

原型模式是一种创建型模式,主要用于对象复制。使用原型模式创建对象比直接new一个对象在性能上要好的多,因为Object类的clone方法是一个本地方法,它直接操作内存中的二进制流。使用原型模式的另一个好处是简化对象的创建,使得创建对象就像在编辑文档时的复制粘贴。基于以上优点,在需要重复地创建相似对象时可以考虑使用原型模式。比如需要在一个循环体内创建对象,假如对象创建过程比较复杂或者循环次数很多的话,使用原型模式不但可以简化创建过程,而且可以使系统的整体性能提高很多。



原型模式有三种角色:



Client客户端;

Prototype原型——一般表现为抽象类或者接口,比如JDK中的Cloneable接口;

ConcretePrototype具体原型类——实现了Prototype原型。

OkHttpCall实现了Call接口,Call接口继承自Cloneable,OkHttpCall的clone方法实现如下:



@Override

publicOkHttpCallclone(){

returnnewOkHttpCall<>(serviceMethod,args);

}

clone的实现就是重新new了一个一样的对象,用于其他地方重用相同的Call,在ExecutorCallbackCall中有用到:



复制代码

staticfinalclassExecutorCallbackCallimplementsCall{

//省略代码

@SuppressWarnings("CloneDoesntCallSuperClone")//Performingdeepclone.

@Override

publicCallclone(){

returnnewExecutorCallbackCall<>(callbackExecutor,delegate.clone());

}

}

复制代码

使用原型模式复制对象需要主要深拷贝与浅拷贝的问题。Object类的clone方法只会拷贝对象中的基本的数据类型,对于数组、容器对象、引用对象等都不会拷贝,这就是浅拷贝。如果要实现深拷贝,必须将原型模式中的数组、容器对象、引用对象等另行拷贝。



六、享元模式

享元模式是对象池的一种实现,运用共享技术有效地支持大量细粒度对象的复用。系统只使用少量的对象,而这些对象都很相似,状态变化很小,可以实现对象的多次复用。由于享元模式要求能够共享的对象必须是细粒度对象,因此它又称为轻量级模式(Flyweight),它是一种对象结构型模式。



享元模式包含三种角色:



Flyweight享元基类或接口;

ConcreteFlyweight具体的享元对象;

FlyweightFactory享元工厂——负责管理享元对象池和创建享元对象。

Retrofit中create方法创建ServiceMethod是通过loadServiceMethod方法实现。loadServiceMethod方法就实现了享元模式。



复制代码

privatefinalMapserviceMethodCache=newLinkedHashMap<>();



ServiceMethodloadServiceMethod(Methodmethod){

ServiceMethodresult;

synchronized(serviceMethodCache){

result=serviceMethodCache.get(method);

if(result==null){

result=newServiceMethod.Builder(this,method).build();

serviceMethodCache.put(method,result);

}

}

returnresult;

}

献花(0)
+1
(本文系thedust79首藏)