設(shè)計(jì)模式--策略模式

一至非、問題的產(chǎn)生
舉例:鴨子模型
鴨子模型中會(huì)出現(xiàn)各種鴨子篷朵,會(huì)游泳勾怒,會(huì)呱呱叫婆排,于是設(shè)計(jì)一個(gè)鴨子超類(Duck)声旺,并讓各種鴨子繼承此超類一、問題的產(chǎn)生
舉例:鴨子模型
鴨子模型中會(huì)出現(xiàn)各種鴨子段只,會(huì)游泳腮猖,會(huì)呱呱叫,于是設(shè)計(jì)一個(gè)鴨子超類(Duck)赞枕,并讓各種鴨子繼承此超類


image.png

這時(shí)澈缺,如果想讓鴨子有會(huì)飛的特征,怎么辦呢炕婶?加上一個(gè)fly()方法姐赡,讓所有鴨子繼承fly()?


image.png

結(jié)果是所有的鴨子都會(huì)飛了柠掂,哪怕是不應(yīng)該會(huì)飛的橡皮鴨子项滑,另外橡皮鴨子也不是呱呱叫而是吱吱叫,這又怎么辦呢涯贞?采用覆蓋的方法枪狂,把quack()和fly()覆蓋成正確的方法危喉?
image.png

如果是誘餌鴨,是一只木頭假鴨州疾,既不會(huì)飛也不會(huì)叫怎么辦呢辜限?
image.png

如果有成千上萬種鴨子,都需要檢查哪些方法需要覆蓋嗎严蓖?

二薄嫡、解決辦法
有一個(gè)設(shè)計(jì)原則可以解決這些問題:找出應(yīng)用中可能需要變化之處,把它們獨(dú)立出來谈飒,不要和那些不需要變化的代碼混在一起岂座。也就是說把會(huì)變化的部分取出并封裝起來,以便以后可以輕易的改到或擴(kuò)充此部分杭措,而不影響不需要變化的其他部分费什。
鴨子模型中,fly()和quack()是需要經(jīng)常變化或修改的地方手素,把它們獨(dú)立出來鸳址,一個(gè)和fly相關(guān),一個(gè)和quack相關(guān)泉懦,并實(shí)現(xiàn)各自的動(dòng)作稿黍。
如何實(shí)現(xiàn)這兩個(gè)動(dòng)作呢?如果這兩種行為是在超類Duck的具體實(shí)現(xiàn)崩哩,或是一個(gè)接口巡球,由子類自行實(shí)現(xiàn),我們還是要在每個(gè)具體的鴨子類中實(shí)現(xiàn)它邓嘹。
另一個(gè)設(shè)計(jì)原則告訴我們:針對(duì)接口編程酣栈,而不是針對(duì)實(shí)現(xiàn)編程。制造一組行為類專門實(shí)現(xiàn)FlyBehavior和QuackBehavior汹押,由此行為類來實(shí)現(xiàn)行為接口矿筝,而不是Duck類。


image.png

image.png

最后鴨子模型的整體設(shè)計(jì)如下:


image.png

可以看出Duck類“HAS-A”(有一個(gè)→)接口實(shí)現(xiàn)<<interface>>FlayBehavior和<<interface>>QuackBehavior棚贾。
各種鴨子類型MallardDuck(綠頭鴨)窖维、ReadheadDuck(紅頭鴨)等“IS-A”(是一個(gè)
image.png
)繼承extend超類Duck的子類。
FlyWithWings妙痹、Quack等是implement(實(shí)現(xiàn)
image.png
)FlayBehavior和QuackBehavior 接口铸史。
“有一個(gè)HAS-A”的關(guān)系可以將各種行為(飛行,呱呱叫)委托他們處理怯伊。和“繼承”不同的地方在于琳轿,鴨子的行為不是繼承來的,而是適當(dāng)?shù)男袨椤敖M合”來的。
這遵循了一個(gè)設(shè)計(jì)原則:多用組合利赋,少用繼承水评。

