Java?裝飾器模式Decorator詳解及實(shí)現(xiàn)步驟
在 Java 的世界里,設(shè)計(jì)模式是開發(fā)者手中的利器,它們幫助我們以更優(yōu)雅、更靈活的方式構(gòu)建軟件系統(tǒng)。今天,我們就來深入探討其中一種非常實(shí)用的設(shè)計(jì)模式 —— 裝飾器模式(Decorator Pattern)。?
一、裝飾器模式概述?
裝飾器模式屬于結(jié)構(gòu)型設(shè)計(jì)模式,它允許向一個(gè)現(xiàn)有的對象添加新的功能,同時(shí)又不改變其結(jié)構(gòu)。這就好比給一個(gè)普通的杯子加上杯蓋、杯套,讓它具備保溫、防燙等功能,而杯子本身的基本結(jié)構(gòu)并沒有發(fā)生變化。在軟件開發(fā)中,當(dāng)我們需要在運(yùn)行時(shí)動(dòng)態(tài)地為對象添加職責(zé),并且避免通過繼承來擴(kuò)展功能帶來的類層次結(jié)構(gòu)復(fù)雜問題時(shí),裝飾器模式就派上用場了。?
裝飾器模式主要包含以下幾個(gè)角色:?
- 抽象組件(Component):定義一個(gè)抽象接口,規(guī)范具體組件和裝飾器的行為。?
- 具體組件(Concrete Component):實(shí)現(xiàn)抽象組件接口,是被裝飾的具體對象。?
- 抽象裝飾器(Decorator):繼承或?qū)崿F(xiàn)抽象組件接口,持有一個(gè)抽象組件的引用,并定義一個(gè)可以動(dòng)態(tài)添加職責(zé)的方法。?
- 具體裝飾器(Concrete Decorator):實(shí)現(xiàn)抽象裝飾器接口,負(fù)責(zé)為具體組件添加具體的功能。

二、裝飾器模式的實(shí)現(xiàn)步驟
1.故事背景:機(jī)器人功能擴(kuò)展的兩種方案
在智能家居領(lǐng)域,某科技公司推出了第一代家用機(jī)器人,它具備三個(gè)核心功能:對話、唱歌和播放音樂。

// 第一代機(jī)器人:基礎(chǔ)功能
interface Robot {
void talk(); // 對話
void sing(); // 唱歌
void playMusic(); // 播放音樂
}
class FirstGenerationRobot implements Robot {
@Override
public void talk() {
System.out.println("機(jī)器人:你好,我能陪你聊天");
}
@Override
public void sing() {
System.out.println("機(jī)器人:正在播放《青花瓷》");
}
@Override
public void playMusic() {
System.out.println("機(jī)器人:正在播放輕音樂");
}
}隨著用戶需求升級,廠家希望擴(kuò)展機(jī)器人的功能,讓它能夠掃地和跳舞。針對這個(gè)需求,有兩種技術(shù)方案可供選擇:

⑴.方案一:傳統(tǒng)繼承(廠家升級方案)
廠家選擇開發(fā)第二代機(jī)器人,通過繼承第一代產(chǎn)品并添加新功能:
?? 接口定義
// 基礎(chǔ)接口:第一代機(jī)器人支持的功能
interface Robot {
void talk();
void sing();
void playMusic();
}?? 類實(shí)現(xiàn)
// 第一代機(jī)器人(基礎(chǔ)實(shí)現(xiàn))
class FirstGenerationRobot implements Robot {
@Override
public void talk() {
System.out.println("對話中...");
}
@Override
public void sing() {
System.out.println("正在唱歌...");
}
@Override
public void playMusic() {
System.out.println("播放音樂中...");
}
}
// 第二代機(jī)器人:通過繼承方式擴(kuò)展功能
class SecondGenerationRobot extends FirstGenerationRobot {
public void sweep() {
System.out.println("正在掃地...");
}
public void dance() {
System.out.println("正在跳舞...");
}
}?? 測試代碼
public class InheritanceTest {
public static void main(String[] args) {
System.out.println("=== 繼承方式測試 ===");
SecondGenerationRobot robot = new SecondGenerationRobot();
robot.talk();
robot.sing();
robot.playMusic();
robot.sweep(); // 擴(kuò)展功能
robot.dance(); // 擴(kuò)展功能
}
}⑵.方案二:裝飾器模式

