Java8中的設計模式(一)

之前在infoq上看到一篇文章:
swift版本原文參考:
http://www.infoq.com/cn/articles/design-patterns-in-swift
于是想著把這篇文章修改為Java8的版本,本文是對原文的Java8版本的修改,所以大部分文章和示例都是采用原文的敘述;再次表達對原文作者的感謝;

設計模式##

設計模式(Design Pattern)是對軟件設計中普遍存在的各種問題,所提出的解決方案。這個術語是由埃里?!べが?shù)热耍‥rich Gamma,Richard Helm,Ralph Johnson和John Vlissides這四人提出的。也被稱為:Gang of Four,GOF,四人幫)在1990年代從建筑設計領域引入到計算機科學的。 設計模式并不能直接用于完成代碼的編寫,而是描述在各種不同情況下,要怎么解決問題的一種方案。
以上描述摘自維基百科。
隨著我們所使用的編程語言的演化,我們遇到的問題也確實一直在改變。GOF提出的23種設計模式也許部分過時了。但我們遇到的問題并不會消失,設計模式的概念將一直存在:提煉普遍存在的問題,提出解決方案。這其實是一個抽象過程?,F(xiàn)在已有的編程語言都存在表達的局限,即對某類問題抽象層次過低。所以,我們在使用任何編程語言時,都還是會遇到一些普遍存在的卻沒有被語言本身很好解決的問題。這時,我們就會使用到“設計模式”,即人們總結出來的解決方案:遇到問題A,用方案A;遇到問題B,用方案B。只不過問題會一直變化?,F(xiàn)在我們不可以再使用那23個模式來解決問題了,但是我們仍然需要總結出其它模式來解決新的問題。這種情況一直會持續(xù)到我們擁有“完美語言”的那一天。但現(xiàn)在看起來,這一天還沒有到來的跡象。

Java8中的設計模式##

Java8中提出了函數(shù)式編程等新的概念,使得Java8在面對傳統(tǒng)的GOF23模式的時候,解決問題的方式已經得到了部分的改善,之前的一些設計模式在Java8面前就已經不再是問題了。但是Java8提出了函數(shù)式編程的同時,因為涉及到向后兼容的問題,同樣在Java8中可能面對新的代碼設計問題,比如接口中的默認方法帶來的諸多設計細節(jié),同樣也是會引人深思的;
下面主要討論的是,在Java8中,傳統(tǒng)的哪些開發(fā)模式被消除了或者以一種更簡單的方式簡化了。

一,命令模式##

命令模式使用對象封裝一系列操作(命令),使得操作可以重復使用,也易于在對象間傳遞。首先來一個傳統(tǒng)Java方式實現(xiàn)的命令模式代碼。

public class Light {
  public void on() {
    System.out.println("light turn on");
  }
  public void off() {
    System.out.println("light turn off");
  }
}

interface Command {
  void execute();
}

class FilpUpCommand implements Command {
   private Light light;
   public FilpUpCommand(Light light) {
    this.light = light;
  }
  public void execute() {
    light.on();
  }
}

class FilpDownCommand implements Command {
  private Light light;
  public FilpDownCommand(Light light) {
    this.light = light;
  }
  public void execute() {
    light.off();
  }
}

以上代碼中,燈(Light)是命令(Command)的操作對象(Receiver)。我們定義了命令的協(xié)議,同時我們實現(xiàn)兩個具體的命令操作:FlipUpCommand和FlipDownCommand。它們分別使燈亮,和使燈滅。

class LightSwitch{
  private List<Command> queue=new ArrayList<>();

  public void addCommand(Command cmd){
    queue.add(cmd);
  }

  public void execute(){
    for(Command cmd:queue){
        cmd.execute();
    }
  }
}

class Client{

  public static void pressSwitch(){
    Light lamp=new Light();
    Command flipUpCommand=new FilpUpCommand(lamp);
    Command flipDowomnCmand=new FilpDownCommand(lamp);
    
    LightSwitch lightSwitch = new LightSwitch();
    lightSwitch.addCommand(flipUpCommand);
    lightSwitch.addCommand(flipDowomnCmand);
    lightSwitch.addCommand(flipUpCommand);
    lightSwitch.addCommand(flipDowomnCmand);
    
    lightSwitch.execute();
  }
}

上面的代碼首先創(chuàng)建了一個命令執(zhí)行者LightSwitch,并創(chuàng)建一個客戶對象來使用命令;
在函數(shù)式編程中,由于存在高階函數(shù)。我們可以直接將一個函數(shù)作為參數(shù)傳給另外一個函數(shù)。所以,使用類包裹函數(shù)在對象間傳遞這件事情就顯得多余了。以下代碼顯示如何使用高階函數(shù)達到命令模式相同的效果:

class LightSwitchFP {
  private List<Consumer<Light>> queue = new ArrayList<>();
  public void addCommand(Consumer<Light> cmd) {
    queue.add(cmd);
  }
  public void execute(Light light) {
    for (Consumer<Light> cunsumer : queue) {
        cunsumer.accept(light);
    }
  }
}

class Client {
  public static void pressSwitch() {
    Light lamp = new Light();

    Consumer<Light> flipUp = light -> {light.on();};
    Consumer<Light> flipDown = light -> {light.off();};
    LightSwitchFP lightSwitch = new LightSwitchFP();
    lightSwitch.addCommand(flipUp);
    lightSwitch.addCommand(flipDown);
    lightSwitch.addCommand(flipUp);
    lightSwitch.addCommand(flipDown);

    lightSwitch.execute(lamp);
  }
}

在Java8中,首先我們直接使用Java8提供的Consumer函數(shù)接口作為我們的命令接口,因為有了lambda表達式,我們根本無需在單獨為具體命令對象創(chuàng)建類型,而通過傳入labmda表達式來完成具體命令對象的創(chuàng)建;

二,策略模式##

策略模式定義了一系列算法,將每個算法封裝起來,并且使它們之間可以互相替換。此模式讓算法的變化獨立于使用算法的客戶。
下面簡單演示一個傳統(tǒng)的策略模式實現(xiàn)方案:

interface Strategy {  
    public Integer compute(Integer op1, Integer op2);
}

class Add implements Strategy {
    public Integer compute(Integer op1, Integer op2) {
     return op1 + op2;
    }
}
class Multiply implements Strategy {
    public Integer compute(Integer op1, Integer op2) {
        return op1 * op2;
    }
}
class Context {
    private Strategy strategy;
    public Context(Strategy strategy) {
        this.strategy = strategy;
    }
    public void use(Integer first, Integer second) {
        System.out.println(this.strategy.compute(first, second));
    }
}                     

類似于命令模式,策略模式中的策略對象主要用于封裝操作(函數(shù)),不同的是策略模式中的策略對象封裝的是不同的算法。這些算法實現(xiàn)了相同的接口,在這個例子中,接口是用Strategy協(xié)議表示的。我們使用兩個實現(xiàn)了Strategy協(xié)議的具體類:Add和Multiply分別封裝兩個簡單的算法。Context對象,用于對算法進行配置選擇,它有一個Strategy類型的實例變量:strategy。通過配置Context的strategy具體類型,可以使用不同的算法。

然后我們再看看怎么簡化策略模式:

public static final BinaryOperator<Integer> add = (op1, op2) -> op1 + op2;
public static final BinaryOperator<Integer> multiply = (op1, op2) -> op1 * op2;

class ContextFP {
    private BinaryOperator<Integer> strategy;

    public ContextFP(BinaryOperator<Integer> strategy) {
        this.strategy = strategy;
    }

    public void use(Integer first, Integer second) {
        System.out.println(strategy.apply(first, second));
    }
}

public static void main(String[] args) {
    StraFP fp = new StraFP();
    ContextFP ctx = fp.new ContextFP(StraFP.add);
    ctx.use(1, 2);
}

在Java8中,我們很自然的使用內建的function interface作為封裝算法的載體,這樣更為直接自然。例子中,ContextFP的構造器的傳參就是函數(shù)類型。給予構造器代表不同算法的函數(shù),就配置了不同的算法。

函數(shù)也可以作為類的實例變量。這樣在類中,直接維護代表算法的函數(shù)也成為可能。從類型聲明可以看出,ContextFP中的實例變量strategy就是一個函數(shù)。

一等函數(shù)的概念使得函數(shù)獲得了更高的地位,使得函數(shù)的靈活性大大增加。在很多場景下直接使用函數(shù)會是更直接自然的選擇。面向對象編程范式,賦予了對象更高的地位。但是,如果給予函數(shù)“正?!币恍┑牡匚?,可以簡化不少問題。設計模式中的不少模式存在都是由于函數(shù)的使用限制,需要使用在使用類包裹函數(shù)。類似的例子還有模版方法模式(Template method)。

上面代碼示例不能對策略做很好的封裝,下面提供了一個枚舉的版本:

public enum StrategyEnum {
    ADD(() -> (x, y) -> x + y), 
    MULTIPLY(() -> (x, y) -> x * y);

    private Supplier<BinaryOperator<Integer>> operation;

    private StrategyEnum(Supplier<BinaryOperator<Integer>> operation) {
        this.operation = operation;
    }

    public BinaryOperator<Integer> get() {
        return operation.get();
    }
}

class ContextFP {
    private StrategyEnum strategy;

    public ContextFP(StrategyEnum strategy) {
        this.strategy = strategy;
    }

    public void use(Integer first, Integer second) {
        System.out.println(strategy.get().apply(first, second));
    }
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容