這種組合的方法具有很大的彈性,不僅可將算法族封裝成類媚送,還可以“在運(yùn)行時(shí)動(dòng)態(tài)改變行為”中燥。

三、策略模式(Strategy Pattern)
1塘偎、定義
策略模式定義了算法族疗涉,分別封裝起來,讓他們直接可以互相替換吟秩,此模式讓算法的變化獨(dú)立于使用算法的客戶咱扣。在某一場(chǎng)景需要有多種情況,不同情況有不同的處理(大量 if-else 或者 switch)涵防,但大致功能是一樣的闹伪,這時(shí)我們可以考慮用策略模式實(shí)現(xiàn)。

2壮池、優(yōu)缺點(diǎn)
A偏瓤、優(yōu)點(diǎn):
1)每個(gè)算法都獨(dú)立于其他,方便單元測(cè)試椰憋。
2)結(jié)構(gòu)更加清晰厅克,不會(huì)有一堆條件語句處理不同情況。
3)客戶端引用的是接口橙依,耦合度低证舟,擴(kuò)展性強(qiáng)。
B窗骑、缺點(diǎn):
1)客戶端必須知道所有的策略類女责,并自行決定使用哪一個(gè)策略類。這就意味著客戶端必須理解這些算法的區(qū)別慧域,以便適時(shí)選擇恰當(dāng)?shù)乃惴惱鹬瘛Q言之浪读,策略模式只適用于客戶端知道所有的算法或行為的情況。
2)隨著策略增加,子類會(huì)增加耍鬓。

3碉钠、舉例
假設(shè)用戶分為普通用戶、會(huì)員痘拆、金牌會(huì)員仰禽,不同的用戶,購(gòu)買商品有不同的打折方式,一個(gè)用戶每消費(fèi)10000就增加一個(gè)級(jí)別吐葵,以上兩種用戶購(gòu)買產(chǎn)品花費(fèi)原價(jià)规揪、九折、八折温峭。
A猛铅、定義計(jì)算價(jià)格的策略接口

public interface CalPrice { //根據(jù)原價(jià)返回一個(gè)最終的價(jià)格 
   Double calPrice(Double orgnicPrice);
 }

B、3種用戶購(gòu)買價(jià)格的計(jì)算方式的實(shí)現(xiàn)

public class Orgnic implements CalPrice {//普通用戶
  @Override 
  public Double calPrice(Double orgnicPrice) { 
    return orgnicPrice; 
  } 
}
public class Vip implements CalPrice { //會(huì)員
  @Override
  public Double calPrice(Double orgnicPrice) { 
    return orgnicPrice * 0.9; 
  } 
}
public class SuperVip implements CalPrice { //金牌會(huì)員
  @Override 
  public Double calPrice(Double orgnicPrice) { 
    return orgnicPrice * 0.8; 
  }  
}

C凤藏、客戶類

public class Customer { 
  private Double totalAmount = 0;//客戶消費(fèi)的總額 
  private Double amount = 0;//客戶單次消費(fèi)金額 
  //每個(gè)客戶都有一個(gè)計(jì)算價(jià)格的策略奸忽,初始都是普通計(jì)算,即原價(jià)
  private CalPrice calPrice = new Orgnic();
  //客戶產(chǎn)品揖庄,就會(huì)增加它的總額 
  public void buy(Double amount) { 
    this.amount = amount; 
    totalAmount += amount; 
    if (totalAmount > 20000) {//改為金牌會(huì)員價(jià)格 
      calPrice = new SuperVip(); 
    } else if (totalAmount > 10000) {//改為會(huì)員價(jià)格 
      calPrice = new Vip(); 
    } 
  } 

  //計(jì)算客戶最終要付的錢 
  public Double calLastAmount() { 
    return calPrice.calPrice(amount); 
  } 
}

D栗菜、客戶端調(diào)用,系統(tǒng)會(huì)幫我們自動(dòng)調(diào)整收費(fèi)策略蹄梢。

