明輝手游網(wǎng)中心:是一個(gè)免費(fèi)提供流行視頻軟件教程、在線學(xué)習(xí)分享的學(xué)習(xí)平臺(tái)!

Decorator模式中遭遇繼承與聚合

[摘要]一:背景:Decorator   *Decorator 常被翻譯成"裝飾",我覺得翻譯成"油漆工"更形象點(diǎn),油漆工(decorator)是用來刷油漆的,那么被刷油漆的對(duì)象我們稱decoratee.這兩種實(shí)體在Decorator 模式中是必須的。   *Dec...
一:背景:Decorator

  *Decorator 常被翻譯成"裝飾",我覺得翻譯成"油漆工"更形象點(diǎn),油漆工(decorator)是用來刷油漆的,那么被刷油漆的對(duì)象我們稱decoratee.這兩種實(shí)體在Decorator 模式中是必須的。

  *Decorator 定義:

  動(dòng)態(tài)給一個(gè)對(duì)象添加一些額外的職責(zé),就象在墻上刷油漆.使用Decorator 模式相比用生成子類方式達(dá)到功能的擴(kuò)充顯得更為靈活。

  *為什么使用Decorator?

  我們通?梢允褂美^承來實(shí)現(xiàn)功能的拓展,如果這些需要拓展的功能的種類很繁多,那么勢(shì)必生成很多子類,增加系統(tǒng)的復(fù)雜性,同時(shí),使用繼承實(shí)現(xiàn)功能拓展,我們必須可預(yù)見這些拓展功能,這些功能是編譯時(shí)就確定了,是靜態(tài)的。

  使用Decorator 的理由是:這些功能需要由用戶動(dòng)態(tài)決定加入的方式和時(shí)機(jī).Decorator 提供了"即插即用"的方法,在運(yùn)行期間決定何時(shí)增加何種功能。

  *對(duì)于該模式,初步歸納為

  1.基本功能為接口

  2.Decorator參數(shù)為接口本身也為接口以便為下一個(gè)Decorator的參數(shù)

  3.基本功能類實(shí)現(xiàn)接口 并作為Decorator構(gòu)造函數(shù)的參數(shù),以便在此基礎(chǔ)上添加新功能

  4.額外功能由Decorator中的數(shù)據(jù)結(jié)構(gòu)處理
  
  二:?jiǎn)栴}

  這是一段Decorator設(shè)計(jì)模式的實(shí)現(xiàn)例子如下:

  基本功能:Counter類
  需要添加的功能

  1:上限控制

  2:下限控制

  import java.io.*;
  class Counter{
  private int value;
  public Counter(int v){
  System.out.println("init me here in The Counter with value!");
  value=v;
  }

  public Counter(Counter cc){
  System.out.println("init me here in The Counter with class!");
  value=cc.value;
  }

  public int read_value(){
  System.out.println("read me here The value is:"+value);
  System.out.println("read me here in The Counter!");

  return value;
  }

  public void increment(){
  System.out.println("increment me here in The Counter !");
  value++;
  }

  public void decrement(){
  System.out.println("decrement me here in The Counter !");
  value--;
  }
  }

  class Decorator extends Counter
  {
  Counter counter;
  public Decorator(Counter c)
  {
  super(c);
  System.out.println("init me here with class Decorator!");
  } 
  }

  class UpperLimit extends Decorator//上限控制
  {
  public UpperLimit(Counter c)
  {
  super(c);
  counter=c;
  System.out.println("init me here with class UpperLimit!");
  }
  public void increment()
  {
  if(counter.read_value()>20)
  {
   System.out.println("Too High");
  }
  else
  {
   System.out.println("increment me here with class UpperLimit!");
   counter.increment();
  }
  }
  /*public void decrement()
  {
  counter.decrement();
  }
  public int read_value()
  {
  return counter.read_value();
  }*/

  }

  class LowerLimit extends Decorator//下限控制
  {
  public LowerLimit(Counter c)
  {
  super(c);
  counter=c;
  System.out.println("init me here in The Counter with class LowerLimit!");
  }
  public void decrement()
  {
  System.out.println("Class value :"+read_value());
  System.out.println("Dec value :"+counter.read_value());
  if(counter.read_value()<=0)
  {
   System.out.println(counter.read_value());
   System.out.println("Too Low");
  }
  else
  {
   System.out.println("decrement me here in The Counter with class LowerLimit!");
   counter.decrement();
  }
  }
  /*public void increment()
  {
  counter.increment();
  }
  public int read_value()
  {
  return counter.read_value();
  }*/
  }

  class CounterFactory
  {
  public static Counter createCounter(int value,int op)
  {
  switch(op)
  {
   case 1:
   {
    return new Counter(value);
   }
   case 2:
   {
    return new UpperLimit(new Counter(value));
   }
   case 3:
   {
    return new LowerLimit(new Counter(value));
   }
   default:
   {
    return new UpperLimit(new LowerLimit(new Counter(value)));
   }
  }
  }

  }
  class Console
  {
  private static BufferedReader read=new BufferedReader(new InputStreamReader(System.in));

  public static int readInt(String index){
  System.out.println(index);
  try{
   return Integer.parseInt(read.readLine());
  }
  catch(Exception e){
   return 0;
  }
  }
  }

  public class Q1s{
  public static void main(String[] args){
  System.out.println("Counter Type:");
  System.out.println("1: Normal");
  System.out.println("2: Upper Limit");
  System.out.println("3: Lower Limit");
  System.out.println("4: Upper & Lower Limit");
  int option=Console.readInt("Enter Choice:");
  Counter c = CounterFactory.createCounter(6,option);
  int choice=1;
  while(choice!=4){
   System.out.println("1: Increment");
   System.out.println("2: Decrement");
   System.out.println("3: Read Value");
   System.out.println("4: Exit");
   choice=Console.readInt("Enter Choice:");
   switch(choice){
    case 1: c.increment(); break;
    case 2: c.decrement(); break;
    case 3: int v=c.read_value();
    System.out.println("Value="+v);break;
   }
  }
  }
  }

  按如下步驟運(yùn)行出現(xiàn)明顯問題:

  1:選3,"Lower Limit",

  2:選3,"Read Value" 獲得的值是 6
  
  3:選1,"Increment"(此后value值應(yīng)為 7)

  4:選3,"Read Value" 獲得的值是 7(正確)

  5:選2,"Decrement"(此后的value值應(yīng)為 6)

  6:選3,"Read Value" 獲得的值為 7 (問題出現(xiàn)了)

  考察 Upper Limit 時(shí)同樣出現(xiàn)該問題

  三:追究

  從輸出的追蹤語句可以看出在class LowerLimit的decrement方法的開始兩輸出語句具有明顯的指導(dǎo)意義而其中一方法是調(diào)用的來自父類的read_value()而另一個(gè)為聚合對(duì)象的read_value()。在上面步驟的第六步兩句分別輸出

  Class value :7
  Dec value :6

  可見,有兩份value的存在。問題是兩份如何產(chǎn)生?

  繼續(xù)觀察,工廠在開關(guān)語句選中case 3時(shí)調(diào)用的是

  return new LowerLimit(new Counter(value));

  其中新建一匿名對(duì)象為參數(shù),繼續(xù)追蹤該過程,發(fā)現(xiàn)在class Decorator 中該匿名對(duì)象被Decorator的屬性指向,而在指向之前,Decorator還把該匿名對(duì)象傳給其父類,而父類取得該對(duì)象后僅僅是取出該對(duì)象中value的拷貝,由此有兩份value存在:

  1.在Decorator自身中(父類的value)

  2.也在Decorator自身中(成員變量Counter的value屬性)!

  問題漸漸明朗,再次觀察class LowerLimit,它僅僅實(shí)現(xiàn)了decrement()方法,也就是說,對(duì)LowerLimit調(diào)用read_value()與increment()時(shí),其全部追溯到其父類中執(zhí)行,而被作用的值value為第一類型值。對(duì)LowerLimit調(diào)用decrement()方法時(shí),該方法是用其成員變量Counter的read_value()來執(zhí)行的。此時(shí)被作用的值為第二類型值.所以會(huì)出現(xiàn)value值不同步的現(xiàn)象。

  四:解決

  很明顯,解決方法在于僅使用其中一份value值,當(dāng)然,Decorator本身的目的便在于避免使用繼承,所以應(yīng)當(dāng)覆蓋原有的方法,用成員變量的方法實(shí)現(xiàn)內(nèi)部實(shí)現(xiàn)。也就是將代碼中的注釋去掉.

  五:小結(jié)

  父類獲得了只是值的拷貝 而本身的聚合對(duì)象獲得了(參數(shù)的)全部的引用否則 如果有方法沒有覆蓋原來的 則該方法將沿用父類的 在拷貝的值(value)上操作而被覆蓋了的方法的將對(duì)本身聚合的對(duì)象中的值(value)進(jìn)行操作 于是兩value值產(chǎn)生分歧。

  從Decorator模式本身的用意可知:在已有的類基礎(chǔ)上的動(dòng)態(tài)功能的添加(不是用繼承來實(shí)現(xiàn),以防太深,難以控制)所以,構(gòu)造函數(shù)的參數(shù)應(yīng)當(dāng)也必須作為"已有的類基礎(chǔ)"來對(duì)待,也就是說,自身的聚合對(duì)象必須是
  該參數(shù)的引用,所有的方法也必須重造,否則將落入繼承的陷阱。