Android 常用的设计模式之Builder模式

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

public class Person {  

    private String name;  

    private int age;  

    private double height;  

    private double weight;    

    public String getName() {  

        return name;  

    }    

    public void setName(String name) {  

        this.name = name;  

    }    

    public int getAge() {  

        return age;  

    }    

    public void setAge(int age) {  

        this.age = age;  

    }    

    public double getHeight() {  

        return height;  

    }   

    public void setHeight(double height) {  

        this.height = height;  

    }    

    public double getWeight() {  

        return weight;  

    }    

    public void setWeight(double weight) {  

        this.weight = weight;  

    }  

}  

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

public Person(String name, int age, double height, double weight) {  

    this.name = name;  

    this.age = age;  

    this.height = height;  

    this.weight = weight;  

}  

 

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

public Person() {  

}  


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

public Person(String name) {  

    this.name = name;  

}    

public Person(String name, int age) {  

    this.name = name;  

    this.age = age;  

}   

public Person(String name, int age, double height) {  

    this.name = name;  

    this.age = age;  

    this.height = height;  

}  


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

Person p1=new Person();  

Person p2=new Person("张三");  

Person p3=new Person("李四",18);  

Person p4=new Person("王五",21,180);  

Person p5=new Person("赵六",17,170,65.4);  

 

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

 

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

public class Person {  

    private String name;  

    private int age;  

    private double height;  

    private double weight;    

    privatePerson(Builder builder) {  

        this.name=builder.name;  

        this.age=builder.age;  

        this.height=builder.height;  

        this.weight=builder.weight;  

    }  

    public String getName() {  

        return name;  

    }    

    public void setName(String name) {  

       this.name = name;  

    }    

    public int getAge() {  

        return age;  

    }    

    public void setAge(int age) {  

        this.age = age;  

    }    

    public double getHeight() {  

        return height;  

    }  

 

    public void setHeight(double height) {  

        this.height = height;  

    }    

    public double getWeight() {  

        return weight;  

    }    

    public void setWeight(double weight) {  

        this.weight = weight;  

    }    

    static class Builder{  

        private String name;  

        private int age;  

        private double height;  

       private double weight;  

       public Builder name(String name){  

            this.name=name;  

            return this;  

        }  

       public Builder age(int age){  

            this.age=age;  

            return this;  

       }  

        public Builder height(double height){  

            this.height=height;  

            return this;  

        }    

        public Builder weight(double weight){  

            this.weight=weight;  

            return this;  

        }  

 

        public Person build(){  

            return new Person(this);  

        }  

    }  

}  

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

 

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

Person.Builder builder=new Person.Builder();  

Person person=builder  

        .name("张三")  

        .age(18)  

        .height(178.5)  

        .weight(67.4)  

        .build();  

是不是有那么点感觉了呢

 

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

AlertDialog.Builder builder=new AlertDialog.Builder(this);  

AlertDialog dialog=builder.setTitle("标题")  

        .setIcon(android.R.drawable.ic_dialog_alert)  

        .setView(R.layout.myview)  

        .setPositiveButton(R.string.positive, new DialogInterface.OnClickListener() {  

            @Override  

            public void onClick(DialogInterface dialog, int which) {  

            }  

        })  

        .setNegativeButton(R.string.negative, new DialogInterface.OnClickListener() {  

            @Override  

            public void onClick(DialogInterface dialog, int which) {  

            }  

        })  

        .create();  

dialog.show();  

 

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

 

Gson中的GsonBuilder

GsonBuilder builder=new GsonBuilder();  

Gson gson=builder.setPrettyPrinting()  

        .disableHtmlEscaping()  

        .generateNonExecutableJson()  

        .serializeNulls()  

        .create();  


网络框架OKHttp

Request.Builder builder=new Request.Builder();  

Request request=builder.addHeader("","")  

    .url("")  

    .post(body)  

    .build(); 

 

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

 

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

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

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

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

