Android开发中无处不在的设计模式——策略模式

这个系列停更了好久了,差不多可以重新拿起来更一篇了,这篇文章主要介绍策略模式。在这之前,先温习一下前面介绍的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语句,而这违反了面向对象的原则之一,对修改封闭。而这时候,策略模式则可以完美的解决这一切。

首先,需要定义一个策略接口。


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();
    }
}

输出结果如下

plane
walk
subway

可以看到,应用了策略模式后,如果我们想增加新的出行方式,完全不必要修改现有的类,我们只需要实现策略接口即可,这就是面向对象中的对扩展开放准则。假设现在我们增加了一种自行车出行的方式。只需新增一个类即可。

public class BikeStrategy implements Strategy{

    @Override
    public void travel() {
        System.out.println("bike");
    }

}

之后设置策略即可

public class Main {
    public static void main(String[] args) {
        TravelContext travelContext=new TravelContext();
        travelContext.setStrategy(new BikeStrategy());
        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.
}

此外还有一个BaseInterpolator插值器实现了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);
        }
    }
}
public class ArgbEvaluator implements TypeEvaluator {
    private static final ArgbEvaluator sInstance = new ArgbEvaluator();

    public static ArgbEvaluator getInstance() {
        return sInstance;
    }

    public Object evaluate(float fraction, Object startValue, Object endValue) {
        int startInt = (Integer) startValue;
        int startA = (startInt >> 24) & 0xff;
        int startR = (startInt >> 16) & 0xff;
        int startG = (startInt >> 8) & 0xff;
        int startB = startInt & 0xff;

        int endInt = (Integer) endValue;
        int endA = (endInt >> 24) & 0xff;
        int endR = (endInt >> 16) & 0xff;
        int endG = (endInt >> 8) & 0xff;
        int endB = endInt & 0xff;

        return (int)((startA + (int)(fraction * (endA - startA))) << 24) |
                (int)((startR + (int)(fraction * (endR - startR))) << 16) |
                (int)((startG + (int)(fraction * (endG - startG))) << 8) |
                (int)((startB + (int)(fraction * (endB - startB))));
    }
}

上面的都是一些系统实现好的估值策略,在内部调用估值器的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();
        }
    }

}

它有两个实现类NoCacheDiskBasedCache,使用的时候设置对应的缓存策略即可。

在android开发中,ViewPager是一个使用非常常见的控件,它的使用往往需要伴随一个Indicator指示器。如果让你重新实现一个ViewPager,并且带有Indicator,这时候,你会不会想到用策略模式呢?在你自己写的ViewPager中(不是系统的,当然你也可以继承系统的)持有一个策略接口Indicator的变量,通过set方法设置策略,然后ViewPager滑动的时候调用策略接口的对应方法改变指示器。默认提供几个Indicator接口的实现类,比如圆形指示器CircleIndicator、线性指示器LineIndicator、Tab指示器TabIndicator、图标指示器IconIndicator 等等等等。有兴趣的话自己去实现一个吧。

