配色: 字号:
Android中所涉及的常用设计模式
2017-01-16 | 阅:  转:  |  分享 
  
Android中所涉及的常用设计模式



1、单例模式

概念:Ensureaclasshasonlyoneinstance,andprovideaglobalpointofaccesstoit.

动态确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。



优点:



1.1、由于单例模式在内存中只有一个实例,减少了内存开销。对于那些耗内存的类,只实例化一次,大大提高性能,尤其是移动开发中。



1.2、单例模式可以避免对资源的多重占用,例如一个写文件时,由于只有一个实例存在内存中,避免对同一个资源文件的同时写操作。



1.3、单例模式可以在系统设置全局的访问点,优化和共享资源访问。



[java]viewplaincopy在CODE上查看代码片派生到我的代码片

publicclassSingleton{

privatevolatilestaticSingletoninstance=null;



privateSingleton(){

}



publicstaticSingletongetInstance(){

if(instance==null){

synchronized(Singleton.class){

if(instance==null){

instance=newSingleton();

}

}

}

returninstance;

}

}

构造函数私有化,定义静态函数获得实例就不多说了,这里着重说一下volatile:

volatile本质是在告诉jvm当前变量在寄存器中的值是不确定的,需要从内存中读取,synchronized则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住.(首先我们要先意识到有这样的现象,编译器为了加快程序运行的速度,对一些变量的写操作会先在寄存器或者是CPU缓存上进行,最后才写入内存.

而在这个过程,变量的新值对其他线程是不可见的.而volatile的作用就是使它修饰的变量的读写操作都必须在内存中进行!)



synchronized

同步块大家都比较熟悉,通过synchronized关键字来实现,所有加上synchronized和块语句,在多线程访问的时候,同一时刻只能有一个线程能够用

synchronized修饰的方法或者代码块。

volatile

用volatile修饰的变量,线程在每次使用变量的时候,都会读取变量修改后的最的值。volatile很容易被误用,用来进行原子性操作。

再就是这个双重判断null:



这是因为如果线程A进入了该代码,线程B在等待,这是A线程创建完一个实例出来后,线程B获得锁进入同步代码,实例已经存在,木有必要再创建一个,所以双重判断有必要。



Android中用到的地方很多,比如Android-Universal-Image-Loader中的单例,EventBus中的单例

最后给出一个管理我们activity的类,可以作为一个简单工具类

[java]viewplaincopy在CODE上查看代码片派生到我的代码片

