Android 經常使用的設計模式之Builder模式

這裏咱們經過一個例子來引出Build模。假設有一個Person類,他的一些屬性能夠爲null,能夠經過這個類來構架一大批人java

public class Person {  android

    private String name;  算法

    private int age;  設計模式

    private double height;  api

    private double weight;    緩存

    public String getName() {  cookie

        return name;  網絡

    }    框架

    public void setName(String name) {  socket

        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;  

}  

相關文章
相關標籤/搜索