3 观察者模式

二话不说,上来就是定义

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

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

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

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

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

 

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

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

 

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

public class Weather {  

    private String description;    

    public Weather(String description) {  

        this.description = description;  

    }    

    public String getDescription() {  

        return description;  

    }    

    public void setDescription(String description) {  

        this.description = description;  

    }    

    @Override  

    public String toString() {  

        return "Weather{" +  

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

                '}';  

    }  

}  

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

public class Observable<T> {  

    List<Observer<T>> mObservers = new ArrayList<Observer<T>>();    

    public void register(Observer<T> observer) {  

        if (observer == null) {  

            throw new NullPointerException("observer == null");  

        }  

        synchronized (this) {  

            if (!mObservers.contains(observer))  

               mObservers.add(observer);  

        }  

    }    

    public synchronized void unregister(Observer<T> observer) {  

        mObservers.remove(observer);  

    }    

    public void notifyObservers(T data) {  

        for (Observer<T> observer : mObservers) {  

            observer.onUpdate(this, data);  

        }  

    }    

}  

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

public interface Observer<T> {  

    void onUpdate(Observable<T> observable,T data);  

}  


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

 

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

public class Main {  

    public static void main(String [] args){  

        Observable<Weather> observable=new Observable<Weather>();  

        Observer<Weather> observer1=new Observer<Weather>() {  

            @Override  

            public void onUpdate(Observable<Weather> observable, Weather data) {  

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

            }  

        };  

        Observer<Weather> observer2=new Observer<Weather>() {  

            @Override  

            public void onUpdate(Observable<Weather> observable, Weather data) {  

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

            }  

        };    

        observable.register(observer1);  

        observable.register(observer2);     

       Weather weather=new Weather("晴转多云");  

        observable.notifyObservers(weather);    

        Weather weather1=new Weather("多云转阴");  

        observable.notifyObservers(weather1);    

        observable.unregister(observer1);    

        Weather weather2=new Weather("台风");  

        observable.notifyObservers(weather2);    

    }  

}  


输出也灭有问题

观察者1:Weather{description=’晴转多云’}
观察者2:Weather{description=’晴转多云’}
观察者1:Weather{description=’多云转阴’}
观察者2:Weather{description=’多云转阴’}
观察者2:Weather{description=’台风’}

 

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

Button btn=new Button(this);  

btn.setOnClickListener(new View.OnClickListener() {  

    @Override  

    public void onClick(View v) {  

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

    }  

});  

严格意义来说,这只能算是一个回调,但是我们可以把它看做是一个一对一的观察者模式,其实,只要是set系列的设置监听事件的方法最多都只能算是一种回调,但是一些监听器是通过add添加的,这些就观察者模式了比如,RecyclerView的addScrollListener方法

private List<OnScrollListener> mScrollListeners;  

public void addOnScrollListener(OnScrollListener listener) {  

    if (mScrollListeners == null) {  

        mScrollListeners = new ArrayList<OnScrollListener>();  

    }  

    mScrollListeners.add(listener);  

}  

public void removeOnScrollListener(OnScrollListener listener) {  

    if (mScrollListeners != null) {  

        mScrollListeners.remove(listener);  

    }  

}  

public void clearOnScrollListeners() {  

    if (mScrollListeners != null) {  

        mScrollListeners.clear();  

    }  

}  


然后有滚动事件时就触发观察者方法进行回调

public abstract static class OnScrollListener {  

    public void onScrollStateChanged(RecyclerView recyclerView, int newState){}  

    public void onScrolled(RecyclerView recyclerView, int dx, int dy){}  

}    

void dispatchOnScrolled(int hresult, int vresult) {  

    if (mScrollListeners != null) {  

        for (int i = mScrollListeners.size() - 1; i >= 0; i--) {  

            mScrollListeners.get(i).onScrolled(this, hresult, vresult);  

        }  

    }  

}  

