1. 程式人生 > >JAVA設計模式初探之裝飾者模式

JAVA設計模式初探之裝飾者模式

這個模式花費了挺長時間,開始有點難理解,其實就是

定義:動態給一個物件新增一些額外的職責,就象在牆上刷油漆.使用Decorator模式相比用生成子類方式達到功能的擴充顯得更為靈活。
設計初衷:通常可以使用繼承來實現功能的拓展,如果這些需要拓展的功能的種類很繁多,那麼勢必生成很多子類,增加系統的複雜性,同時,使用繼承實現功能拓展,我們必須可預見這些拓展功能,這些功能是編譯時就確定了,是靜態的。

 要點:裝飾者與被裝飾者擁有共同的超類,繼承的目的是繼承型別,而不是行為

   實際上Java 的I/O API就是使用Decorator實現的。

//定義被裝飾者
public interface Human {
	public void wearClothes();

	public void walkToWhere();
}

//定義裝飾者
public abstract class Decorator implements Human {
	private Human human;

	public Decorator(Human human) {
		this.human = human;
	}

	public void wearClothes() {
		human.wearClothes();
	}

	public void walkToWhere() {
		human.walkToWhere();
	}
}

//下面定義三種裝飾,這是第一個,第二個第三個功能依次細化,即裝飾者的功能越來越多
public class Decorator_zero extends Decorator {

	public Decorator_zero(Human human) {
		super(human);
	}

	public void goHome() {
		System.out.println("進房子。。");
	}

	public void findMap() {
		System.out.println("書房找找Map。。");
	}

	@Override
	public void wearClothes() {
		// TODO Auto-generated method stub
		super.wearClothes();
		goHome();
	}

	@Override
	public void walkToWhere() {
		// TODO Auto-generated method stub
		super.walkToWhere();
		findMap();
	}
}

public class Decorator_first extends Decorator {

	public Decorator_first(Human human) {
		super(human);
	}

	public void goClothespress() {
		System.out.println("去衣櫃找找看。。");
	}

	public void findPlaceOnMap() {
		System.out.println("在Map上找找。。");
	}

	@Override
	public void wearClothes() {
		// TODO Auto-generated method stub
		super.wearClothes();
		goClothespress();
	}

	@Override
	public void walkToWhere() {
		// TODO Auto-generated method stub
		super.walkToWhere();
		findPlaceOnMap();
	}
}

public class Decorator_two extends Decorator {

	public Decorator_two(Human human) {
		super(human);
	}

	public void findClothes() {
		System.out.println("找到一件D&G。。");
	}

	public void findTheTarget() {
		System.out.println("在Map上找到神祕花園和城堡。。");
	}

	@Override
	public void wearClothes() {
		// TODO Auto-generated method stub
		super.wearClothes();
		findClothes();
	}

	@Override
	public void walkToWhere() {
		// TODO Auto-generated method stub
		super.walkToWhere();
		findTheTarget();
	}
}

//定義被裝飾者,被裝飾者初始狀態有些自己的裝飾
public class Person implements Human {

	@Override
	public void wearClothes() {
		// TODO Auto-generated method stub
		System.out.println("穿什麼呢。。");
	}

	@Override
	public void walkToWhere() {
		// TODO Auto-generated method stub
		System.out.println("去哪裡呢。。");
	}
}
//測試類,看一下你就會發現,跟java的I/O操作有多麼相似
public class Test {
	public static void main(String[] args) {
		Human person = new Person();
		Decorator decorator = new Decorator_two(new Decorator_first(
				new Decorator_zero(person)));
		decorator.wearClothes();
		decorator.walkToWhere();
	}
}

執行結果:

其實就是進房子找衣服,然後找地圖這樣一個過程,通過裝飾者的三層裝飾,把細節變得豐富。