public class Client { 
  public static void main(String[] args) { 
    Customer customer = new Customer(); 
    customer.buy(5000); 
    System.out.println("需要付錢:" + customer.calLastAmount());
    customer.buy(12000); 
    System.out.println("需要付錢:" + customer.calLastAmount());
    customer.buy(12000); 
    System.out.println("需要付錢:" + customer.calLastAmount()); 
  } 
}

客戶不再依賴于具體的收費(fèi)策略疙筹,依賴于抽象永遠(yuǎn)是正確的。

4禁炒、在android中的應(yīng)用
A腌歉、ListAdapter
如果ListView 要顯示的子布局是個(gè)簡(jiǎn)單的文字時(shí),我們可以使用 ArrayAdapter

mListView = (ListView) findViewById(R.id.list_view);
mListView.setAdapter(new ArrayAdapter<>(
        this,
        R.layout.item_text,
        Arrays.asList("a", "b", "c")));

要顯示復(fù)雜些的布局時(shí)齐苛,需要用 BaseAdapter

mListView = (ListView) findViewById(R.id.list_view);
mListView.setAdapter(new BaseAdapter() {
      @Override
      public int getCount() {
          return mData.size();
      }
        
      @Override
      public object getItem(int positon) {
          return mData.get(position);
      }
        
      @Override
      public long getItemId(int position) {
          return position;
      }
        
      @Override
      public View getView(int position, View convertView, ViewGroup parent) {
          convertView = ...
          return converView;
      }
});

可以看到翘盖,當(dāng)更換Adapter的具體實(shí)現(xiàn)時(shí),仍然調(diào)用的是ListView.setAdapter(…)方法凹蜂,查看ListView源碼馍驯,發(fā)現(xiàn)setAdapter方法的參數(shù)是一個(gè) ListAdapter。

/** 
 * Sets the data behind this ListView.
 *
 * The adapter passed to this method may be wrapped by a {@Link WrapperList Adatper}
 * depending ont the ListView features currently in use. For instance, adding
 * headers and/or footers will cause the adapter to be wrapped.
 *
 * @param adapter the ListAdapter which is responsible for maintaining the 
 *        data backing this list and for producing a view to represent an
 *        item in that data set.
 *
 * @see #getAdapter()
 */