void dispatchOnScrollStateChanged(int state) {  

    if (mScrollListeners != null) {  

        for (int i = mScrollListeners.size() - 1; i >= 0; i--) {  

            mScrollListeners.get(i).onScrollStateChanged(this, state);  

        }  

    }  

}  

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

 

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

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

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

EventBus.getDefault().register(Object subscriber);  

EventBus.getDefault().unregister(Object subscriber);    

EventBus.getDefault().post(Object event);  

比较重量级的库RxJava,创建一个被观察者

Observable<String> myObservable = Observable.create(    

    new Observable.OnSubscribe<String>() {    

        @Override    

        public void call(Subscriber<? super String> sub) {    

            sub.onNext("Hello, world!");    

            sub.onCompleted();    

        }    

    }    

);  


创建一个观察者,也就是订阅者

Subscriber<String> mySubscriber = new Subscriber<String>() {    

    @Override    

    public void onNext(String s) { System.out.println(s); }        

    @Override    

    public void onCompleted() { }    

    

    @Override    

    public void onError(Throwable e) { }    

};  


观察者进行事件的订阅

myObservable.subscribe(mySubscriber);  

 

4 策略模式

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

乍一看也没看出个所以然来,还是举个栗子,

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

public class TravelStrategy {  

    enum Strategy{  

        WALK,PLANE,SUBWAY  

    }  

    private Strategy strategy;  

    public TravelStrategy(Strategy strategy){  

        this.strategy=strategy;  

    }  

    public void travel(){  

        if(strategy==Strategy.WALK){  

            print("walk");  

        }else if(strategy==Strategy.PLANE){  

            print("plane");  

        }else if(strategy==Strategy.SUBWAY){  

            print("subway");  

        }  

    }        

    public void print(String str){  

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

    }        

    public static void main(String[] args) {  

        TravelStrategy walk=new TravelStrategy(Strategy.WALK);  

        walk.travel();            

        TravelStrategy plane=new TravelStrategy(Strategy.PLANE);  

        plane.travel();            

        TravelStrategy subway=new TravelStrategy(Strategy.SUBWAY);  

        subway.travel();  

    }  

}  

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

 

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

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

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

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

public interface Strategy {  

    void travel();  

}  


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

public class WalkStrategy implements Strategy{    

    @Override  

    public void travel() {  

        System.out.println("walk");  

   }  

public class PlaneStrategy implements Strategy{  

    @Override  

    public void travel() {  

        System.out.println("plane");  

    }    

}  

 

blic class SubwayStrategy implements Strategy{  

    @Override  

    public void travel() {  

        System.out.println("subway");  

    }  

}  

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

public class TravelContext {  

    Strategy strategy;  

  

    public Strategy getStrategy() {  

       return strategy;  

    }  

    public void setStrategy(Strategy strategy) {  

       this.strategy = strategy;  

    }  

    public void travel() {  

        if (strategy != null) {  

            strategy.travel();  

        }  

    }  

}  


测试一下代码

public class Main {  

    public static void main(String[] args) {  

        TravelContext travelContext=new TravelContext();  

        travelContext.setStrategy(new PlaneStrategy());  

        travelContext.travel();  

        travelContext.setStrategy(new WalkStrategy());  

        travelContext.travel();  

        travelContext.setStrategy(new SubwayStrategy());  

        travelContext.travel();  

    }  

}  

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

 

Android的源码中,策略模式也是使用相当广泛的,最典型的就是属性动画中的应用,我们知道,属性动画中有个叫插值器的东东,他的作用就是根据时间流逝的百分比来计算当前属性值改变的百分比

我们使用属性动画的时候,可以通过set方法对插值器进行设置,可以看到内部维持了一个时间差值器的引用,并设置getter和setter方法,默认情况下是先加速后减速的插值器,set方法如果传入的是null,则是线性插值器,而时间插值器TimeInterpolator是个接口,有一个接口继承了该接口,就是Interpolator,作用是为了保持兼容

private static final TimeInterpolator sDefaultInterpolator =  