關鍵點:
1、Decorator抽象類中,持有Human介面,方法全部委託給該介面呼叫,目的是交給該介面的實現類即子類進行呼叫。
2、Decorator抽象類的子類(具體裝飾者),裡面都有一個構造方法呼叫super(human),這一句就體現了抽象類依賴於子類實現即抽象依賴於實現的原則。因為構造裡面引數都是Human介面,只要是該Human的實現類都可以傳遞進去,即表現出Decorator dt = new Decorator_second(new Decorator_first(new Decorator_zero(human)));這種結構的樣子。所以當呼叫dt.wearClothes();dt.walkToWhere()的時候,又因為每個具體裝飾者類中,都先呼叫super.wearClothes和super.walkToWhere()方法,而該super已經由構造傳遞並指向了具體的某一個裝飾者類(這個可以根據需要調換順序),那麼呼叫的即為裝飾類的方法,然後才呼叫自身的裝飾方法,即表現出一種裝飾、鏈式的類似於過濾的行為。
3、具體被裝飾者類,可以定義初始的狀態或者初始的自己的裝飾,後面的裝飾行為都在此基礎上一步一步進行點綴、裝飾。
4、裝飾者模式的設計原則為:對擴充套件開放、對修改關閉,這句話體現在我如果想擴充套件被裝飾者類的行為,無須修改裝飾者抽象類,只需繼承裝飾者抽象類,實現額外的一些裝飾或者叫行為即可對被裝飾者進行包裝。所以:擴充套件體現在繼承、修改體現在子類中,而不是具體的抽象類,這充分體現了依賴倒置原則,這是自己理解的裝飾者模式。


說的不清楚,有些只可意會不可言傳的感覺,多看幾遍程式碼,然後自己敲出來執行一下,基本上就領悟了。


下面這個例子也有助於理解 裝飾的流程和作用

現在需要一個漢堡,主體是雞腿堡,可以選擇新增生菜、醬、辣椒等等許多其他的配料,這種情況下就可以使用裝飾者模式。


漢堡基類(被裝飾者,相當於上面的Human)

package decorator;  
  
public abstract class Humburger {  
      
    protected  String name ;  
      
    public String getName(){  
        return name;  
    }  
      
    public abstract double getPrice();  
  
}  
雞腿堡類(被裝飾者的初始狀態,有些自己的簡單裝飾,相當於上面的Person)
package decorator;  
  
public class ChickenBurger extends Humburger {  
      
    public ChickenBurger(){  
        name = "雞腿堡";  
    }  
  
    @Override  
    public double getPrice() {  
        return 10;  
    }  
  
}  

配料的基類(裝飾者,用來對漢堡進行多層裝飾,每層裝飾增加一些配料,相當於上面Decorator)

package decorator;  
  
public abstract class Condiment extends Humburger {  
      
    public abstract String getName();  
  
}  

生菜(裝飾者的第一層,相當於上面的decorator_zero)

package decorator;  
  
public class Lettuce extends Condiment {  
      
    Humburger humburger;  
      
    public Lettuce(Humburger humburger){  
        this.humburger = humburger;  
    }  
  
    @Override  
    public String getName() {  
        return humburger.getName()+" 加生菜";  
    }  
  
    @Override  
    public double getPrice() {  
        return humburger.getPrice()+1.5;  
    }  
  
}  


辣椒(裝飾者的第二層,相當於上面的decorator_first)

package decorator;  
  
public class Chilli extends Condiment {  
      
    Humburger humburger;  
      
    public Chilli(Humburger humburger){  
        this.humburger = humburger;  
          
    }  
  
    @Override  
    public String getName() {  
        return humburger.getName()+" 加辣椒";  
    }  
  
    @Override  
    public double getPrice() {  
        return humburger.getPrice();  //辣椒是免費的哦  
    }  
  
}  


測試類

package decorator;  
  
public class Test {  
  
    /** 
     * @param args 
     */  
    public static void main(String[] args) {  
        Humburger humburger = new ChickenBurger();  
        System.out.println(humburger.getName()+"  價錢:"+humburger.getPrice());  
        Lettuce lettuce = new Lettuce(humburger);  
        System.out.println(lettuce.getName()+"  價錢:"+lettuce.getPrice());  
        Chilli chilli = new Chilli(humburger);  
        System.out.println(chilli.getName()+"  價錢:"+chilli.getPrice());  
        Chilli chilli2 = new Chilli(lettuce);  
        System.out.println(chilli2.getName()+"  價錢:"+chilli2.getPrice());  
    }  
  
}  

輸出

雞腿堡  價錢:10.0  
雞腿堡 加生菜  價錢:11.5  
雞腿堡 加辣椒  價錢:10.0  
雞腿堡 加生菜 加辣椒  價錢:11.5  

作者:jason0539