@Overid
public void setAdapter(ListAdapter adapter) {

繼續(xù)看 ListAdapter 源碼和類結(jié)構(gòu)


image.png

可以看到ListAdapter是一個(gè)接口玛痊,ArrayAdapter和BaseAdapter是它的一個(gè)實(shí)現(xiàn)類汰瘫,在 ListView 中引用的是接口 ListAdapter,這就是一個(gè)策略模式的使用擂煞。
B混弥、時(shí)間插值器TimeInterpolator
時(shí)間插值器,它是一個(gè)接口对省,定義了動(dòng)畫改變的速率蝗拿,允許動(dòng)畫進(jìn)行非勻速變化。 在使用屬性動(dòng)畫時(shí)蒿涎,可以根據(jù)需要選擇合適的時(shí)間插值器

ObjectAnimator animator = ObjectAnimator.ofFloat(mListView, View.Alpha, 0f, 1f);
animator.setInterpolator(new AccelerateInterpolator()); //加速
animator.setInterpolator(new OvershootInterpolator()); //跑到頭又返回來

和ListView的setAdapter一樣哀托,ValueAnimator的setInterpolator方法中也引用的是接口TimeInterpolator

/** 
 * The time interpolator used in calculating the elapsed fraction of this animation. The
 * interpolator determines whether the animation runs with linear or non_linear mation,
 * such as acceleration and deceleration. The default value is
 * {@link android.view.animation.AccelerateDecelerateInterpolator}
 * 
 * @param value the interpolator to be used by this animation. A value of <code>null</code>
 * will result in linear interpolation. 
 */
@Overid
public void setInterpolator(TimeInterpolator value) {
   if (value != null) {
       mInterpolator = value;
   } else {
       mInterpolator = new LinearInterpolator();
   }
}

TimeInterpolator 源碼及類結(jié)構(gòu)


image.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市劳秋,隨后出現(xiàn)的幾起案子仓手,更是在濱河造成了極大的恐慌胖齐,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,273評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件嗽冒,死亡現(xiàn)場(chǎng)離奇詭異呀伙,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)添坊,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,349評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門区匠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人帅腌,你說我怎么就攤上這事驰弄。” “怎么了速客?”我有些...
    開封第一講書人閱讀 167,709評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵戚篙,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我溺职,道長(zhǎng)岔擂,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,520評(píng)論 1 296
  • 正文 為了忘掉前任浪耘,我火速辦了婚禮乱灵,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘七冲。我一直安慰自己痛倚,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,515評(píng)論 6 397
  • 文/花漫 我一把揭開白布澜躺。 她就那樣靜靜地躺著蝉稳,像睡著了一般。 火紅的嫁衣襯著肌膚如雪掘鄙。 梳的紋絲不亂的頭發(fā)上耘戚,一...
    開封第一講書人閱讀 52,158評(píng)論 1 308
  • 那天,我揣著相機(jī)與錄音操漠,去河邊找鬼收津。 笑死,一個(gè)胖子當(dāng)著我的面吹牛浊伙,可吹牛的內(nèi)容都是我干的撞秋。 我是一名探鬼主播,決...
    沈念sama閱讀 40,755評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼吧黄,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼部服!你這毒婦竟也來了唆姐?” 一聲冷哼從身側(cè)響起拗慨,我...
    開封第一講書人閱讀 39,660評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后赵抢,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體剧蹂,經(jīng)...
    沈念sama閱讀 46,203評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,287評(píng)論 3 340
  • 正文 我和宋清朗相戀三年烦却,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了宠叼。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,427評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡其爵,死狀恐怖冒冬,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情摩渺,我是刑警寧澤简烤,帶...
    沈念sama閱讀 36,122評(píng)論 5 349
  • 正文 年R本政府宣布,位于F島的核電站摇幻,受9級(jí)特大地震影響横侦,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜绰姻,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,801評(píng)論 3 333
  • 文/蒙蒙 一枉侧、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧狂芋,春花似錦榨馁、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,272評(píng)論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至黍特,卻和暖如春蛙讥,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背灭衷。 一陣腳步聲響...
    開封第一講書人閱讀 33,393評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工次慢, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人翔曲。 一個(gè)月前我還...
    沈念sama閱讀 48,808評(píng)論 3 376
  • 正文 我出身青樓迫像,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親瞳遍。 傳聞我的和親對(duì)象是個(gè)殘疾皇子闻妓,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,440評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容

  • 目錄 本文的結(jié)構(gòu)如下: 引言 什么是策略模式 模式的結(jié)構(gòu) 典型代碼 代碼示例 策略模式和模板方法模式的區(qū)別 優(yōu)點(diǎn)和...
    w1992wishes閱讀 862評(píng)論 1 7
  • 策略模式是指對(duì)一系列的算法定義,并將每一個(gè)算法封裝起來掠械,而且使它們還可以相互替換由缆。策略模式讓算法獨(dú)立于使用它的客戶...
    做一只快樂的碼農(nóng)1990閱讀 197評(píng)論 0 0
  • Strategy:找出變化的部分獨(dú)立出來進(jìn)行封裝注祖,對(duì)象行為型模式。 策略模式定義了算法簇均唉,分別封裝起來是晨,讓他們相互...
    TheLittleSky閱讀 204評(píng)論 1 0
  • 概念及定義 概念在完成某一功能時(shí),有時(shí)需要根據(jù)不同環(huán)境采取不同的策略或行為舔箭。將這些不同的策略或行為(稱為算法)一一...
    maxwellyue閱讀 536評(píng)論 0 0
  • 今天在課程中罩缴,最大的收獲是接納自己和感恩。 無論你是優(yōu)秀的层扶,還是自卑的箫章,是善良的還是有邪惡的部分,是匱乏的還是富足...
    聞宣閱讀 62評(píng)論 0 0