WINAPI,WinAPI手册,最全的WINAPI函数手册WINAPI开发必备 目录 1 内容简介 14 前言 14 第一章 Win32 API概论 14 为什么使用 Wiu32 API 14 Win32 API 简介 15 第二章 窗口管理函数 16 第一节 易用特性函数(Accessibility Features) 20 SoundSentryProc 20 SystemParametersinfo 21 第二节 按钮函数(Button) 34 CheckDlgButton 34 CheckRadioButton 34 IsDlgButtonChecked 35 第三节 插入标记(^)函数(Caret) 36 CreateCaret 36 DestroyCaret 37 GetCaretBlinkTime 37 GetCaretPos 38 HideCaret 38 SetCaretBlinkTime 39 SetCaretPos 39 ShowCaret 40 第四节 组合框函数(Combo box) 40 CCHookProc 40 CFHookProc 42 ChooseColor 43 ChooseFont 44 CommDlgExtendedEorror 45 DlgDirListComboBox 47 DlgDirSelectEx 49 DlgDirSelectComboBoxEx 50 FindText 50 FRHookProc 51 GetFileTitle 52 GetOpenFileName 53 GetSaveFileName 54 OFNGookProc 54 OFNHookProcOldStyle 56 PagePaintHook 57 PageSetupDlg 58 pageSetupHook 58 PrintDlg 59 PrintdlgEx 60 PrintHookProc 61 ReplaceText 62 SetupHookProc 63 第五节 标函数(Cursor) 64 ClipCursor 64 CopyCursor 65 CreateCursor 65 DestroyCursor 66 GetClipCursor 67 GetCursor 67 GetCursorpos 67 LoadCursorFromFile 68 SetCursor 68 SetCursorPos 69 SetSystemCursor 69 ShowCursor 70 LoadCursor 71 第六节 对话框函数(Dialog Box) 72 CreateDialog 72 CreateDialoglndirect 73 CreateDialoglndirectParam 74 CreateDialogParam 76 DefDlgProc 77 DialogBox 78 DialogBoxlndirect 79 DialogBoxlndirectParam 80 DialogBoxParam 81 DialogProc 82 EndDialog 83 GetDialogBaseUnits 83 GetDigCtrllD 84 GetDigltem 85 GetDlgltemlnt 85 GetDlgltemText 86 GetNextDlgGroupltem 87 GetNexTDlgTabltem 88 IsDialogMessage 88 MapDialogRect 89 MessageBox 90 MessageBoxEx 93 SendDlgltemMessage 94 SetDlgltemlnt 95 SetDlgltemText 95 MessageBoxlndirect 96 第七节 编辑控制函数(Edit Control) 97 EditWordBreakproc 97 第八节 图标函数(Icon) 98 Copylcon 98 Createlcon 99 CreatelconFromResource 100 CreatelconFromResourceEx 101 Destroylcon 102 Create_cpm_mdorect_ZIWe 102 Drawlcon 103 DrawlconEx 104 ExtractAssociatedlcon 105 Extractlcon 106 ExtractlconEx 106 Getlconlnfo 107 LookuplconldFromDirectory 108 LookuplconldFrom 109 Loadlcon 110 第九节 键盘加速器函数(Keyboard Accelerator) 110 CopyAcceleratorTable 111 CreateAcceleratorTable 111 DestroyAcceleratorTable 112 LoadAccelerators 112 TranslateAccelerator 113 第十节 键盘输入函数(Keyboard Input) 114 ActivateKeyboadLayout 114 EnableWindow 115 GetActiveWindows 116 GetAsyncKeyState 116 GetFocus 117 GetKBCodePage 118 GetKeyboardLayout 118 GetKeyboardLayoutList 118 GetKeyboardLayoutName 119 GetKeyboardState 119 GetKeyNameText 120 GetKeyState 121 IsWindowEnabled 122 keybd_event 122 LoadKeyboardLayout 123 MapVirtualKey 124 MapVlrtualKeyEx 126 OemKeyScan 126 RegisterHotKey 127 SetActiveWindow 128 SetFocus 129 SetKeyboardState 130 ToAscii 130 ToAsciiCxToAsciiCx 131 ToUnicode 132 ToUnicodeEx 133 UnloadKeyboardL 134 UnreglsterHotKey 134 VkKeyScan 135 vkKeyScanEx 136 第十一节 列表框函数(List boX) 136 DlgDirList 136 DlgDirSelectEx(2) 137 第十二节 菜单函数(Menu) 138 CheckMenuRadlol 138 CreateMenu 139 CreatePopupMenu 140 DeleteMenu 140 DestroyMenu 141 DrawMenuBar 141 EnableMenultem 142 GetMenu 143 GetMenuDefaultltem 143 GetMenultemlD 144 GetMenultemlnfo 144 GetMenultemRect 145 getSubMenu 145 GetSystemMenu 146 HlllteMenultem 147 InsertMenultem 147 IsMenu 148 LoadMenu 149 LoadMenulndirect 149 MenultemFromPo 150 RemoveMenu 150 SetMenu 151 SetMenuDefaultltem 151 SetMenultemBitm 152 SetMenultemlnfo 153 TrackPopupMenu 154 TrackPopupMenuEx 155 AppendMenu 156 CheckMenultem 159 GetMenuCheckMarkDimensions 159 GetMenuState 160 GetMenuString 161 InsertMenu 161 ModifyMenu 163 第十三节 消息和消息总队列函数(Message and Message Queue) 165 BroadcastSystemMessage 165 DispatchMessage 166 GetlnputState 167 GetMessage 167 GetMessageExtralnfo 168 GetMessagePos 168 GetMessageTime 169 GetQueueStatus 170 InSendMessage 171 InSendMessageEx 171 PeekMessage 172 PostMessage 173 PostQuitMessage 174 PostThreadMessage 175 RegisterWindowsMessage 176 ReplyMessage 176 SendAsyncProc 177 SendMessage 178 SendMessageCallback 178 SendMessageTImeout 179 SendNotifyMessage 181 SendMessageExtralnfo 181 TranslateMessage 182 WaltMessage 183 PostAppMessage 183 SetMessageQueue 183 第十四节 鼠标输入函数(Mouse Input) 183 DragDetect 183 GetCapture 184 GetDoubleCllckTime 184 GetMouseMovePoints 185 mouse_event 186 ReleaseCapture 188 SetCapture 188 SetDoubleCllckTime 189 SwapMouseButton 189 TrackMouseEvent 190 第十五节 多文档接口函数(Multiple Document Interface) 191 CreateMDIWindow 191 DefFrameProc 192 DefMDIChildProc 193 TranslateMdISysAccel 194 第十六节 资源函数(Resource) 195 BeginUpdateResource 195 CopyImage 195 EndUpdateResource 197 EnumResLangProc 197 EnumResNameProc 198 EnumResourceLanguages 199 EnumResourceNames 200 EnumResourceTypes 200 EnumResTypeProc 201 FlndResource 202 FindResourceEx 203 LoadImage 204 LoadResource 206 LockResource 207 SlzeofResource 207 UpdateResource 208 FreeResource 209 UnlockResource 209 第十七节 滚动条函数(Scroll Bar) 209 EnableScrollBar 209 GetScrolllnfo 210 ScrollDC 211 ScrollWindowEx 212 SetScrolllnfo 214 ShowScrollBar 215 GetScrollPos 215 GetScrollRange 216 ScrollWindow 217 SetScrollPos 218 SetScrollRange 219 第十八节 窗口函数(Window) 220 AdlustWindowRect 220 AdjustWindowRectEx 221 AnImateWindow 222 ArrangelconlcWindows 223 BeginDeferWindowPos 223 BromgWindowToTop 224 CascadeWindows 225 ChildWindowFromaPoint 225 ChildWindowFromaPointEx 226 CloseWindow 227 Create Window 227 CreateWindowEx 233 DeferWindowPos 235 DestroyWindow 238 EnableWindow 238 EndDeferWindowPos 239 EnumChildProc 240 EnumTreadWindows 240 EnumThreadWndProc 241 EnumWindows 241 EnumWindowsProc 242 FindWindow 243 FlndWindowEx 243 GetClientRect 244 GetDesktopWindow 244 GetForegroundWindow 245 GetLastActivePopup 245 GetNextWindow 246 GetParent 246 GetTopWindow 247 GetWindow 247 GetWindowPlacement 248 GetWindowRect 249 GetWindowText 249 IsChild 250 GetWindowTextLength 251 GetWlndowThreadprocessld 251 IsIconic 252 IsWindow 252 IsWindowUnicode 253 IsWindowVlslble 253 IsZoomed 254 MoveWindow 254 Openlcon 255 SetForegroundWindow 255 SetParent 256 SetWindowLong 256 SetWindowPlacement 259 SetWindowPos 260 SetWindowText 263 ShowOwnedPopups 263 ShowWindow 264 ShowWindowAsync 265 TileWindows 266 WindowFromPoint 267 WinMain 267 AnyPopup 269 EnumTaskWindows 269 GetSysModalWindow 269 GetWindowTask 269 SetSysModalWindow 269 第十九节 窗口类函数(Window Class) 270 GetClasslnfoEx 270 GetClassLong 270 GetClassName 271 GetWindowLong 272 RegisterClassEx 273 SetClassLong 274 SetWindowLong 275 GetClasslnfoEx 278 GetClassWord 279 GetWindowWord 279 RegisterClass 279 SetClassWord 280 SetWindowWord 281 第二十节 窗口过程函数(Window Procedure) 281 CallWindowProc 281 DefWindowProc 282 WindowProc 283 第二十一节 窗口属性函数(Window Property) 284 EnumProps 284 EnumPropsEx 284 GetProp 285 PropEnumProcEx 285 RemoveProp 286 SetProp 287
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值