尋找android中的設計模式(二)

尋找android中的設計模式(二)


  1. 概述

    前面學習了單例模式和觀察者模式,其中觀察者模式可以很好的降低對象直接的耦合。後面的模式會接觸到更多的設計原則。

  2. 尋找策略模式

    定義:定義了算法族,分別封裝起來,讓它們之間可以互相替換,此模式讓算法的變化獨立於使用算法的客戶。

    學完之後,我也思考着生活當中哪些地方可以使用到。就以遊戲爲例吧,需求描述是:有很多個英雄,每個英雄都有自己的技能和坐騎。用戶可以隨意定製英雄的技能和坐騎。


  1. 根據策略模式,我們先把會變化的技能和坐騎抽出來,定義兩個接口:

    public interface IAttackBehavior {
    	public void attack();
    }
    public interface IMountBehavior {
    	public void mount();
    }

  2. 根據接口實現現有的幾個算法,也就是各種技能和坐騎。簡單舉幾個:

    public class FireAttack implements IAttackBehavior {
    
    	@Override
    	public void attack() {
    		System.out.println("火攻擊");
    	}
    
    }
    public class WaterAttack implements IAttackBehavior {
    
    	@Override
    	public void attack() {
    		System.out.println("水攻擊");
    	}
    
    }
    public class ElectricAttack implements IAttackBehavior {
    
    	@Override
    	public void attack() {
    		System.out.println("電攻擊");
    	}
    
    }

    public class HorseMount implements IMountBehavior {
    
    	@Override
    	public void mount() {
    		System.out.println("騎馬坐騎");
    	}
    
    }
    public class CarMount implements IMountBehavior {
    
    	@Override
    	public void mount() {
    		System.out.println("開車坐騎");
    	}
    
    }

    public class AirMount implements IMountBehavior {
    
    	@Override
    	public void mount() {
    		System.out.println("飛行坐騎");
    	}
    
    }
    
    
    


  3. 有了不同的算法,我們定義一個英雄的父類,將技能和坐騎組合進來。

    public abstract class Hero {
    	protected String name;
    	private IAttackBehavior attackBehavior;
    	private IMountBehavior mountBehavior;
    
    	public Hero setAttackBehavior(IAttackBehavior attackBehavior) {
    		this.attackBehavior = attackBehavior;
    		return this;
    	}
    
    	public Hero setMountBehavior(IMountBehavior mountBehavior) {
    		this.mountBehavior = mountBehavior;
    		return this;
    	}
    
    	public void attack() {
    		this.attackBehavior.attack();
    	}
    
    	public void mount() {
    		this.mountBehavior.mount();
    	}
    }

    這樣就可以給英雄設置不同的技能和坐騎。

  4. 實現幾個英雄(客戶)

    public class HeroA extends Hero {
    	public HeroA(String name) {
    		this.name = name;
    		System.out.println(name);
    	}
    }
    public class HeroB extends Hero {
    	public HeroB(String name) {
    		this.name = name;
    	}
    }

    可以看到客戶(英雄)和算法(技能和坐騎)完全獨立。

  5. 測試

    	public static void main(String[] args) {
    		Hero heroA = new HeroA("英雄A");
    		heroA.setAttackBehavior(new FireAttack()).setMountBehavior(
    				new AirMount());
    		heroA.attack();
    		heroA.mount();
    	}

    打印結果:

    英雄A
    火攻擊
    飛行坐騎

    該模式的好處是當需要增加一種算法(技能)可以任意添加。而且可以很方便的給客戶(英雄)定製算法(技能)。

    接着尋找android源碼中的該模式:


    1. 動畫插值器Interpolator

      插值器的主要作用是可以控制動畫的變化速率。它類似上面的算法,可以給動畫定製不同的變化速率。下面是google提供的一些插值器實現類:


      TimeInterpolator是一個接口:

      public interface TimeInterpolator {
      
          /**
           * Maps a value representing the elapsed fraction of an animation to a value that represents
           * the interpolated fraction. This interpolated value is then multiplied by the change in
           * value of an animation to derive the animated value at the current elapsed animation time.
           *
           * @param input A value between 0 and 1.0 indicating our current point
           *        in the animation where 0 represents the start and 1.0 represents
           *        the end
           * @return The interpolation value. This value can be more than 1.0 for
           *         interpolators which overshoot their targets, or less than 0 for
           *         interpolators that undershoot their targets.
           */
          float getInterpolation(float input);
      }
      

      找到了算法類(各種插值器)和接口後,下面看下屬性動畫類ValueAnimator中如何使用:

          // The time interpolator to be used if none is set on the animation
          private static final TimeInterpolator sDefaultInterpolator =
                  new AccelerateDecelerateInterpolator();

          /**
           * The time interpolator to be used. The elapsed fraction of the animation will be passed
           * through this interpolator to calculate the interpolated fraction, which is then used to
           * calculate the animated values.
           */
          private TimeInterpolator mInterpolator = sDefaultInterpolator;
          /**
           * The time interpolator used in calculating the elapsed fraction of this animation. The
           * interpolator determines whether the animation runs with linear or non-linear motion,
           * 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.
           */
          @Override
          public void setInterpolator(TimeInterpolator value) {
              if (value != null) {
                  mInterpolator = value;
              } else {
                  mInterpolator = new LinearInterpolator();
              }
          }

      這裏的ValueAnimator相當客戶類,它的父類Animator是一個超類,類似上面英雄的父類。客戶類ValueAnimator默認給了一個            AccelerateDecelerateInterpolator插值器(先加速後減速)。我們可以給客戶設置不同的插值器(DecelerateInterpolatorAcceleratelnterpolator等);

      從動畫插值器的設計來分析,它封裝了變化即將各種算法(插值器)獨立起來,將插值器組合進來沒有用繼承。對於ValueAnimator只針對客戶,不針對插值器的算法,符合針對接口編程,不針對實現編程。

    2. 列表適配器

      android列表顯示數據的開發流程一般是這樣的:獲取數據-》定義一個包含該數據的適配器-》定義一個列表-》給列表設置這個適配器。

      思考一下,是否符合策略模式呢?

      我認爲是符合的,適配器是算法,可以有不同的算法(適配器),列表是客戶,可以有多個客戶(列表佈局、網格佈局)。而且客戶完全獨立與算法。下面查找下是否符合OO設計原則。首先看下算法(適配器)是否實現接口:

      public interface ListAdapter extends Adapter {

      public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter {

       可以看到確實是的。接着看下客戶listview是否針對接口編程:

          public void setAdapter(ListAdapter adapter) {
              if (mAdapter != null && mDataSetObserver != null) {
                  mAdapter.unregisterDataSetObserver(mDataSetObserver);
              }
      
              resetList();
              mRecycler.clear();
      
              if (mHeaderViewInfos.size() > 0|| mFooterViewInfos.size() > 0) {
                  mAdapter = new HeaderViewListAdapter(mHeaderViewInfos, mFooterViewInfos, adapter);
              } else {
                  mAdapter = adapter;
              }

      可以看到客戶不會自己去實現一個算法(適配器),而是把算法設置進來使用。不僅封裝了變化,而且將接口組合。


       

      學完了策略模式後,對設計原則有了簡單的瞭解。當然僅僅是學會了幾條要領,要掌握精髓,還需不斷練習。目前涉及OO原則如下:


        1. 封裝變化

        2. 多用組合,少用繼承

        3. 針對接口編程,不針對實現編程

        4. 爲交互對象之間的鬆耦合設計而努力

        5. 類應該對擴展開發,對修改關閉

        6. 依賴抽象,不要依賴具體類




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章