        new AccelerateDecelerateInterpolator();    

private TimeInterpolator mInterpolator = sDefaultInterpolator;   

@Override  

public void setInterpolator(TimeInterpolator value) {  

    if (value != null) {  

        mInterpolator = value;  

    } else {  

        mInterpolator = new LinearInterpolator();  

    }  

}    

@Override  

public TimeInterpolator getInterpolator() {  

    return mInterpolator;  

}  

public interface Interpolator extends TimeInterpolator {  

    // A new interface, TimeInterpolator, was introduced for the new android.animation  

    // package. This older Interpolator interface extends TimeInterpolator so that users of  

    // the new Animator-based animations can use either the old Interpolator implementations or  

    // new classes that implement TimeInterpolator directly.  

}  

此外,还有一个BaseInterpolateor 插值器实现了 Interpolator接口,是一个抽象类

abstract public class BaseInterpolator implements Interpolator {  

    private int mChangingConfiguration;  

    /** 

     * @hide 

     */  

    public int getChangingConfiguration() {  

        return mChangingConfiguration;  

    }  

  

    /** 

    * @hide 

     */  

    void setChangingConfiguration(int changingConfiguration) {  

        mChangingConfiguration = changingConfiguration;  

    }  

}  

平时我们使用的时候通过设置不同的插值器,来实现不同的速率变化效果,比如线性,回弹,自由落体等,这些都是插值器接口的具体实现,也就是具体的插值器策略,详细看几个

 

public class LinearInterpolator extends BaseInterpolator implements NativeInterpolatorFactory {    

    public LinearInterpolator() {  

    }    

    public LinearInterpolator(Context context, AttributeSet attrs) {  

    }   

    public float getInterpolation(float input) {  

        return input;  

    }  

  

    /** @hide */  

    @Override  

    public long createNativeInterpolator() {  

        return NativeInterpolatorFactoryHelper.createLinearInterpolator();  

    }  

}  

 

public class AccelerateDecelerateInterpolator extends BaseInterpolator  

        implements NativeInterpolatorFactory {  

    public AccelerateDecelerateInterpolator() {  

    }  

  

    @SuppressWarnings({"UnusedDeclaration"})  

    public AccelerateDecelerateInterpolator(Context context, AttributeSet attrs) {  

    }    

    public float getInterpolation(float input) {  

        return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;  

    }    

    /** @hide */  

    @Override  

    public long createNativeInterpolator() {  

        return NativeInterpolatorFactoryHelper.createAccelerateDecelerateInterpolator();  

    }  

}  


内部使用的时候直接调用getInterpolation方法就可以返回对应值了,也就是属性值变化的百分比

 

属性动画中另外一个应用策略模式的地方就是估值器,他的作用是根据当前属性改变的百分比来计算改变后的属性值,该属性和插值器是类似的,有几个默认的实现,其中TypeEvaluator是一个接口。

public interface TypeEvaluator<T> {  

    public T evaluate(float fraction, T startValue, T endValue);  

 

}  

 

public class IntEvaluator implements TypeEvaluator<Integer> {    

    public Integer evaluate(float fraction, Integer startValue, Integer endValue) {  

        int startInt = startValue;  

        return (int)(startInt + fraction * (endValue - startInt));  

    }  

}  

 

public class FloatEvaluator implements TypeEvaluator<Number> {  

    public Float evaluate(float fraction, Number startValue, Number endValue) {  

        float startFloat = startValue.floatValue();  

        return startFloat + fraction * (endValue.floatValue() - startFloat);  

    }  

}  

 

public class PointFEvaluator implements TypeEvaluator<PointF> {    

    private PointF mPoint;    

    public PointFEvaluator() {  

    }    

    public PointFEvaluator(PointF reuse) {  

        mPoint = reuse;  

    }    

    @Override  

