設計模式大類--結構模式(下)

5、Decorate(裝飾者)
描述:動態的給一個對象添加額外的職責,比繼承達到更好的靈活性
好處:某些功能須要用戶動態決定加入方式和時機,裝飾者提供即插即用的模型框架

例子:
舉Adapter中的打樁示例,在Adapter中有兩種類:方形樁 圓形樁,Adapter模式展現如何綜合使用這兩個類,在Decorator模式中,咱們是要在打樁時增長一些額外功能,好比,挖坑 在樁上釘木板等,不關心如何使用兩個不相關的類
咱們先創建一個接口:
public interface Work
{
  public void insert();ide

}
this

接口Work有一個具體實現:插入方形樁或圓形樁,這兩個區別對Decorator是無所謂.咱們以插入方形樁爲例:
public class SquarePeg implements Work{
  public void insert(){
    System.out.println("方形樁插入");
  } 設計

}
xml

如今有一個應用:須要在樁打入前,挖坑,在打入後,在樁上釘木板,這些額外的功能是動態,可能隨意增長調整修改,好比,可能又須要在打樁以後釘架子(只是比喻).
那麼咱們使用Decorator模式,這裏方形樁SquarePeg是decoratee(被裝飾者),咱們須要在decoratee上加些"裝飾",這些裝飾就是那些額外的功能.
public class Decorator implements Work{對象

  private Work work;
  //額外增長的功能被打包在這個List中
  private ArrayList others = new ArrayList(); 繼承

  //在構造器中使用組合new方式,引入Work對象;
  public Decorator(Work work)
  {
    this.work=work;
  
    others.add("挖坑");接口

    others.add("釘木板");
  }內存

  public void insert(){字符串

    newMethod();
  }


  
  //在新方法中,咱們在insert以前增長其餘方法,這裏次序前後是用戶靈活指定的   
  public void newMethod()
  {
    otherMethod();
    work.insert();


  }

  public void otherMethod()
  {
    ListIterator listIterator = others.listIterator();
    while (listIterator.hasNext())
    {
      System.out.println(((String)(listIterator.next())) + " 正在進行");
    }

  }

}

在上例中,咱們把挖坑和釘木板都排在了打樁insert前面,這裏只是舉例說明額外功能次序能夠任意安排.

好了,Decorator模式出來了,咱們看如何調用:

Work squarePeg = new SquarePeg();
Work decorator = new Decorator(squarePeg);
decorator.insert();

Decorator模式至此完成.

若是你細心,會發現,上面調用相似咱們讀取文件時的調用:

FileReader fr = new FileReader(filename);
BufferedReader br = new BufferedReader(fr);

實際上Java 的I/O API就是使用Decorator實現的,I/O變種不少,若是都採起繼承方法,將會產生不少子類,顯然至關繁瑣.

6、Bridege(橋接)
描述:將抽象和行爲劃分開,各自獨立,但能動態結合
好處:多個concrete class之間有概念上重疊.那麼須要咱們把抽象共同部分和行爲共同部分各自獨立開來,原來是準備放在一個接口裏,如今須要設計兩個接口,分別放置抽象和行爲

例如,一杯咖啡爲例,有中杯和大杯之分,同時還有加奶 不加奶之分. 若是用單純的繼承,這四個具體實現(中杯 大杯 加奶 不加奶)之間有概念重疊,由於有中杯加奶,也有中杯不加奶, 若是再在中杯這一層再實現兩個繼承,很顯然混亂,擴展性極差.那咱們使用Bridge模式來實現它.

如何實現?
以上面提到的咖啡 爲例. 咱們原來打算只設計一個接口(抽象類),使用Bridge模式後,咱們須要將抽象和行爲分開,加奶和不加奶屬於行爲,咱們將它們抽象成一個專門的行爲接口.

先看看抽象部分的接口代碼:

public abstract class Coffee
{
  CoffeeImp coffeeImp;

  public void setCoffeeImp() {
    this.CoffeeImp = CoffeeImpSingleton.getTheCoffeImp();
  }

  public CoffeeImp getCoffeeImp() {return this.CoffeeImp;}

  public abstract void pourCoffee();
}

其中CoffeeImp 是加不加奶的行爲接口,看其代碼以下:

public abstract class CoffeeImp
{
  public abstract void pourCoffeeImp();
}

如今咱們有了兩個抽象類,下面咱們分別對其進行繼承,實現concrete class:

//中杯
public class MediumCoffee extends Coffee
{
  public MediumCoffee() {setCoffeeImp();}

  public void pourCoffee()
  {
    CoffeeImp coffeeImp = this.getCoffeeImp();
    //咱們以重複次數來講明是衝中杯仍是大杯 ,重複2次是中杯
    for (int i = 0; i < 2; i++)
    {

      coffeeImp.pourCoffeeImp();
    }
  
  }
}

