1. 程式人生 > 實用技巧 >使用SoundTouch演算法庫對聲音進行變速

使用SoundTouch演算法庫對聲音進行變速

目錄

基本介紹

裝飾器模式意圖為一個物件擴充套件新的功能,且不改變原有的結構,裝飾器模式屬於結構型設計模式

一般的,我們為了擴充套件一個類經常使用繼承方式實現,由於繼承為類引入靜態特徵,並且隨著擴充套件功能的增多,子類會很膨脹

使用場景

  • 擴充套件一個類的功能
  • 動態增加功能,動態撤銷

 

假設有一家咖啡公司,姑且咱就叫怪獸咖啡吧,該咖啡公司是以擴充套件速度最快而聞名的咖啡公司(像瑞幸咖啡一樣),但是最近由於擴充套件速度太快,它們想重新設計一套訂單系統,以滿足它們現在的飲料需求。

它們最初始的設計如下:

 

然鵝使用者在購買咖啡時,通常會新增一些調料,例如:摩卡、豆漿、奶泡等等。怪獸咖啡並根據業務去計算相應的總價,這就要求咖啡公司在設計訂單時需要考慮這些調料的部分。然後就設計出這麼一套龐雜的系統,因為奶茶種類太多,調料太多。種類*調料 = 類爆炸,實在太瘋狂了

 

這麼設計的問題顯而易見

  • 調料價格變化時,需要更改現有程式碼
  • 出現新的調料,需要新增新的方法,並改動超類中的cost()方法
  • 以後開發新的飲料,對於這些飲料,某些調料並不適合,但是這個設計中,子類仍需繼承那些不需要的方法

 

裝飾器模式

從上述的設計方案來看,這顯然並不是一個聰明的結果,因為會遇到 類爆炸、設計過於冗餘,以及基類加入的新功能並不一定適用於所有子類

 

所以我們要考慮換一種方式,設想一下 我們能不能 以飲料為紅花,用調料作為 綠葉 裝飾它

比如,客戶想要 摩卡和奶泡 搭配黑莓咖啡(口味獨特),那麼,我們怎麼做呢?

  • 拿黑莓咖啡(DarkRoast)作為物件
  • 用摩卡(Mocha)物件裝飾它
  • 用奶泡(Whip)物件裝飾它
  • 呼叫 cost() 方法,並通過依賴將調料價格和摩卡價格想加

 

 

上圖詳細的介紹了,裝飾模式的總體過程

 

上圖為裝飾器模式的結構類圖以及各個類的作用說明

 

好,下面我們詳細介紹訂單系統的正確設計方案

1、我們先設計飲料元件

/**
 * 建立一個飲料抽象類
 */
public abstract class Beverage {

    String description = "UnKnown Beverage";

    public String getDescription() {
        return description;
    }

    public abstract Double cost();

}

 

2、假設目前店鋪中,有 綜合咖啡、濃縮咖啡、黑莓

public class HouseBlend extends Beverage {

    public HouseBlend() {
        description = "綜合咖啡";
    }

    @Override
    public Double cost() {
        return 23.2;
    }
}
public class Expresso extends Beverage{

    public Expresso(){
        description = "濃縮咖啡";
    }

    @Override
    public Double cost() {
        return 19.9;
    }
}
public class DarkRoast extends Beverage {

    public DarkRoast() {
        description = "黑莓咖啡";
    }

    @Override
    public Double cost() {
        return 21.8;
    }

}

 

3、下面我們實現調料類的抽象類,也就是裝飾者類

/**
 * 調料抽象類
 */
public abstract class Condiment extends Beverage {

    Beverage beverage;

    Condiment(Beverage decoratedBeverage){
        this.beverage = decoratedBeverage;
    }

    public abstract String getDescription();

}

 

4、 然後我們再實現相應的 Mocha、Whip、Soy 的調料程式碼

/**
 * 摩卡
 */
public class Mocha extends Condiment {


    public Mocha(Beverage beverage) {
        super(beverage);
    }

    @Override
    public String getDescription() {
        return beverage.getDescription() + "【摩卡】";
    }

    @Override
    public Double cost() {
        return new BigDecimal(beverage.cost().toString()).add(new BigDecimal(2.2)).doubleValue();
    }
}
/**
 * 豆漿
 */
public class Soy extends Condiment {

    public Soy(Beverage beverage) {
        super(beverage);
    }

    @Override
    public String getDescription(){
        return beverage.getDescription() + "【豆漿】";
    }

    @Override
    public Double cost() {
        return new BigDecimal(beverage.cost().toString()).add(new BigDecimal(3.6)).doubleValue();
    }
}
/**
 *  奶泡
 */
public class Whip extends Condiment {

    public Whip(Beverage beverage){
        super(beverage);
    }

    @Override
    public String getDescription() {
        return beverage.getDescription() + "【奶泡】";
    }

    @Override
    public Double cost() {
        return new BigDecimal(beverage.cost().toString()).add(new BigDecimal(2.7)).doubleValue();
    }
}

 

好了,參考裝飾者模式,訂單模組的基本實現已經基本完成。下面是測試程式碼

public class Client {
    public static void main(String[] args) {
        //一杯DarkRoast 不需要調料
        Beverage darkRoast = new DarkRoast();
        System.out.println(darkRoast.getDescription() + "," + darkRoast.cost());

        //再點一個濃縮咖啡 加 雙倍摩卡 一份奶泡
        Beverage beverage = new Expresso();
        beverage = new Mocha(beverage);
        beverage = new Mocha(beverage);
        beverage = new Whip(beverage);
        System.out.println(beverage.getDescription() + "," + beverage.cost());
    }
}


 

IO體系中的裝飾器


由圖可見,InputStream就是裝飾者模式中的超類(Component),
ByteArrayInputStream,FileInputStream相當於 被裝飾者(ConcreteComponent),這些類都提供了最基本的位元組讀取功能。
而另外一個和這兩個類是同一級的類FilterInputStream 即為 抽象裝飾者(AbstarctDecorator)
BufferedInputStream,DataInputStream,PushbackInputStream(都繼承了FilterInputStream類),它們為 裝飾者(Decorator),在原有基礎功能上都實現了功能的擴充套件和增強。
 
例:用BufferedInputStream 裝飾 FileInputStream,和上面Mocha(摩卡) 裝飾 DarkRoast(黑莓)如出一轍

File file = new File ("hello.txt"); 
FileInputStream in=new FileInputStream(file); 
BufferedInputStream inBuffered=new BufferedInputStream (in);