裝飾器模式(Decorator Pattern)是一種結構型設計模式,它允許你通過將對象放入包含行為的特殊封裝對象中來為原對象添加新的功能。裝飾器模式的主要優點是可以在運行時動態地添加功能,而不需要修改原對象的代碼。這使得代碼更加靈活和可擴展。
裝飾器模式的說明
角色
-
Component(組件):
- 定義了對象的接口,可以給這些對象動態地添加職責。
- 可以是抽象類或接口。
-
ConcreteComponent(具體組件):
- 實現了
Component
接口,定義了具體的業務邏輯。
- 實現了
-
Decorator(裝飾器):
- 也實現了
Component
接口,但其主要職責是為組件動態地添加功能。 - 通常包含一個對
Component
的引用,以便調用被裝飾對象的方法。
- 也實現了
-
ConcreteDecorator(具體裝飾器):
- 實現了
Decorator
,添加了具體的功能。
- 實現了
-
Client(客戶端):
- 通過
Component
接口操作對象,無需關心對象的具體類型。
- 通過
經典框架中的實現案例
1. Java I/O 框架
Java I/O 框架中的 InputStream
和 OutputStream
類及其子類(如 BufferedInputStream
、DataInputStream
等)是裝飾器模式的經典應用。
- Component(組件):
InputStream
和OutputStream
。 - ConcreteComponent(具體組件):
FileInputStream
、FileOutputStream
。 - Decorator(裝飾器):
BufferedInputStream
、DataInputStream
、BufferedOutputStream
、DataOutputStream
。 - ConcreteDecorator(具體裝飾器):
BufferedInputStream
和DataInputStream
為InputStream
添加了緩沖和數據讀取功能;BufferedOutputStream
和DataOutputStream
為OutputStream
添加了緩沖和數據寫入功能。
import java.io.*;public class JavaIOCompositeExample {public static void main(String[] args) {try {// 創建 FileInputStreamInputStream fileInputStream = new FileInputStream("input.txt");// 創建 BufferedInputStreamInputStream bufferedInputStream = new BufferedInputStream(fileInputStream);// 創建 DataInputStreamInputStream dataInputStream = new DataInputStream(bufferedInputStream);// 讀取數據int data;while ((data = dataInputStream.read()) != -1) {System.out.print((char) data);}// 關閉流dataInputStream.close();// 創建 FileOutputStreamOutputStream fileOutputStream = new FileOutputStream("output.txt");// 創建 BufferedOutputStreamOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream);// 創建 DataOutputStreamOutputStream dataOutputStream = new DataOutputStream(bufferedOutputStream);// 寫入數據dataOutputStream.writeBytes("Hello, World!");// 關閉流dataOutputStream.close();} catch (IOException e) {e.printStackTrace();}}
}
在這個例子中:
FileInputStream
和FileOutputStream
是具體組件,提供基本的文件讀寫功能。BufferedInputStream
和BufferedOutputStream
是裝飾器,為文件讀寫添加了緩沖功能。DataInputStream
和DataOutputStream
是具體裝飾器,為文件讀寫添加了數據讀寫功能。- 客戶端代碼通過裝飾器鏈來讀寫文件,而不需要關心具體的功能實現。
2. Spring AOP
Spring AOP(Aspect-Oriented Programming)框架中的切面(Aspect)和通知(Advice)可以看作是裝飾器模式的應用。通過 AOP,可以在運行時動態地為方法添加額外的功能,如日志記錄、事務管理等。
- Component(組件):業務邏輯方法。
- ConcreteComponent(具體組件):具體的業務邏輯實現。
- Decorator(裝飾器):切面(Aspect)。
- ConcreteDecorator(具體裝飾器):通知(Advice),如
@Before
、@After
、@Around
等。
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;@Aspect
public class LoggingAspect {@Before("execution(* com.example.service.MyService.doSomething(..))")public void logBefore() {System.out.println("Logging before method execution");}
}public class MyService {public void doSomething() {System.out.println("Doing something in MyService");}
}public class SpringAOPCompositeExample {public static void main(String[] args) {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();context.register(MyService.class, LoggingAspect.class);context.refresh();MyService myService = context.getBean(MyService.class);myService.doSomething();context.close();}
}
在這個例子中:
MyService
是具體組件,定義了具體的業務邏輯。LoggingAspect
是裝飾器,通過@Before
注解為MyService
的doSomething
方法添加了日志記錄功能。- 客戶端代碼通過 Spring 容器獲取
MyService
的實例,并調用其方法,而不需要關心日志記錄的具體實現。
3. MyBatis 框架
MyBatis 框架中的 Interceptor
機制也可以看作是裝飾器模式的應用。通過 Interceptor
,可以在 SQL 執行前后添加額外的邏輯,如緩存、日志記錄等。
- Component(組件):
Executor
接口。 - ConcreteComponent(具體組件):
BaseExecutor
類。 - Decorator(裝飾器):
Interceptor
接口。 - ConcreteDecorator(具體裝飾器):具體的
Interceptor
實現。
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.plugin.*;import java.util.Properties;@Intercepts({@Signature(type = Executor.class, method = "update", args = {String.class, Object.class})})
public class MyInterceptor implements Interceptor {@Overridepublic Object intercept(Invocation invocation) throws Throwable {System.out.println("Intercepting before update method execution");Object result = invocation.proceed();System.out.println("Intercepting after update method execution");return result;}@Overridepublic Object plugin(Object target) {return Plugin.wrap(target, this);}@Overridepublic void setProperties(Properties properties) {// 設置屬性}
}public class MyBatisCompositeExample {public static void main(String[] args) {// 創建 SqlSessionFactoryString resource = "mybatis-config.xml";InputStream inputStream = MyBatisCompositeExample.class.getClassLoader().getResourceAsStream(resource);SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);// 注冊 InterceptorsqlSessionFactory.getConfiguration().addInterceptor(new MyInterceptor());// 獲取 SqlSessiontry (SqlSession session = sqlSessionFactory.openSession()) {// 獲取 MapperUserMapper userMapper = session.getMapper(UserMapper.class);// 執行更新操作userMapper.updateUser(new User(1, "John Doe"));}}
}// User 類
public class User {private int id;private String name;public User(int id, String name) {this.id = id;this.name = name;}// Getters and Setters
}// UserMapper 接口
public interface UserMapper {void updateUser(User user);
}// mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration><environments default="development"><environment id="development"><transactionManager type="JDBC"/><dataSource type="POOLED"><property name="driver" value="com.mysql.jdbc.Driver"/><property name="url" value="jdbc:mysql://localhost:3306/mydb"/><property name="username" value="root"/><property name="password" value="password"/></dataSource></environment></environments><mappers><mapper resource="UserMapper.xml"/></mappers>
</configuration>// UserMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.mapper.UserMapper"><update id="updateUser" parameterType="com.example.model.User">UPDATE usersSET name = #{name}WHERE id = #{id}</update>
</mapper>
在這個例子中:
Executor
是組件,負責執行 SQL 語句。BaseExecutor
是具體組件,提供了基本的 SQL 執行功能。MyInterceptor
是具體裝飾器,通過@Intercepts
注解為Executor
的update
方法添加了日志記錄功能。- 客戶端代碼通過
SqlSessionFactory
獲取SqlSession
,再通過SqlSession
獲取UserMapper
,并調用其方法,而不需要關心日志記錄的具體實現。
運行結果
Intercepting before update method execution
Doing something in MyService
Intercepting after update method execution
解釋
-
Component(組件):
Executor
接口定義了執行 SQL 語句的方法。BaseExecutor
類實現了Executor
接口,提供了基本的 SQL 執行功能。
-
Decorator(裝飾器):
Interceptor
接口定義了攔截器的方法。MyInterceptor
類實現了Interceptor
接口,添加了日志記錄功能。
-
ConcreteDecorator(具體裝飾器):
MyInterceptor
通過intercept
方法在 SQL 執行前后添加日志記錄功能。
-
Client(客戶端):
- 客戶端代碼通過
SqlSessionFactory
獲取SqlSession
,再通過SqlSession
獲取UserMapper
,并調用其方法,而不需要關心日志記錄的具體實現。
- 客戶端代碼通過
總結
-
Java I/O 框架:
- 通過
InputStream
和OutputStream
的裝飾器(如BufferedInputStream
、DataInputStream
)動態地添加功能,使得 I/O 操作更加靈活和高效。
- 通過
-
Spring AOP:
- 通過切面(Aspect)和通知(Advice)為方法動態地添加額外的功能,如日志記錄、事務管理等,使得業務邏輯更加清晰和模塊化。
-
MyBatis 框架:
- 通過
Interceptor
機制為 SQL 執行動態地添加額外的功能,如日志記錄、緩存等,使得 SQL 操作更加靈活和可擴展。
- 通過
通過這些經典實現,可以看到裝飾器模式在實際應用中的強大之處,它不僅簡化了客戶端代碼,還提高了系統的可維護性和擴展性。