//大杯
public class SuperSizeCoffee extends Coffee
{
  public SuperSizeCoffee() {setCoffeeImp();}

  public void pourCoffee()
  {
    CoffeeImp coffeeImp = this.getCoffeeImp();
    //咱們以重複次數來講明是衝中杯仍是大杯 ,重複5次是大杯
    for (int i = 0; i < 5; i++)
    {

      coffeeImp.pourCoffeeImp();
    }
  
  }
}

上面分別是中杯和大杯的具體實現.下面再對行爲CoffeeImp進行繼承:

//加奶
public class MilkCoffeeImp extends CoffeeImp
{
  MilkCoffeeImp() {}

  public void pourCoffeeImp()
  {
    System.out.println("加了美味的牛奶");
  }
}

//不加奶
public class FragrantCoffeeImp extends CoffeeImp
{
  FragrantCoffeeImp() {}

  public void pourCoffeeImp()
  {
    System.out.println("什麼也沒加,清香");
  }
}

Bridge模式的基本框架咱們已經搭好了,別忘記定義中還有一句:動態結合,咱們如今能夠喝到至少四種咖啡:
1.中杯加奶
2.中杯不加奶
3.大杯加奶
4.大杯不加奶

看看是如何動態結合的,在使用以前,咱們作個準備工做,設計一個單態類(Singleton)用來hold當前的CoffeeImp:

public class CoffeeImpSingleton
{
  private static CoffeeImp coffeeImp;

  public CoffeeImpSingleton(CoffeeImp coffeeImpIn)
   {this.coffeeImp = coffeeImpIn;}

  public static CoffeeImp getTheCoffeeImp()
  {
    return coffeeImp;
  }
}

看看中杯加奶 和大杯加奶 是怎麼出來的:

//拿出牛奶
CoffeeImpSingleton coffeeImpSingleton = new CoffeeImpSingleton(new MilkCoffeeImp());

//中杯加奶
MediumCoffee mediumCoffee = new MediumCoffee();
mediumCoffee.pourCoffee();

//大杯加奶
SuperSizeCoffee superSizeCoffee = new SuperSizeCoffee();
superSizeCoffee.pourCoffee();


7、Flyweight(享元)
描述:運用共享技術有效的支持大量細粒度的對象
好處:避免大量擁有相同內容的小類的開銷(如耗費內存),使你們共享一個類(元類).

當大量從數據源中讀取字符串,其中確定有重複的,那麼咱們使用Flyweight模式能夠提升效率,以唱片CD爲例,在一個XML文件中,存放了多個CD的資料.

每一個CD有三個字段:
1.出片日期(year)
2.歌唱者姓名等信息(artist)
3.唱片曲目 (title)

其中,歌唱者姓名有可能重複,也就是說,可能有同一個演唱者的多個不一樣時期 不一樣曲目的CD.咱們將"歌唱者姓名"做爲可共享的ConcreteFlyweight.其餘兩個字段做爲UnsharedConcreteFlyweight.

首先看看數據源XML文件的內容:


<?xml version="1.0"?>
<collection>

<cd>
<title>Another Green World</title>
<year>1978</year>
<artist>Eno, Brian</artist>
</cd>

<cd>
<title>Greatest Hits</title>
<year>1950</year>
<artist>Holiday, Billie</artist>
</cd>

<cd>
<title>Taking Tiger Mountain (by strategy)</title>
<year>1977</year>
<artist>Eno, Brian</artist>
</cd>

.......

</collection>


雖然上面舉例CD只有3張,CD可當作是大量重複的小類,由於其中成分只有三個字段,並且有重複的(歌唱者姓名).

CD就是相似上面接口 Flyweight:


public class CD {

  private String title;
  private int year;
  private Artist artist;

  public String getTitle() {  return title; }
  public int getYear() {    return year;  }
  public Artist getArtist() {    return artist;  }

  public void setTitle(String t){    title = t;}
  public void setYear(int y){year = y;}
  public void setArtist(Artist a){artist = a;}

}

將"歌唱者姓名"做爲可共享的ConcreteFlyweight:

public class Artist {

  //內部狀態
  private String name;

  // note that Artist is immutable.
  String getName(){return name;}

  Artist(String n){
    name = n;
  }

}

再看看Flyweight factory,專門用來製造上面的可共享的ConcreteFlyweight:Artist

public class ArtistFactory {

  Hashtable pool = new Hashtable();

  Artist getArtist(String key){

    Artist result;
    result = (Artist)pool.get(key);
    ////產生新的Artist
    if(result == null) {
      result = new Artist(key);
      pool.put(key,result);
      
    }
    return result;
  }

}

當你有幾千張甚至更多CD時,Flyweight模式將節省更多空間,共享的flyweight越多,空間節省也就越大

相關文章
相關標籤/搜索