    public PointF evaluate(float fraction, PointF startValue, PointF endValue) {  

        float x = startValue.x + (fraction * (endValue.x - startValue.x));  

        float y = startValue.y + (fraction * (endValue.y - startValue.y));   

        if (mPoint != null) {  

            mPoint.set(x, y);  

            return mPoint;  

        } else {  

            return new PointF(x, y);  

        }  

    }  

}  


以上都是系统实现好的估值策略,在内部调用evaluate方法即可获得改变后的值,我们也可以自己定义自己的估值策略

 

在开源框架中,策略模式也无处不在,volley中,有一个策略重试接口

 

public interface RetryPolicy {   

    public int getCurrentTimeout();//获取当前请求用时(用于 Log)      

    public int getCurrentRetryCount();//获取已经重试的次数(用于 Log)    

    public void retry(VolleyError error) throws VolleyError;//确定是否重试,参数为这次异常的具体信息。在请求异常时此接口会被调用,可在此函数实现中抛出传入的异常表示停止重试。  

}  

在Volley中,该接口有一个默认的实现DefaultRetryPolicy,Volley默认的重试策略实现类,主要通过在retry(...)函数中判断 重试次数是否达到了上限,确定是否继续重试

public class DefaultRetryPolicy implements RetryPolicy {  

    ...  

}  


而策略的设置在Request类中

public abstract class Request<T> implements Comparable<Request<T>> {  

    private RetryPolicy mRetryPolicy;  

    public Request<?> setRetryPolicy(RetryPolicy retryPolicy) {  

        mRetryPolicy = retryPolicy;  

        return this;  

    }  

    public RetryPolicy getRetryPolicy() {  

        return mRetryPolicy;  

    }  

}  

 

此外,各大网络请求框架,或多或少的会用到缓存,缓存一般会定义一个Cache接口,然后实现不同的缓存策略,如内存缓存,磁盘缓存,等等,这个缓存的实现,其实也是可以使用策略模式,直接看Volley,里边也有缓存

 

/** 

 * An interface for a cache keyed by a String with a byte array as data. 

 */  

public interface Cache {  

    /** 

     * Retrieves an entry from the cache. 

     * @param key Cache key 

     * @return An {@link Entry} or null in the event of a cache miss 

     */  

    public Entry get(String key);  

    /** 

     * Adds or replaces an entry to the cache. 

     * @param key Cache key 

     * @param entry Data to store and metadata for cache coherency, TTL, etc. 

     */  

    public void put(String key, Entry entry);  

    /** 

     * Performs any potentially long-running actions needed to initialize the cache; 

     * will be called from a worker thread. 

     */  

    public void initialize();  

    /** 

     * Invalidates an entry in the cache. 

     * @param key Cache key 

     * @param fullExpire True to fully expire the entry, false to soft expire 

     */  

    public void invalidate(String key, boolean fullExpire);    

    /** 

     * Removes an entry from the cache. 

     * @param key Cache key 

    */  

    public void remove(String key);   

    /** 

     * Empties the cache. 

     */  

    public void clear();    

    /** 

    * Data and metadata for an entry returned by the cache. 

     */  

    public static class Entry {  

        /** The data returned from cache. */  

       public byte[] data;   

        /** ETag for cache coherency. */  

       public String etag;    

       /** Date of this response as reported by the server. */  

       public long serverDate;  

       /**The last modified date for the requested object. */  

        public long lastModified;    

        /** TTL for this record. */  

        public long ttl;    

        /** Soft TTL for this record. */  

        public long softTtl;  

  

        /** Immutable response headers as received from server; must be non-null. */  

        public Map<String, String> responseHeaders = Collections.emptyMap();  

  

        /** True if the entry is expired. */  

        public boolean isExpired() {  

            return this.ttl < System.currentTimeMillis();  

        }  

  

        /** True if a refresh is needed from the original data source. */  

