對於開發來說,設計模式是必不可少的,就像是一個項目的架海紫金樑,而且熟悉常用的設計模式可以有助於我們研讀源碼。好,廢話不多說,開始介紹
1> 單例:
不想再說了,如果還有人不會,我叫他大爺。
大爺,您好!
概念:確保一個類只有一個實例,並且自行實例化並向整個系統提供整個實例
優點:
1,對於那些耗內存的類,只實例化一次,大大提高性能,尤其是移動開發中
2,程序運行中,始終保持只有一個實例在內存中
public class Singleton {
private static volatile Singleton instance = null;
private Singleton(){
}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
構造函數私有化,定義靜態函數獲得實例就不多說了,這裏着重說一下volatile:
volatile本質是在告訴jvm當前變量在寄存器中的值是不確定的,需要從內存中讀取,synchronized則是鎖定當前變量,只有當前線程可以訪問該變量,其他線程被阻塞住.(首先我們要先意識到有這樣的現象,編譯器爲了加快程序運行的速度,對一些變量的寫操作會先在寄存器或者是CPU緩存上進行,最後才寫入內存.
而在這個過程,變量的新值對其他線程是不可見的.而volatile的作用就是使它修飾的變量的讀寫操作都必須在內存中進行!)
再就是這個雙重判斷null :
這是因爲如果線程A進入了該代碼,線程B 在等待,這是A線程創建完一個實例出來後,線程B 獲得鎖進入同步代碼,實例已經存在,木有必要再創建一個,所以雙重判斷有必要。
哎,不說不說的又寫了這麼多。
Android中 用到的地方很多,比如Android-Universal-Image-Loader中的單例,EventBus中的單例
最後給出一個管理我們activity的類,可以作爲一個簡單工具類
public class ActivityManager {
private static volatile ActivityManager instance;
private Stack<Activity> mActivityStack = new Stack<Activity>();
private ActivityManager(){
}
public static ActivityManager getInstance(){
if (instance == null) {
synchronized (ActivityManager.class) {
if (instance == null) {
instance = new ActivityManager();
}
}
return instance;
}
public void addActicity(Activity act){
mActivityStack.push(act);
}
public void removeActivity(Activity act){
mActivityStack.remove(act);
}
public void killMyProcess(){
int nCount = mActivityStack.size();
for (int i = nCount - 1; i >= 0; i--) {
Activity activity = mActivityStack.get(i);
activity.finish();
}
mActivityStack.clear();
android.os.Process.killProcess(android.os.Process.myPid());
}
2> 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");
}
}
public 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 {
...
}
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);
網絡請求中最常用的OkHttp中,也應用了原型模式,就在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;
}
該文章轉載至人家的譯文好像還是位妹子qaq