java編程思想第四版第九章總結
1. 策略設計模式
- 參考這篇文章:http://blog.csdn.net/chenjie19891104/article/details/6396458 講的很清楚,策略設計模式。並且舉了一個例子,很具有代表性。
- 先簡單了解一下:
和模板方法模式的區別:
文章裏還有一個例子:
備註:我來分解,解釋一下這個例子。
將共同的方法定義成了一個接口,在這個接口中並沒有這個共同方法的實現。
在Strategy類中,定義了一個方法execute,它的參數是擁有共同方法的接口類。
用戶Context在調用Strategy的execute方法時,在定義這個共同的方法。 這樣就大大提高了靈活性。因為公共方法也是可以後定義的。而不是在創建類的時候就定義好了。
下面把這個例子敲成代碼
package net.mindview.interfaces; //公共方法回調類 interface SameCallback{ void doTheSame(); } //定義了一個策略類 interface Strategy { void execute(SameCallback sc); } //定義策略實現類 class concreteStrategy1 implements Strategy { @Override public void execute(SameCallback sc) { sc.doTheSame(); } }class concreteStrategy2 implements Strategy { @Override public void execute(SameCallback sc) { sc.doTheSame(); } } class concreteStrategy3 implements Strategy { @Override public void execute(SameCallback sc) { sc.doTheSame(); } } public class Context {public static void main(String[] args) { Strategy strategy = new concreteStrategy1(); //這個公共方法類 SameCallback sc = new SameCallback() { @Override public void doTheSame() { System.out.println("do same things"); } }; //那個策略需要用到公共方法類,調用即可,如果不用, 那就不在方法中小勇 strategy.execute(sc); strategy = new concreteStrategy2(); strategy.execute(sc); } }
思考: 如果一段代碼中, 總會出現很多的if...else或者case,就要考慮使用策略設計模式了
2. 解耦
- 看下面這兩個例子:
案例一
package net.mindview.interfaces.classprocessor; import java.math.BigInteger; import java.util.Arrays; /** * 處理器 */ class Processor { public String name(){ return getClass().getName(); }; //處理 Object process(Object input) { return input; } } /** * 大寫處理器 */ class UpCase extends Processor { @Override public Object process(Object input) { return ((String)input).toUpperCase(); } } /** * 小寫處理器 */ class DownCase extends Processor { @Override public Object process(Object input) { return ((String)input).toLowerCase(); } } /** * 分割數組處理器 */ class Splitter extends Processor { @Override public Object process(Object input) { return Arrays.toString(((String)input).toString().split(" ")); } } public class Apply { public static void process(Processor p, Object s){ System.out.println("Using Processor "+ p.name()); System.out.println(p.process(s)); } public static String s = "Disagreement with beliefs is by definition incorrect"; /** * 向本例這樣, 創建一個能夠根據所傳遞的參數對象的不同而具有不同行為的方法, * 被稱為策略設計模式 * @param args */ public static void main(String[] args) { BigInteger b = new BigInteger("111111111111111"); process(new UpCase(), s); process(new DownCase(), s); process(new Splitter(), s); } /** * 這個類使用到了一個設計模式: 叫做策略設計模式 * 所謂 的策略設計模式指的是: 能夠根據傳遞參數的不同而具有不同的行為的方法, 被稱為策略設計模式 * 這類方法包含了所有執行的算法中固定不變的部分(這裏是基類Processor,Object),而策略就是實際調用該方法時傳遞的實際參數, * 這部分參數是變化的,因此被稱之為是"策略"部分. * 在main中看到了三種不同類型的策略應用到了String類型s對象上。 */ }在這段代碼中,定義了一個處理器,這個處理器有兩個方法name()和process(). 然後定義了三個類, UpCase, DownCase,Spltter繼承自這個類. 當我們在Apply類中定義process(Process process, Object o)方法, 方法傳遞的參數是父類Process. 這樣不僅可以處理父類,還可以處理子類. 在main方法中就有體現. 這就是策略設計模式的思想。但是,他還有局限性。如果在main中想調用Apply.process()方法, 則必須傳遞Process類或者子類,不能傳遞其他類。 下面這個案例濾波器就說明了這一點:
案例二
package net.mindview.interfaces.filters; //波形 public class Waveform { //計數器,第幾波 private static long counter; private final long id = counter ++; //當前第幾波 @Override public String toString() { return "Waveform " + id; } } package net.mindview.interfaces.filters; //濾波器 public class Filter { public String name(){ return getClass().getSimpleName(); } //加工 public Waveform process(Waveform input){ return input; } } package net.mindview.interfaces.filters; /** * 低頻電磁波 */ public class LowPass extends Filter{ //切波的波長 double cutoff; public LowPass(double cutoff){ this.cutoff = cutoff; } //加工 @Override public Waveform process(Waveform input) { return input; } } package net.mindview.interfaces.filters; /** * 高頻電磁波 */ public class HighPass extends Filter { //切波 double cutoff; public HighPass(double cutoff){ this.cutoff = cutoff; } //加工 @Override public Waveform process(Waveform input) { return input; } } package net.mindview.interfaces.filters; /** * 段波電磁波 */ class BandPass extends Filter { //其實切波點, 最高且波點 double cutoff ,highCutoff; public BandPass(double cutoff, double highCutoff){ this.cutoff = cutoff; this.highCutoff = highCutoff; } //加工 @Override public Waveform process(Waveform input) { return input; } }在上面這個類中,我們定義了一個Filter濾波器。這裏面有何Process中同名的兩個方法。這個時候,我想復用案例一的Apply類中的process()方法,可不可以呢?不可以。因為Filter不是Process的子類。雖然, process中的方法體調用完全適用於繼承Filter類的子類。為什麽不可以呢?Apply就是一個應用類。應用類要處理一些列的類。他可以堆字符串進行處理, 應該也可以對波進行處理呀。現在不可以,因為Process類是一個實體類,他和Apply類中的process方法緊密耦合。如果能夠實現解耦,那麽,就可以將Apply類更加泛化。這就是將Process作為接口來處理
案例三
package net.mindview.interfaces.classprocessor2; import java.math.BigInteger; import java.util.Arrays; /** * 處理器 */ interface Processor { public String name(); //處理 Object process(Object input); } /** * 處理器有一部分是所有處理器都是一樣的.有一部分是不同的. * 所以定義一個抽象類, 將共有方法定義為實體方法, 將各自實現的部分定義為抽象的 */ abstract class StringProcess implements Processor { @Override public String name() { return getClass().getSimpleName(); } public abstract Object process(Object input); } /** * 大寫處理器 */ class UpCase extends StringProcess { @Override public Object process(Object input) { return ((String)input).toUpperCase(); } } /** * 小寫處理器 */ class DownCase extends StringProcess { @Override public Object process(Object input) { return ((String)input).toLowerCase(); } } /** * 分割數組處理器 */ class Splitter extends StringProcess { @Override public Object process(Object input) { return Arrays.toString(((String)input).toString().split(" ")); } } public class Apply { public static void process(Processor p, Object s){ System.out.println("Using Processor "+ p.name()); System.out.println(p.process(s)); } public static String s = "Disagreement with beliefs is by definition incorrect"; /** * 向本例這樣, 創建一個能夠根據所傳遞的參數對象的不同而具有不同行為的方法, * 被稱為策略設計模式 * @param args */ public static void main(String[] args) { BigInteger b = new BigInteger("111111111111111"); process(new UpCase(), s); process(new DownCase(), s); process(new Splitter(), s); } /** * 這個類使用到了一個設計模式: 叫做策略設計模式 * 所謂 的策略設計模式指的是: 能夠根據傳遞參數的不同而具有不同的行為的方法, 被稱為策略設計模式 * 這類方法包含了所有執行的算法中固定不變的部分(這裏是基類Processor,Object),而策略就是實際調用該方法時傳遞的實際參數, * 這部分參數是變化的,因此被稱之為是"策略"部分. * 在main中看到了三種不同類型的策略應用到了String類型s對象上。 */ }在這個類裏面,將Process定義為了接口。並且添加了StringProcess 字符串處理器類,提供了字符串處理的公用方法。這時如何修改案例二,才能讓Apply成為一個公用類呢?其中一個方法是讓Filter實現Process接口。但為題又來了,Filter中的process的輸入輸出參數都是Waveform類型的對象。而Process中process的輸入輸出都是Object。這時我們使用一個適配器來解決這個問題
package net.mindview.interfaces.filters2; import net.mindview.interfaces.classprocessor2.Processor; /** * 由於Filter中的process的輸入輸出參數都是Waveform,而Processor中process的輸入 * 輸出參數都是Object. 因此,寫一個適配器,讓Filter可以適配Processor類型的接口 * */ public class FilterAdapter implements Processor{ Filter filter; public FilterAdapter(Filter filter){ this.filter = filter; } @Override public String name() { return filter.name(); } @Override public Object process(Object input) { return filter.process((Waveform)input); } }
接下來看看Apply類如何處理的?
public class Apply { public static void process(Processor p, Object s){ System.out.println("Using Processor "+ p.name()); System.out.println(p.process(s)); } public static String s = "Disagreement with beliefs is by definition incorrect"; /** * 向本例這樣, 創建一個能夠根據所傳遞的參數對象的不同而具有不同行為的方法, * 被稱為策略設計模式 * @param args */ public static void main(String[] args) { System.out.println("======處理字符串==========="); process(new UpCase(), s); process(new DownCase(), s); process(new Splitter(), s); System.out.println("======過濾波==========="); process(new FilterAdapter(new LowPass(1.0)), new Waveform()); process(new FilterAdapter(new HighPass(100.0)), new Waveform()); process(new FilterAdapter(new BandPass(1.0,10.0)), new Waveform()); } }
上面這個例子就示範了使用接口的好處,讓類和方法解耦。同時還涉及到一個設計模式,叫適配器設計模式。適配器設計模式有三種, 確切的說這是對象適配器模式.
3. 適配器設計模式
參考文章:http://blog.csdn.net/zxt0601/article/details/52848004
定義:適配器模式將某個類的接口轉換成客戶端期望的另一個接口表示,主的目的是兼容性,讓原本因接口不匹配不能一起工作的兩個類可以協同工作。其別名為包裝器(Wrapper)。
屬於結構型模式
主要分為三類:類適配器模式、對象的適配器模式、接口的適配器模式。
本文做以下約定
-
- 需要被適配的類,對象,接口被定義為源(目前已有的),簡稱為src(source).
- 最終需要輸出的(我們需要的),簡稱dst(destination,也叫target)
- 適配器Adapter
一句話描述適配器模式:src->adapter->dist,即src以某種形式(類,對象,接口)給到適配器,適配器最終輸出dist。
使用 場景
-
-
想要使用一個已經存在的類,但如果它的方法不滿足需求時;
-
兩個類的職責相同或相似,但是具有不同的接口時要使用它;
-
應該在雙方都不太容易修改的時候再使用適配器模式適配,而不是一有不同時就使用它
-
第一類: 類適配器模式
一句話描述使用方法:Adapter類,通過繼承src類,實現dist接口,完成src -> dist的適配.
以充電器為例:充電器本身就是一個適配器。輸入的時220v電壓, 輸出的是5v電壓。
首先, 有一個src類,本身是輸出220v電壓
package net.mindview.interfaces.adaptor; /** * 這是目前有的電壓 200v * @author samsung * */ public class Voltage220 { public int output(){ int i = 220; System.out.println("這是220v電壓"); return i; } }
然後, 有一個目標使用電壓是5v
package net.mindview.interfaces.adaptor; /** * 我們需要使用的目標電壓是5v * 這是一個5v電壓的接口 * @author samsung * */ public interface Voltage5 { public int output(); }
定義一個適配器, 將220v電壓經過處理後轉換為5v電壓供用戶使用
package net.mindview.interfaces.adaptor; /** * 適配器,將220v電壓輸出為5v電壓 * 適配器 繼承src類, 實現dist類,實現從src->dist的轉換 */ public class VoltageAdapter extends Voltage220 implements Voltage5{ @Override public int output() { int src = super.output(); System.out.println("適配器工作開始適配電壓"); int dist = src/44; System.out.println("適配完成後輸出電壓:" + dist); return dist; } }
有一款手機需要充電,充電電壓是5v
package net.mindview.interfaces.adaptor; public class Mobile { public void chargeing(Voltage5 v5){ if(v5.output() == 5){ System.out.println("正常充電"); } else { System.out.println("秒變note7"); } } }
使用手機的時候,需要將家裏的220v電壓轉換為5v輸出.調用適配器
public static void main(String[] args) { Mobile m = new Mobile(); m.chargeing(new VoltageAdapter()); }
輸出:
===============類適配器============== 我是220V 適配器工作開始適配電壓 適配完成後輸出電壓:5 電壓剛剛好5V,開始充電
小結:
Java這種單繼承的機制,所有需要繼承的我個人都不太喜歡。
所以類適配器需要繼承src類這一點算是一個缺點,
因為這要求dst必須是接口,有一定局限性;
且src類的方法在Adapter中都會暴露出來,也增加了使用的成本。
但同樣由於其繼承了src類,所以它可以根據需求重寫src類的方法,使得Adapter的靈活性增強了。
第二類: 對象適配器模式
基本思路和類適配器一樣, 只是將adapter類做了修改, 這次不繼承src類,而是持有src類實例對象,以解決兼容性問題。
一句話總結使用方法:持有src類,實現dist類接口, 實現src -> dist的適配
(根據"合成復用規則", 在系統中盡量使用組合來代替繼承)
package net.mindview.interfaces.adaptor; /** * 持有src類,實現dist類接口, 實現src -> dist的適配 * @author samsung * */ public class VoltageAdapter2 implements Voltage5{ Voltage220 voltage220; public VoltageAdapter2(Voltage220 voltage220){ this.voltage220 = voltage220; } @Override public int output() { System.out.println("得到220v電壓"); int src = voltage220.output(); System.out.println("經過處理,輸出5v電壓"); int dist = src/44; System.out.println("最終使用的電壓是"+dist+"v"); return dist; } }
測試結果:
得到220v電壓
這是220v電壓
經過處理,輸出5v電壓
最終使用的電壓是5v
正常充電
小結:
對象適配器和類適配器其實算是同一種思想,只不過實現方式不同。
根據合成復用原則,組合大於繼承,
所以它解決了類適配器必須繼承src的局限性問題,也不再強求dst必須是接口。
同樣的它使用成本更低,更靈活。
第三類 接口適配器模式
也有文獻稱之為認適配器模式(Default Adapter Pattern)或缺省適配器模式。
定義:
當不需要全部實現接口提供的方法時,可先設計一個抽象類實現接口,並為該接口中每個方法提供一個默認實現(空方法),那麽該抽象類的子類可有選擇地覆蓋父類的某些方法來實現需求,它適用於一個接口不想使用其所有的方法的情況。
我們直接進入大家最喜愛的源碼撐腰環節:
源碼撐腰環節:
Android中的屬性動畫ValueAnimator
類可以通過addListener(AnimatorListener listener)
方法添加監聽器,
那麽常規寫法如下:
ValueAnimator valueAnimator = ValueAnimator.ofInt(0,100); valueAnimator.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { } @Override public void onAnimationEnd(Animator animation) { } @Override public void onAnimationCancel(Animator animation) { } @Override public void onAnimationRepeat(Animator animation) { } }); valueAnimator.start();
有時候我們不想實現Animator.AnimatorListener
接口的全部方法,我們只想監聽onAnimationStart
,我們會如下寫:
ValueAnimator valueAnimator = ValueAnimator.ofInt(0,100); valueAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationStart(Animator animation) { //xxxx具體實現 } }); valueAnimator.start();
顯然,這個AnimatorListenerAdapter
類,就是一個接口適配器。
查看該Adapter類源碼:
public abstract class AnimatorListenerAdapter implements Animator.AnimatorListener, Animator.AnimatorPauseListener { @Override public void onAnimationCancel(Animator animation) { } @Override public void onAnimationEnd(Animator animation) { } @Override public void onAnimationRepeat(Animator animation) { } @Override public void onAnimationStart(Animator animation) { } @Override public void onAnimationPause(Animator animation) { } @Override public void onAnimationResume(Animator animation) { } }
可見,它空實現了Animator.AnimatorListener
類(src)的所有方法.
對應的src類:
public static interface AnimatorListener { void onAnimationStart(Animator animation); void onAnimationEnd(Animator animation); void onAnimationCancel(Animator animation); void onAnimationRepeat(Animator animation); }
類圖:
我們程序裏的匿名內部類就是Listener1 2 這種具體實現類。
new AnimatorListenerAdapter() { @Override public void onAnimationStart(Animator animation) { //xxxx具體實現 } }
接口適配器模式很好理解,令我們的程序更加簡潔明了。
總結
我個人理解,三種命名方式,是根據 src是以怎樣的形式給到Adapter(在Adapter裏的形式)來命名的。
類適配器,以類給到,在Adapter裏,就是將src當做類,繼承,
對象適配器,以對象給到,在Adapter裏,將src作為一個對象,持有。
接口適配器,以接口給到,在Adapter裏,將src作為一個接口,實現。
Adapter模式最大的作用還是將原本不兼容的接口融合在一起工作。
但是在實際開發中,實現起來不拘泥於本文介紹的三種經典形式,
例如Android中ListView、GridView的適配器Adapter,就不是以上三種經典形式之一,
我個人理解其屬於對象適配器模式,一般日常使用中,我們都是在Adapter裏持有datas,然後通過getView()/onCreateViewHolder()方法向ListView/RecyclerView提供View/ViewHolder。
Client是Lv Gv Rv ,它們是顯示View的類。
所以dst(Target)是View。
一般來說我們有的src是數據datas,
即,我們希望:datas(src)->Adapter->View(dst)->Rv(Client)。
java編程思想第四版第九章總結