裝飾模式
一同云、概念
裝飾模式(Decorator Pattern):動(dòng)態(tài)地給一個(gè)對(duì)象增加一些額外的職責(zé),就增加對(duì)象功能來(lái)說(shuō)堵腹,裝飾模式比生成子類(lèi)實(shí)現(xiàn)更為靈活炸站。裝飾模式是一種對(duì)象結(jié)構(gòu)型模式。
定義一個(gè)抽象的裝飾類(lèi)疚顷,將具體的裝飾類(lèi)作為其子類(lèi)旱易,然后繼承具體的裝飾類(lèi)。
二腿堤、使用場(chǎng)景
- 在不影響其他對(duì)象的情況下阀坏,以動(dòng)態(tài)、透明的方式給單個(gè)對(duì)象添加職責(zé)笆檀。
- 當(dāng)不能采用繼承的方式對(duì)系統(tǒng)進(jìn)行擴(kuò)展或者采用繼承不利于系統(tǒng)擴(kuò)展和維護(hù)時(shí)可以使用裝飾模式忌堂。
使用裝飾模式的步驟:
- 在裝飾類(lèi)的內(nèi)部維護(hù)一個(gè)被裝飾類(lèi)的引用。
- 讓裝飾類(lèi)有一個(gè)共同的父類(lèi)或者是父接口酗洒。
三士修、UML結(jié)構(gòu)圖
四、代碼示例
案例一:
Component:
public interface Component {
public void operation();
}
Decorator:
public class Decorator implements Component{
//維持一個(gè)對(duì)抽象構(gòu)件對(duì)象的引用
private Component component;
//注入一個(gè)抽象構(gòu)件類(lèi)型的對(duì)象(通過(guò)構(gòu)造方法或者set方法)
public Decorator(Component component) {
this.component = component;
}
@Override
public void operation() {
//調(diào)用原有業(yè)務(wù)方法樱衷,此處沒(méi)有真正的實(shí)現(xiàn)operation方法棋嘲,具體的裝飾過(guò)程交由子類(lèi)完成
component.operation();
}
}
ConcreateDecorator:
public class ConcreateDecorator extends Decorator{
public ConcreateDecorator(Component component) {
super(component);
}
public void operation(){
//調(diào)用原有業(yè)務(wù)方法
super.operation();
//調(diào)用新增業(yè)務(wù)方法
addedBehavior();
}
//新增業(yè)務(wù)方法
public void addedBehavior(){
}
}
案例二:
使用繼承的方式增強(qiáng)一個(gè)類(lèi)的功能:
package com.hcx.pattern;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
/**
* 拓展BufferedReader的功能, 增強(qiáng)readLine方法箫老,返回的字符串帶有行號(hào)封字。
* @author hcx
*
*/
class BufferedLineNum extends BufferedReader{
//行號(hào)
int count = 1;
public BufferedLineNum(Reader in) {
super(in);
}
@Override
public String readLine() throws IOException {
String line = super.readLine();
if(line==null) {
return null;
}
line = count+" "+line;
count++;
return line;
}
}
/**
* 帶分號(hào)的緩沖輸入字符流
* @author hcx
*
*/
class BufferedSemi extends BufferedReader{
public BufferedSemi(Reader in) {
super(in);
}
@Override
public String readLine() throws IOException {
String line = super.readLine();
if(line==null) {
return null;
}
line = line+";";
return line;
}
}
/**
* 帶雙引號(hào)的緩沖輸入字符流
* @author hcx
*
*/
class BufferedQuto extends BufferedReader{
public BufferedQuto(Reader in) {
super(in);
}
@Override
public String readLine() throws IOException {
String line = super.readLine();
if(line == null) {
return null;
}
line = "\""+line+"\"";
return line;
}
}
public class Demo1 {
public static void main(String[] args) throws IOException {
File file = new File("F:\\Demo1.java");
//建立數(shù)據(jù)的輸入通道
FileReader fileReader = new FileReader(file);
//建立帶行號(hào)的緩沖輸入字符流
BufferedLineNum bufferedLineNum = new BufferedLineNum(fileReader);
//帶有分號(hào)的緩沖輸入字符流
BufferedSemi bufferedSemi = new BufferedSemi(fileReader);
//帶有雙引號(hào)的緩沖輸入字符流
BufferedQuto bufferedQuto = new BufferedQuto(fileReader);
String line = null;
while((line = bufferedLineNum.readLine())!=null) {
System.out.println(line);
}
}
}
使用裝飾模式增強(qiáng)一個(gè)類(lèi)的功能:
package com.hcx.pattern;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
/**
* 帶行號(hào)的緩沖輸入字符流
* @author hcx
*
*/
class BufferedLineNum2 extends BufferedReader{
//在內(nèi)部維護(hù)一個(gè)被裝飾類(lèi)的引用。
BufferedReader bufferedReader;
int count = 1;
public BufferedLineNum2(BufferedReader bufferedReader) {
// 注意: 該語(yǔ)句沒(méi)有任何的作用,只不過(guò)是為了讓代碼不報(bào)錯(cuò)阔籽。
super(bufferedReader);
this.bufferedReader = bufferedReader;
}
public String readLine() throws IOException {
String line = bufferedReader.readLine();
if(line==null) {
return null;
}
line = count+" "+line;
count++;
return line;
}
}
/**
* 帶分號(hào)緩沖輸入字符流
* 繼承的原因:為了讓這些裝飾類(lèi)的對(duì)象可以作為參數(shù)進(jìn)行傳遞流妻,達(dá)到互相裝飾的效果。
* @author hcx
*
*/
class BufferedSemi2 extends BufferedReader{
//在內(nèi)部維護(hù)一個(gè)被裝飾類(lèi)的引用笆制。
BufferedReader bufferedReader;
public BufferedSemi2(BufferedReader bufferedReader) {
//BufferReader沒(méi)有無(wú)參的構(gòu)造方法绅这,繼承了BufferedReader,所以要指定調(diào)用父類(lèi)的帶參的構(gòu)造方法
super(bufferedReader);// 注意: 該語(yǔ)句沒(méi)有任何的作用在辆,只不過(guò)是為了讓代碼不報(bào)錯(cuò)证薇。
this.bufferedReader = bufferedReader;
}
public String readLine() throws IOException{
//創(chuàng)建該類(lèi)對(duì)象時(shí),如果傳入的是buffereLineNum匆篓,則這里的ReadLine方法是調(diào)用了buffereLineNum的readLine方法.
String line = bufferedReader.readLine();
if(line==null){
return null;
}
line = line +";";
return line;
}
}
/**
* 帶雙引號(hào)緩沖輸入字符流
* @author hcx
*
*/
class BufferedQuto2 extends BufferedReader{
//在內(nèi)部維護(hù)一個(gè)被裝飾的類(lèi)
BufferedReader bufferedReader;
public BufferedQuto2(BufferedReader bufferedReader){ //new BufferedSemi2();
super(bufferedReader) ; //只是為了讓代碼不報(bào)錯(cuò)
this.bufferedReader = bufferedReader;
}
public String readLine() throws IOException{
//創(chuàng)建該類(lèi)對(duì)象時(shí)浑度,如果傳入的是bufferedSemi2,則這里的ReadLine方法是調(diào)用了bufferedSemi2的readLine方法.
String line = bufferedReader.readLine();
if(line==null){
return null;
}
line = "\""+line +"\"";
return line;
}
}
public class Demo2 {
public static void main(String[] args) throws IOException {
File file = new File("F:\\Demo1.java");
FileReader fileReader = new FileReader(file);
//建立緩沖輸入字符流
BufferedReader bufferedReader = new BufferedReader(fileReader);
//建立帶行號(hào)的緩沖輸入字符流
BufferedLineNum2 bufferedLineNum = new BufferedLineNum2(bufferedReader);
//帶分號(hào)的緩沖輸入字符流
BufferedSemi2 bufferedSemi2 = new BufferedSemi2(bufferedLineNum);
//帶雙引號(hào)的緩沖輸入字符流
BufferedQuto2 bufferedQuto2 = new BufferedQuto2(bufferedSemi2);
String line = null;
while((line = bufferedQuto2.readLine())!=null){
System.out.println(line);
}
}
}
案例三:
一家三口每個(gè)人都會(huì)工作鸦概,兒子的工作就是畫(huà)畫(huà)箩张,母親的工作就是在兒子的基礎(chǔ)上做一個(gè)增強(qiáng),不單止可以畫(huà)畫(huà)窗市,還可以上涂料先慷。爸爸的工作就是在媽媽基礎(chǔ)上做了增強(qiáng),就是上畫(huà)框咨察。
interface Work{
public void work();
}
class Son implements Work{
@Override
public void work() {
System.out.println("畫(huà)畫(huà)");
}
}
class Mather implements Work{
//需要被增強(qiáng)的類(lèi)论熙。
Work worker;
public Mather(Work worker){
this.worker = worker;
}
@Override
public void work() {
worker.work();
System.out.println("給畫(huà)上顏色");
}
}
class Father implements Work{
//需要被增強(qiáng)的類(lèi)的引用
Work worker;
public Father(Work worker){
this.worker = worker;
}
@Override
public void work() {
worker.work();
System.out.println("上畫(huà)框");
}
}
public class Demo {
public static void main(String[] args) {
Son s = new Son();
// s.work();
Mather m = new Mather(s);
// m.work();
Father f = new Father(s);
f.work();
}
}
總結(jié):
繼承實(shí)現(xiàn)的增強(qiáng)類(lèi)和裝飾模式實(shí)現(xiàn)的增強(qiáng)類(lèi)有何區(qū)別?
繼承實(shí)現(xiàn)的增強(qiáng)類(lèi):
- 優(yōu)點(diǎn):代碼結(jié)構(gòu)清晰摄狱,而且實(shí)現(xiàn)簡(jiǎn)單.
- 缺點(diǎn):對(duì)于每一個(gè)的需要增強(qiáng)的類(lèi)都要?jiǎng)?chuàng)建具體的子類(lèi)來(lái)幫助其增強(qiáng)脓诡,這樣會(huì)導(dǎo)致繼承體系過(guò)于龐大。
裝飾模式實(shí)現(xiàn)的增強(qiáng)類(lèi):
- 優(yōu)點(diǎn):內(nèi)部可以通過(guò)多態(tài)技術(shù)對(duì)多個(gè)需要增強(qiáng)的類(lèi)進(jìn)行增強(qiáng)媒役,可以使這些裝飾類(lèi)達(dá)到互相裝飾的效果誉券。使用比較靈活。
- 缺點(diǎn):需要內(nèi)部通過(guò)多態(tài)技術(shù)維護(hù)需要被增強(qiáng)的類(lèi)的實(shí)例刊愚。進(jìn)而使得代碼稍微復(fù)雜踊跟。
五、裝飾模式的優(yōu)點(diǎn)
- 對(duì)于擴(kuò)展一個(gè)對(duì)象的功能鸥诽,裝飾模式比繼承更加靈活商玫,不會(huì)導(dǎo)致類(lèi)的個(gè)數(shù)急劇增長(zhǎng)。
- 可以通過(guò)一種動(dòng)態(tài)的方式來(lái)擴(kuò)展一個(gè)對(duì)象的功能
- 可以對(duì)一個(gè)對(duì)象進(jìn)行多次裝飾牡借,通過(guò)使用不同的具體裝飾類(lèi)以及這些裝飾類(lèi)的排列組合拳昌。