通過組合方式動(dòng)態(tài)添加新功能:
以下是根據(jù)機(jī)器人例子實(shí)現(xiàn)的裝飾器模式代碼及詳細(xì)解釋:
抽象組件(Robot 接口)
interface Robot {
void talk(); // 對話
void sing(); // 唱歌
void playMusic(); // 放音樂
}- 作用:定義機(jī)器人的基本功能,作為裝飾器模式的核心接口,規(guī)范所有機(jī)器人(包括原始機(jī)器人和裝飾后的機(jī)器人)的行為。
具體組件(第一代機(jī)器人)
class FirstGenerationRobot implements Robot {
@Override
public void talk() {
System.out.println("第一代機(jī)器人:對話");
}
@Override
public void sing() {
System.out.println("第一代機(jī)器人:唱歌");
}
@Override
public void playMusic() {
System.out.println("第一代機(jī)器人:放音樂");
}
}- 作用:實(shí)現(xiàn)抽象組件,是被裝飾的原始對象(第一代機(jī)器人,具有基本功能)。
抽象裝飾器(RobotDecorator)
abstract class RobotDecorator implements Robot {
protected Robot robot; // 持有被裝飾的機(jī)器人對象
public RobotDecorator(Robot robot) {
this.robot = robot;
}
// 轉(zhuǎn)發(fā)原始功能(保持原有功能不變)
@Override
public void talk() {
robot.talk();
}
@Override
public void sing() {
robot.sing();
}
@Override
public void playMusic() {
robot.playMusic();
}
}- 作用:作為裝飾器的基類,繼承抽象組件接口,通過組合(關(guān)聯(lián))持有原始機(jī)器人對象。它實(shí)現(xiàn)了原始功能的轉(zhuǎn)發(fā),確保裝飾時(shí)不破壞原有功能,同時(shí)為具體裝飾器提供統(tǒng)一的擴(kuò)展框架。
具體裝飾器(添加拖地和跳舞功能)
// 拖地裝飾器:繼承自RobotDecorator,用于為機(jī)器人添加拖地功能
class SweepFloorDecorator extends RobotDecorator {
// 構(gòu)造函數(shù):接收被裝飾的機(jī)器人實(shí)例
public SweepFloorDecorator(Robot robot) {
// 通過super調(diào)用父類構(gòu)造函數(shù),保存對原始機(jī)器人的引用
super(robot);
}
public void sweepFloor() { // 新增功能:拖地
System.out.println("裝飾后:拖地");
}
}
// 跳舞裝飾器
class DanceDecorator extends RobotDecorator {
public DanceDecorator(Robot robot) {
super(robot);
}
public void dance() { // 新增功能:跳舞
System.out.println("裝飾后:跳舞");
}
}測試代碼
public class RobotDemo {
public static void main(String[] args) {
// 原始機(jī)器人(第一代)
Robot robot = new FirstGenerationRobot();
robot.talk(); // 原始功能:對話
robot.sing(); // 原始功能:唱歌
robot.playMusic(); // 原始功能:放音樂
// 裝飾器擴(kuò)展:添加拖地功能
SweepFloorDecorator decoratedWithSweep = new SweepFloorDecorator(robot);
decoratedWithSweep.talk(); // 保留原始功能
decoratedWithSweep.sweepFloor(); // 新增功能:拖地
// 再裝飾:添加跳舞功能
DanceDecorator fullyDecorated = new DanceDecorator(decoratedWithSweep);
fullyDecorated.talk(); // 保留原始功能
// 正確調(diào)用拖地功能
decoratedWithSweep.sweepFloor(); // 拖地功能
// 新增功能:跳舞
fullyDecorated.dance();
}
}
裝飾器模式通過組合而非繼承,實(shí)現(xiàn)了運(yùn)行時(shí)動(dòng)態(tài)擴(kuò)展對象功能,避免了繼承帶來的類爆炸問題,是處理 “對象功能擴(kuò)展” 的優(yōu)雅方案。在機(jī)器人例子中,利用裝飾器模式,無需等待廠家更新(繼承方式),即可快速為第一代機(jī)器人添加拖地、跳舞功能,體現(xiàn)了模式的靈活性和實(shí)用性。