publicclassActivityManager{



privatestaticvolatileActivityManagerinstance;

privateStackmActivityStack=newStack();



privateActivityManager(){



}



publicstaticActivityManagergetInstance(){

if(instance==null){

synchronized(ActivityManager.class){

if(instance==null){

instance=newActivityManager();

}

}

returninstance;

}



publicvoidaddActicity(Activityact){

mActivityStack.push(act);

}



publicvoidremoveActivity(Activityact){

mActivityStack.remove(act);

}



publicvoidkillMyProcess(){

intnCount=mActivityStack.size();

for(inti=nCount-1;i>=0;i--){

Activityactivity=mActivityStack.get(i);

activity.finish();

}



mActivityStack.clear();

android.os.Process.killProcess(android.os.Process.myPid());

}

}

单例模式在Android源码中的应用:

在Android源码中,使用到单例模式的例子很多,如:



InputMethodManager类

[html]viewplaincopy在CODE上查看代码片派生到我的代码片

publicfinalclassInputMethodManager{

staticfinalbooleanDEBUG=false;

staticfinalStringTAG="InputMethodManager";



staticfinalObjectmInstanceSync=newObject();

staticInputMethodManagermInstance;



finalIInputMethodManagermService;

finalLoopermMainLooper;

创建唯一的实例staticInputMethodManagermInstance;

[html]viewplaincopy在CODE上查看代码片派生到我的代码片

/

RetrievetheglobalInputMethodManagerinstance,creatingitifit

doesn''talreadyexist.

@hide

/

staticpublicInputMethodManagergetInstance(Contextcontext){

returngetInstance(context.getMainLooper());

}



/

Internally,theinputmethodmanagercan''tbecontext-dependent,so

wehavethisherefortheplacesthatneedit.

@hide

/

staticpublicInputMethodManagergetInstance(LoopermainLooper){

synchronized(mInstanceSync){

if(mInstance!=null){

returnmInstance;

}

IBinderb=ServiceManager.getService(Context.INPUT_METHOD_SERVICE);

IInputMethodManagerservice=IInputMethodManager.Stub.asInterface(b);

mInstance=newInputMethodManager(service,mainLooper);

}

returnmInstance;

}

防止多线程同时创建实例:



synchronized(mInstanceSync){

if(mInstance!=null){

returnmInstance;

}

当没有创建实例对象时,调用mInstance=newInputMethodManager(service,mainLooper);

其中类构造函数如下所示:



[html]viewplaincopy在CODE上查看代码片派生到我的代码片

InputMethodManager(IInputMethodManagerservice,Looperlooper){

mService=service;

mMainLooper=looper;

mH=newH(looper);

mIInputContext=newControlledInputConnectionWrapper(looper,

mDummyInputConnection);



if(mInstance==null){

mInstance=this;

}

}



2、建造者模式(Builder模式)

定义:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示



概念就是比较抽象的,让大家很难理解的,如果简单从这个一个概念就搞懂了这个模式的话,那就不用费力的去查资料整理后边的东西了。

这里我们通过一个例子来引出Build模式。假设有一个Person类,他的一些属性可以为null,可以通过这个类来构架一大批人



[java]viewplaincopy在CODE上查看代码片派生到我的代码片

publicclassPerson{

privateStringname;

privateintage;

privatedoubleheight;

privatedoubleweight;



publicStringgetName(){

returnname;

}



publicvoidsetName(Stringname){

this.name=name;

}



publicintgetAge(){

returnage;

}



publicvoidsetAge(intage){

this.age=age;

}



publicdoublegetHeight(){

returnheight;

}



publicvoidsetHeight(doubleheight){

this.height=height;

}



publicdoublegetWeight(){

returnweight;

}



publicvoidsetWeight(doubleweight){

this.weight=weight;

}

}

然后为了方便,你可能会写这么一个构造函数来传属性

[java]viewplaincopy在CODE上查看代码片派生到我的代码片

publicPerson(Stringname,intage,doubleheight,doubleweight){

this.name=name;

this.age=age;

this.height=height;

this.weight=weight;

}

或者为了更方便还会写一个空的构造函数

[java]viewplaincopy在CODE上查看代码片派生到我的代码片

publicPerson(){

}

有时候还会比较懒,只传入某些参数,又会来写这些构造函数

[html]viewplaincopy在CODE上查看代码片派生到我的代码片

publicPerson(Stringname){

this.name=name;

}



publicPerson(Stringname,intage){

this.name=name;

this.age=age;

}



publicPerson(Stringname,intage,doubleheight){

this.name=name;

this.age=age;

this.height=height;

}

于是就可以来创建各种需要的类

[html]viewplaincopy在CODE上查看代码片派生到我的代码片

Personp1=newPerson();

Personp2=newPerson("张三");

Personp3=newPerson("李四",18);

Personp4=newPerson("王二",21,180);

Personp5=newPerson("麻子",16,170,65.4);

其实这种写法的坏处在你写的过程中想摔键盘的时候就该想到了,既然就是一个创建对象的过程,怎么这么繁琐,并且构造函数参数过多,其他人创建对象的时候怎么知道各个参数代表什么意思呢,这个时候我们为了代码的可读性,就可以用一下Builder模式了

给Person类添加一个静态Builder类,然后修改Person的构造函数,如下:



[java]viewplaincopy在CODE上查看代码片派生到我的代码片

publicclassPerson{

privateStringname;

privateintage;

privatedoubleheight;

privatedoubleweight;



privatePerson(Builderbuilder){

this.name=builder.name;

this.age=builder.age;

this.height=builder.height;

this.weight=builder.weight;

}

publicStringgetName(){

returnname;

}



publicvoidsetName(Stringname){

this.name=name;

}



publicintgetAge(){

returnage;

}



publicvoidsetAge(intage){

this.age=age;

}



publicdoublegetHeight(){

returnheight;

}



publicvoidsetHeight(doubleheight){

this.height=height;

}



publicdoublegetWeight(){

returnweight;

}



publicvoidsetWeight(doubleweight){

this.weight=weight;

}



staticclassBuilder{

privateStringname;

privateintage;

privatedoubleheight;

privatedoubleweight;

publicBuildername(Stringname){

this.name=name;

returnthis;

}

publicBuilderage(intage){

this.age=age;

returnthis;

}

publicBuilderheight(doubleheight){

this.height=height;

returnthis;

}



publicBuilderweight(doubleweight){

this.weight=weight;

returnthis;

}



publicPersonbuild(){

returnnewPerson(this);

}

}

}

从上边代码我们可以看到我们在Builder类中定义了一份跟Person类一样的属性,通过一系列的成员函数进行赋值,但是返回的都是this,最后提供了一个build函数来创建person对象,对应的在Person的构造函数中,传入了Builder对象,然后依次对自己的成员变量进行赋值。此外,Builder的成员函数返回的都是this的另一个作用就是让他支持链式调用,使代码可读性大大增强

于是我们就可以这样创建Person对象



[java]viewplaincopy在CODE上查看代码片派生到我的代码片

Person.Builderbuilder=newPerson.Builder();

Personperson=builder

.name("张三")

.age(18)

.height(178.5)

.weight(67.4)

.build();

是不是有那么点感觉了呢

Android中大量地方运用到了Builder模式,比如常见的对话框创建



[java]viewplaincopy在CODE上查看代码片派生到我的代码片

AlertDialog.Builderbuilder=newAlertDialog.Builder(this);

AlertDialogdialog=builder.setTitle("对话框")

.setIcon(android.R.drawable.ic_dialog)

.setView(R.layout.custom_view)

.setPositiveButton(R.string.positive,newDialogInterface.OnClickListener(){

@Override

publicvoidonClick(DialogInterfacedialog,intwhich){



}

})

.setNegativeButton(R.string.negative,newDialogInterface.OnClickListener(){

@Override

publicvoidonClick(DialogInterfacedialog,intwhich){



}

})

.create();

dialog.show();

其实在java中StringBuilder和StringBuffer都用到了Builder模式,只不过是稍微简单一点了

Gson中的GsonBuilder

[java]viewplaincopy在CODE上查看代码片派生到我的代码片

GsonBuilderbuilder=newGsonBuilder();

Gsongson=builder.setPrettyPrinting()

.disableHtmlEscaping()

.generateNonExecutableJson()

.serializeNulls()

.create();

网络框架OKHttp

[html]viewplaincopy在CODE上查看代码片派生到我的代码片

Request.Builderbuilder=newRequest.Builder();

Requestrequest=builder.addHeader("","")

.url("")

.post(body)

.build();

可见大量框架运用了Builder设计模式,总结一下吧:

定义一个静态内部类Builder,内部成员变量跟外部一样



Builder通过一系列方法给成员变量赋值,并返回当前对象(this)



Builder类内部提供一个build方法方法或者create方法用于创建对应的外部类,该方法内部调用了外部类的一个私有化构造方法,该构造方法的参数就是内部类Builder



外部类提供一个私有化的构造方法供内部类调用,在该构造函数中完成成员变量的赋值



3、观察者模式

定义:Defineaone-to-manydependencybetweenobjectssothatwhenoneobjectchangesstate,allitsdependentsaernotifiedandupdatedautomatically.

定义对象间一种一对多的依赖关系,使得当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新。



主要包括四个部分:

1.Subject被观察者。是一个接口或者是抽象类,定义被观察者必须实现的职责,它必须能偶动态地增加、取消观察者,管理观察者并通知观察者。

2.Observer观察者。观察者接收到消息后,即进行update更新操作,对接收到的信息进行处理。

3.ConcreteSubject具体的被观察者。定义被观察者自己的业务逻辑,同时定义对哪些事件进行通知。

4.ConcreteObserver具体观察者。每个观察者在接收到信息后处理的方式不同,各个观察者有自己的处理逻辑。

这个好像还好理解那么一点点,不过还是先来讲个情景,



天气预报的短信服务,一旦付费订阅,每次天气更新都会向你及时发送



其实就是我们无需每时每刻关注我们感兴趣的东西,我们只需要订阅它即可,一旦我们订阅的事务有变化了,被订阅的事务就会即时的通知我们



我们来看一下观察者模式的组成:



观察者,我们称它为Observer,有时候我们也称它为订阅者,即Subscriber

被观察者,我们称它为Observable,即可以被观察的东西,有时候还会称之为主题,即Subject

至于观察者模式的具体实现,java里为我们提供了Observable类和Observer接口供我们快速实现该模式,但是这里为了加深印象,不用这个两个类

我们来模拟上边的场景,先定义一个Weather的类



[java]viewplaincopy在CODE上查看代码片派生到我的代码片

publicclassWeather{

privateStringdescription;



publicWeather(Stringdescription){

this.description=description;

}



publicStringgetDescription(){

returndescription;

}



publicvoidsetDescription(Stringdescription){

this.description=description;

}



@Override

publicStringtoString(){

return"Weather{"+

"description=''"+description+''\''''+

''}'';

}

}

然后定义我们的被观察着,我们希望它能够通用,所以定义成泛型,内部应该暴露出register和unRegister供观察者订阅和取消订阅,至于观察者的保存,我们用ArrayList即可,另外,当主题发生变化的时候,需要通知观察者来做出响应,还需要一个notifyObservers方法,具体实现如下:

[java]viewplaincopy在CODE上查看代码片派生到我的代码片

publicclassObservable{

List>mObservers=newArrayList>();



publicvoidregister(Observerobserver){

if(observer==null){

thrownewNullPointerException("observer==null");

}

synchronized(this){

if(!mObservers.contains(observer))

mObservers.add(observer);

}

}



publicsynchronizedvoidunregister(Observerobserver){

mObservers.remove(observer);

}



publicvoidnotifyObservers(Tdata){

for(Observerobserver:mObservers){

observer.onUpdate(this,data);

}

}



}

而我们的观察者只需要实现一个观察者的接口Observer,该接口也是泛型的

[java]viewplaincopy在CODE上查看代码片派生到我的代码片

publicinterfaceObserver{

voidonUpdate(Observableobservable,Tdata);

}

一旦订阅的主题发生了变化,就会调用该接口

用一下,我们定义一个天气变化的主题,也就是被观察者,再定义两个观察者来观察天气的变化,一旦变化了就打印出天气的情况,注意,一定要用register方法来注册,否则观察者收不到变化的信息,而一旦不感兴趣,就可以调用unregister方法



[java]viewplaincopy在CODE上查看代码片派生到我的代码片

publicclassMain{

publicstaticvoidmain(String[]args){

Observableobservable=newObservable();

Observerobserver1=newObserver(){

@Override

publicvoidonUpdate(Observableobservable,Weatherdata){

System.out.println("观察者1:"+data.toString());

}

};

Observerobserver2=newObserver(){

@Override

publicvoidonUpdate(Observableobservable,Weatherdata){

System.out.println("观察者2:"+data.toString());

}

};



observable.register(observer1);

observable.register(observer2);





Weatherweather=newWeather("晴转多云");

observable.notifyObservers(weather);



Weatherweather1=newWeather("多云转阴");

observable.notifyObservers(weather1);



observable.unregister(observer1);



Weatherweather2=newWeather("台风");

observable.notifyObservers(weather2);



}

}

输出也没有问题

观察者1:Weather{description=’晴转多云’}

观察者2:Weather{description=’晴转多云’}

观察者1:Weather{description=’多云转阴’}

观察者2:Weather{description=’多云转阴’}

观察者2:Weather{description=’台风’}



好,我们来看一下在Android中的应用,从最简单的开始,Button的点击事件



[java]viewplaincopy在CODE上查看代码片派生到我的代码片

Buttonbtn=newButton(this);

btn.setOnClickListener(newView.OnClickListener(){

@Override

publicvoidonClick(Viewv){

Log.e("TAG","click");

}

});

另外广播机制,本质也是观察者模式

调用registerReceiver方法注册广播,调用unregisterReceiver方法取消注册,之后使用sendBroadcast发送广播,之后注册的广播会受到对应的广播信息,这就是典型的观察者模式



开源框架EventBus也是基于观察者模式的,



观察者模式的注册,取消,发送事件三个典型方法都有



[java]viewplaincopy在CODE上查看代码片派生到我的代码片

EventBus.getDefault().register(Objectsubscriber);

EventBus.getDefault().unregister(Objectsubscriber);



EventBus.getDefault().post(Objectevent);



4、策略模式

定义:策略模式定义了一系列算法,并将每一个算法封装起来,而且使他们可以相互替换,策略模式让算法独立于使用的客户而独立改变



最常见的就是关于出行旅游的策略模式,出行方式有很多种,自行车,汽车,飞机,火车等,如果不使用任何模式,代码是这样子的



[java]viewplaincopy在CODE上查看代码片派生到我的代码片

publicclassTravelStrategy{

enumStrategy{

WALK,PLANE,SUBWAY

}

privateStrategystrategy;

publicTravelStrategy(Strategystrategy){

this.strategy=strategy;

}



publicvoidtravel(){

if(strategy==Strategy.WALK){

print("walk");

}elseif(strategy==Strategy.PLANE){

print("plane");

}elseif(strategy==Strategy.SUBWAY){

print("subway");

}

}



publicvoidprint(Stringstr){

System.out.println("出行旅游的方式为:"+str);

}



publicstaticvoidmain(String[]args){

TravelStrategywalk=newTravelStrategy(Strategy.WALK);

walk.travel();



TravelStrategyplane=newTravelStrategy(Strategy.PLANE);

plane.travel();



TravelStrategysubway=newTravelStrategy(Strategy.SUBWAY);

subway.travel();

}

}

很明显,如果需要增加出行方式就需要在增加新的elseif语句,这违反了面向对象的原则之一,对修改封装(开放封闭原则)

题外话:面向对象的三大特征:封装,继承和多态



五大基本原则:单一职责原则(接口隔离原则),开放封闭原则,Liskov替换原则,依赖倒置原则,良性依赖原则



好,回归主题,如何用策略模式来解决这个问题



首先,定义一个策略的接口



[java]viewplaincopy在CODE上查看代码片派生到我的代码片

publicinterfaceStrategy{

voidtravel();

}

然后根据不同的出行方法来实现该接口

[html]viewplaincopy在CODE上查看代码片派生到我的代码片

publicclassWalkStrategyimplementsStrategy{



@Override

publicvoidtravel(){

System.out.println("walk");

}



}

[html]viewplaincopy在CODE上查看代码片派生到我的代码片

publicclassPlaneStrategyimplementsStrategy{



@Override

publicvoidtravel(){

System.out.println("plane");

}



}

[html]viewplaincopy在CODE上查看代码片派生到我的代码片

publicclassSubwayStrategyimplementsStrategy{



@Override

publicvoidtravel(){

System.out.println("subway");

}



}



此外还需要一个包装策略的类,来调用策略中的接口

[html]viewplaincopy在CODE上查看代码片派生到我的代码片

publicclassTravelContext{

Strategystrategy;



publicStrategygetStrategy(){

returnstrategy;

}



publicvoidsetStrategy(Strategystrategy){

this.strategy=strategy;

}



publicvoidtravel(){

if(strategy!=null){

strategy.travel();

}

}

}

测试一下代码:

[html]viewplaincopy在CODE上查看代码片派生到我的代码片

publicclassMain{

publicstaticvoidmain(String[]args){

TravelContexttravelContext=newTravelContext();

travelContext.setStrategy(newPlaneStrategy());

travelContwww.tt951.comext.travel();

travelContext.setStrategy(newWalkStrategy());

travelContext.travel();

travelContext.setStrategy(newSubwayStrategy());

travelContext.travel();

}

}

以后如果再增加什么别的出行方式,就再继承策略接口即可,完全不需要修改现有的类



策略模式优缺点

定义一系列算法:策略模式的功能就是定义一系列算法,实现让这些算法可以相互替换。所以会为这一系列算法定义公共的接口,以约束一系列算法要实现的功能。如果这一系列算法具有公共功能,可以把策略接口实现成为抽象类,把这些公共功能实现到父类里面,对于这个问题,前面讲了三种处理方法,这里就不罗嗦了。



避免多重条件语句:根据前面的示例会发现,策略模式的一系列策略算法是平等的,可以互换的,写在一起就是通过if-else结构来组织,如果此时具体的算法实现里面又有条件语句,就构成了多重条件语句,使用策略模式能避免这样的多重条件语句。



更好的扩展性:在策略模式中扩展新的策略实现非常容易,只要增加新的策略实现类,然后在选择使用策略的地方选择使用这个新的策略实现就好了。



客户必须了解每种策略的不同:策略模式也有缺点,比如让客户端来选择具体使用哪一个策略,这就可能会让客户需要了解所有的策略,还要了解各种策略的功能和不同,这样才能做出正确的选择,而且这样也暴露了策略的具体实现。



增加了对象数目:由于策略模式把每个具体的策略实现都单独封装成为类,如果备选的策略很多的话,那么对象的数目就会很可观。



只适合扁平的算法结构:策略模式的一系列算法地位是平等的,是可以相互替换的,事实上构成了一个扁平的算法结构,也就是在一个策略接口下,有多个平等的策略算法,就相当于兄弟算法。而且在运行时刻只有一个算法被使用,这就限制了算法使用的层级,使用的时候不能嵌套使用。



Android中的应用



下面说说在Android里面的应用。在Android里面策略模式的其中一个典型应用就是Adapter,在我们平时使用的时候,一般情况下我们可能继承BaseAdapter,然后实现不同的View返回,GetView里面实现不同的算法。外部使用的时候也可以根据不同的数据源,切换不同的Adapter。

5、原型模式

定义:用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象。



[html]viewplaincopy在CODE上查看代码片派生到我的代码片

publicclassPerson{

privateStringname;

privateintage;

privatedoubleheight;

privatedoubleweight;



publicPerson(){



}



publicStringgetName(){

returnname;

}



publicvoidsetName(Stringname){

this.name=name;

}



publicintgetAge(){

returnage;

}



publicvoidsetAge(intage){

this.age=age;

}



publicdoublegetHeight(){

returnheight;

}



publicvoidsetHeight(doubleheight){

this.height=height;

}



publicdoublegetWeight(){

returnweight;

}



publicvoidsetWeight(doubleweight){

this.weight=weight;

}



@Override

publicStringtoString(){

return"Person{"+

"name=''"+name+''\''''+

",age="+age+

",height="+height+

",weight="+weight+

''}'';

}

}

要实现原型模式,按照以下步骤来:

1,实现一个Cloneable接口



[html]viewplaincopy在CODE上查看代码片派生到我的代码片

publicclassPersonimplementsCloneable{



}

重写Object的clone方法,在此方法中实现拷贝逻辑

[html]viewplaincopy在CODE上查看代码片派生到我的代码片

@Override

publicObjectclone(){

Personperson=null;

try{

person=(Person)super.clone();

person.name=this.name;

person.weight=this.weight;

person.height=this.height;

person.age=this.age;

}catch(CloneNotSupportedExceptione){

e.printStackTrace();

}

returnperson;

}

测试一下:



[html]viewplaincopy在CODE上查看代码片派生到我的代码片

publicclassMain{

publicstaticvoidmain(String[]args){

Personp=newPerson();

p.setAge(18);

p.setName("张三");

p.setHeight(178);

p.setWeight(65);

System.out.println(p);



Personp1=(Person)p.clone();

System.out.println(p1);



p1.setName("李四");

System.out.println(p);

System.out.println(p1);

}

}

输出结果如下:

Person{name=’张三’,age=18,height=178.0,weight=65.0}

Person{name=’张三’,age=18,height=178.0,weight=65.0}

Person{name=’张三’,age=18,height=178.0,weight=65.0}

Person{name=’李四’,age=18,height=178.0,weight=65.0}

试想一下,两个不同的人,除了姓名不一样,其他三个属性都一样,用原型模式进行拷贝就会显得异常简单,这也是原型模式的应用场景之一



假设Person类还有一个属性叫兴趣集合,是一个List集合,就酱紫:



[html]viewplaincopy在CODE上查看代码片派生到我的代码片

privateArrayListhobbies=newArrayList();



publicArrayListgetHobbies(){

returnhobbies;

}



publicvoidsetHobbies(ArrayListhobbies){

this.hobbies=hobbies;

}

在进行拷贝的时候就要注意了,如果还是跟之前的一样操作,就会发现其实两个不同的人的兴趣集合的是指向同一个引用,我们对其中一个人的这个集合属性进行操作,另一个人的这个属性也会相应的变化,其实导致这个问题的本质原因是我们只进行了浅拷贝,也就是指拷贝了引用,最终两个对象指向的引用是同一个,一个发生变化,另一个也会发生拜变化。显然解决方法就是使用深拷贝

[html]viewplaincopy在CODE上查看代码片派生到我的代码片

@Override

publicObjectclone(){

Personperson=null;

try{

person=(Person)super.clone();

person.name=this.name;

person.weight=this.weight;

person.height=this.height;

person.age=this.age;



person.hobbies=(ArrayList)this.hobbies.clone();

}catch(CloneNotSupportedExceptione){

e.printStackTrace();

}

returnperson;

}

不再是直接引用,而是拷贝了一份,

其实有的时候我们看到的原型模式更多的是另一种写法:在clone函数里调用构造函数,构造函数里传入的参数是该类对象,然后在函数中完成逻辑拷贝



[html]viewplaincopy在CODE上查看代码片派生到我的代码片

@Override

publicObjectclone(){

returnnewPerson(this);

}

[html]viewplaincopy在CODE上查看代码片派生到我的代码片

publicPerson(Personperson){

this.name=person.name;

this.weight=person.weight;

this.height=person.height;

this.age=person.age;

this.hobbies=newArrayList(hobbies);

}

其实都差不多,只是写法不一样而已

现在来看看Android中的原型模式:



先看Bundle类,



[html]viewplaincopy在CODE上查看代码片派生到我的代码片

publicObjectclone(){

returnnewBundle(this);

}

publicBundle(Bundleb){

super(b);



mHasFds=b.mHasFds;

mFdsKnown=b.mFdsKnown;

}

然后是Intent类

[html]viewplaincopy在CODE上查看代码片派生到我的代码片

@Override

publicObjectclone(){

returnnewIntent(this);

}

publicIntent(Intento){

this.mAction=o.mAction;

this.mData=o.mData;

this.mType=o.mType;

this.mPackage=o.mPackage;

this.mComponent=o.mComponent;

this.mFlags=o.mFlags;

this.mContentUserHint=o.mContentUserHint;

if(o.mCategories!=null){

this.mCategories=newArraySet(o.mCategories);

}

if(o.mExtras!=null){

this.mExtras=newBundle(o.mExtras);

}

if(o.mSourceBounds!=null){

this.mSourceBounds=newRect(o.mSourceBounds);

}

if(o.mSelector!=null){

this.mSelector=newIntent(o.mSelector);

}

if(o.mClipData!=null){

this.mClipData=newClipData(o.mClipData);

}

}

用法也十分简单,一旦我们要用的Intent与现在的一个Intent很多东西都一样,那我们就可以直接拷贝现有的Intent,再修改不同的地方,便可以直接使用

[html]viewplaincopy在CODE上查看代码片派生到我的代码片

Uriuri=Uri.parse("smsto:10086");

IntentshareIntent=newIntent(Intent.ACTION_SENDTO,uri);

shareIntent.putExtra("sms_body","hello");



Intentintent=(Intent)shareIntent.clone();

startActivity(intent);



网络请求中最常用的OkHttp中,也应用了原型模式,就在OkHttpClient类中,他实现了Cloneable接口

[html]viewplaincopy在CODE上查看代码片派生到我的代码片

/ReturnsashallowcopyofthisOkHttpClient./

@Override

publicOkHttpClientclone(){

returnnewOkHttpClient(this);

}

privateOkHttpClient(OkHttpClientokHttpClient){

this.routeDatabase=okHttpClient.routeDatabase;

this.dispatcher=okHttpClient.dispatcher;

this.proxy=okHttpClient.proxy;

this.protocols=okHttpClient.protocols;

this.connectionSpecs=okHttpClient.connectionSpecs;

this.interceptors.addAll(okHttpClient.interceptors);

this.networkInterceptors.addAll(okHttpClient.networkInterceptors);

this.proxySelector=okHttpClient.proxySelector;

this.cookieHandler=okHttpClient.cookieHandler;

this.cache=okHttpwww.baiyuewang.netClient.cache;

this.internalCache=cache!=null?cache.internalCache:okHttpClient.internalCache;

this.socketFactory=okHttpClient.socketFactory;

this.sslSocketFactory=okHttpClient.sslSocketFactory;

this.hostnameVerifier=okHttpClient.hostnameVerifier;

this.certificatePinner=okHttpClient.certificatePinner;

this.authenticator=okHttpClient.authenticator;

this.connectionPool=okHttpClient.connectionPool;

this.network=okHttpClient.network;

this.followSslRedirects=okHttpClient.followSslRedirects;

this.followRedirects=okHttpClient.followRedirects;

this.retryOnConnectionFailure=okHttpClient.retryOnConnectionFailure;

this.connectTimeout=okHttpClient.connectTimeout;

this.readTimeout=okHttpClient.readTimeout;

this.writeTimeout=okHttpClient.writeTimeout;

}

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