        public boolean refreshNeeded() {  

           return this.softTtl < System.currentTimeMillis();  

        }  

    }  

}  


他有两个实现类NoCache和DiskBsaedCache,使用的时候设置对应的缓存策略即可

 

5,原型模式

定义:

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

 

 

本宝宝看不懂,没代码说毛啊,上代码:

public class Person{  

    private String name;  

    private int age;  

    private double height;  

    private double weight;  

      public Person(){            

    }    

    public String getName() {  

        return name;  

    }  

    public void setName(String name) {  

        this.name = name;  

    }  

    public int getAge() {  

        return age;  

    }    

    public void setAge(int age) {  

        this.age = age;  

    }   

    public double getHeight() {  

        return height;  

   }   

    public void setHeight(double height) {  

        this.height = height;  

    }  

    public double getWeight() {  

        return weight;  

    }  

    public void setWeight(double weight) {  

        this.weight = weight;  

    }    

    @Override  

    public String toString() {  

       return "Person{" +  

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

             ", age=" + age +  

                ", height=" + height +  

                ", weight=" + weight +  

                '}';  

    }  

}  


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

1,实现一个Cloneable接口

public class Person implements Cloneable{  

  

}  


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

@Override  

public Object clone(){  

    Person person=null;  

    try {  

        person=(Person)super.clone();  

        person.name=this.name;  

        person.weight=this.weight;  

        person.height=this.height;  

        person.age=this.age;  

    } catch (CloneNotSupportedException e) {  

        e.printStackTrace();  

    }  

    return person;  

}  


测试一下

public class Main {  

    public static void main(String [] args){  

        Person p=new Person();  

        p.setAge(18);  

        p.setName("张三");  

        p.setHeight(178);  

        p.setWeight(65);  

        System.out.println(p);    

        Person p1= (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集合,就酱紫:

private ArrayList<String> hobbies=new ArrayList<String>();    

public ArrayList<String> getHobbies() {  

    return hobbies;  

}    

public void setHobbies(ArrayList<String> hobbies) {  

    this.hobbies = hobbies;  

}  


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

@Override  

public Object clone(){  

    Person person=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<String>)this.hobbies.clone();  

    } catch (CloneNotSupportedException e) {  

        e.printStackTrace();  

    }  

    return person;  

}  


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

 

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

@Override  

public Object clone(){  

    return new Person(this);  

}  

 

public Person(Person person){  

    this.name=person.name;  

    this.weight=person.weight;  

    this.height=person.height;  

    this.age=person.age;  

    this.hobbies= new ArrayList<String>(hobbies);  

}  


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

 

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

先看Bundle类,

public Object clone() {  

    return new Bundle(this);  

}   

public Bundle(Bundle b) {  

    super(b);    

    mHasFds = b.mHasFds;  

    mFdsKnown = b.mFdsKnown;  

}  


然后是Intent类

@Override  

public Object clone() {  

    return new Intent(this);  

}  

public Intent(Intent o) {  

    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 = new ArraySet<String>(o.mCategories);  

    }  

    if (o.mExtras != null) {  

        this.mExtras = new Bundle(o.mExtras);  

    }  

    if (o.mSourceBounds != null) {  

        this.mSourceBounds = new Rect(o.mSourceBounds);  

    }  

    if (o.mSelector != null) {  

        this.mSelector = new Intent(o.mSelector);  

    }  

    if (o.mClipData != null) {  

        this.mClipData = new ClipData(o.mClipData);  

    }  

}  

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

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

 Intent shareIntent = new Intent(Intent.ACTION_SENDTO, uri);      

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

Intent intent = (Intent)shareIntent.clone() ;  

startActivity(intent);  

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

/** Returns a shallow copy of this OkHttpClient. */  

@Override   

public OkHttpClient clone() {  

    return new OkHttpClient(this);  

}  

private OkHttpClient(OkHttpClient okHttpClient) {  

    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 = okHttpClient.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;  

}  

save_snippets.png

转载于:https://my.oschina.net/u/2542649/blog/807322

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值