- 繼承機(jī)制適合功能較為穩(wěn)定、擴(kuò)展需求明確的場景,實(shí)現(xiàn)簡單,但不夠靈活。
- 裝飾器模式適合需要動(dòng)態(tài)組合功能、避免類爆炸的情況,雖然實(shí)現(xiàn)略復(fù)雜,但更符合開閉原則(對擴(kuò)展開放,對修改關(guān)閉)。
三、流程圖和內(nèi)存圖


1.創(chuàng)建基礎(chǔ)機(jī)器人
Robot robot = new FirstGenerationRobot();
- 創(chuàng)建第一代機(jī)器人實(shí)例,具備基礎(chǔ)功能(talk/sing/playMusic)
2.第一次裝飾:添加拖地功能
SweepFloorDecorator decoratedWithSweep = new SweepFloorDecorator(robot);
SweepFloorDecorator持有對原始機(jī)器人的引用- 繼承了
RobotDecorator的默認(rèn)實(shí)現(xiàn)(轉(zhuǎn)發(fā)所有方法調(diào)用)
3.調(diào)用原始功能
decoratedWithSweep.talk();
- 執(zhí)行流程:
decoratedWithSweep.talk()調(diào)用RobotDecorator的實(shí)現(xiàn)RobotDecorator轉(zhuǎn)發(fā)給被裝飾對象:robot.talk()- 最終執(zhí)行
FirstGenerationRobot.talk()
4.調(diào)用新增功能
decoratedWithSweep.sweepFloor();
- 直接調(diào)用
SweepFloorDecorator新增的方法 - 輸出:"裝飾后:拖地"
5.第二次裝飾:添加跳舞功能
DanceDecorator fullyDecorated = new DanceDecorator(decoratedWithSweep);
DanceDecorator持有對SweepFloorDecorator的引用- 形成裝飾器嵌套結(jié)構(gòu):
DanceDecorator -> SweepFloorDecorator -> FirstGenerationRobot
6.調(diào)用原始功能(多層裝飾后)
fullyDecorated.talk();
- 執(zhí)行流程:
fullyDecorated.talk()調(diào)用RobotDecorator的實(shí)現(xiàn)- 轉(zhuǎn)發(fā)給被裝飾對象:
decoratedWithSweep.talk() decoratedWithSweep.talk()再次轉(zhuǎn)發(fā):robot.talk()- 最終執(zhí)行
FirstGenerationRobot.talk()
7.調(diào)用第一層裝飾器功能
decoratedWithSweep.sweepFloor();
- 必須通過
decoratedWithSweep引用調(diào)用 - 因?yàn)?nbsp;
fullyDecorated類型是DanceDecorator,沒有直接暴露sweepFloor()方法
8.調(diào)用第二層裝飾器功能
fullyDecorated.dance();
- 直接調(diào)用
DanceDecorator新增的方法 - 輸出:"裝飾后:跳舞"
3 個(gè)
Robot引用的指向?qū)ο?/p>
變量
robot
- 類型:
Robot- 指向:
FirstGenerationRobot實(shí)例(原始機(jī)器人)
SweepFloorDecorator內(nèi)部的robot成員變量
- 類型:
Robot(父類RobotDecorator的protected Robot robot)- 指向:
FirstGenerationRobot實(shí)例(與變量robot指向同一個(gè)對象)
DanceDecorator內(nèi)部的robot成員變量
- 類型:
Robot(父類RobotDecorator的protected Robot robot)- 指向:
SweepFloorDecorator實(shí)例(與前兩個(gè)引用指向不同對象)
引用的內(nèi)存地址比較
Robot robot = new FirstGenerationRobot(); SweepFloorDecorator decoratedWithSweep = new SweepFloorDecorator(robot); DanceDecorator fullyDecorated = new DanceDecorator(decoratedWithSweep); // 內(nèi)存地址比較(假設(shè)對象內(nèi)存地址為示例值) robot: 0x1000 // 指向 FirstGenerationRobot decoratedWithSweep.robot: 0x1000 // 指向 FirstGenerationRobot(與 robot 相同) fullyDecorated.robot: 0x2000 // 指向 SweepFloorDecorator(與前兩者不同)
robot和decoratedWithSweep.robot指向相同對象(內(nèi)存地址均為0x1000)。fullyDecorated.robot指向不同對象(內(nèi)存地址0x2000,即SweepFloorDecorator實(shí)例)。
四、實(shí)戰(zhàn)中的裝飾器模式
1. Java I/O 庫中的應(yīng)用
// 輸入流裝飾示例:
InputStream fileInput = new FileInputStream("data.txt");
InputStream bufferInput = new BufferedInputStream(fileInput);
DataInputStream dataInput = new DataInputStream(bufferInput);
// 功能動(dòng)態(tài)組合:
// 基礎(chǔ)功能:FileInputStream
// 裝飾1:添加緩沖功能 BufferedInputStream
// 裝飾2:添加數(shù)據(jù)處理功能 DataInputStream2. Spring 框架中的應(yīng)用
// Spring Security的認(rèn)證過濾器鏈:
FilterChain filterChain = new UsernamePasswordAuthenticationFilter(
new BasicAuthenticationFilter(
new SecurityContextPersistenceFilter()
)
);
// 每個(gè)過濾器都是一個(gè)裝飾器,動(dòng)態(tài)增強(qiáng)請求處理能力到此這篇關(guān)于Java 裝飾器模式Decorator詳解及實(shí)現(xiàn)步驟的文章就介紹到這了,更多相關(guān)Java 裝飾器模式Decorator內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Mybatis動(dòng)態(tài)SQL?foreach批量操作方法
這篇文章主要介紹了Mybatis動(dòng)態(tài)SQL?foreach批量操作方法,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-03-03
Java實(shí)現(xiàn)JDBC向數(shù)據(jù)庫批量插入
在Java項(xiàng)目中可能會(huì)出現(xiàn)大量向數(shù)據(jù)庫中插入的情況,本文主要介紹了Java實(shí)現(xiàn)JDBC向數(shù)據(jù)庫批量插入,具有一定的參考價(jià)值,感興趣的可以了解一下2023-09-09
intellij idea創(chuàng)建第一個(gè)動(dòng)態(tài)web項(xiàng)目的步驟方法
這篇文章主要介紹了intellij idea創(chuàng)建第一個(gè)動(dòng)態(tài)web項(xiàng)目的步驟方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-10-10
多線程計(jì)數(shù),怎么保持計(jì)數(shù)準(zhǔn)確的方法
這篇文章主要介紹了多線程計(jì)數(shù)的方法,有需要的朋友可以參考一下2014-01-01
SpringBoot用多線程批量導(dǎo)入數(shù)據(jù)庫實(shí)現(xiàn)方法
這篇文章主要介紹了SpringBoot用多線程批量導(dǎo)入數(shù)據(jù)庫實(shí)現(xiàn)方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧2023-02-02
詳解通過JDBC進(jìn)行簡單的增刪改查(以MySQL為例)
JDBC是用于執(zhí)行SQL語句的一類Java API,通過JDBC使得我們可以直接使用Java編程來對關(guān)系數(shù)據(jù)庫進(jìn)行操作。通過封裝,可以使開發(fā)人員使用純Java API完成SQL的執(zhí)行。2017-01-01

