🎯 设计模式专栏,持续更新中
欢迎订阅:JAVA实现设计模式
🛠️ 希望小伙伴们一键三连,有问题私信都会回复,或者在评论区直接发言
外观模式
外观模式(Facade Pattern)为子系统中的一组接口提供一个统一的接口。它是一个结构型设计模式,能够为复杂子系统提供一个更简单的接口,使客户端无需了解子系统的内部细节。
简而言之,外观模式通过创建一个外观类,简化对子系统的访问,隐藏系统的复杂性。
实际案例:家庭影院系统
想象你有一个家庭影院系统,包括以下多个组件:
- 电视
- 音响系统
- 蓝光播放器
- 灯光
- 投影仪
要启动这个家庭影院,你通常需要打开多个设备,设置音响,调节灯光等等。显然,这个过程非常繁琐。为了简化操作,可以使用一个“外观类”,通过一个简单的方法来一次性处理所有这些操作。
类图解释:外观模式 UML 类图
组件:
- Facade(外观类):为子系统提供一个统一的接口。
- Subsystem(子系统类):一组复杂的子系统类,每个子系统都有各自的复杂功能。
- Client(客户端):通过外观类与子系统进行交互,不直接调用子系统的功能。
代码实现
Step 1: 创建子系统类
首先定义家庭影院的子系统,包括 DVD 播放器、音响系统和投影仪。
// 子系统类 1:DVDPlayer
public class DVDPlayer {
public void on() {
System.out.println("DVD Player is on.");
}
public void playMovie() {
System.out.println("DVD Player is playing the movie.");
}
public void off() {
System.out.println("DVD Player is off.");
}
}
// 子系统类 2:SoundSystem
public class SoundSystem {
public void on() {
System.out.println("Sound System is on.");
}
public void setVolume(int level) {
System.out.println("Sound System volume set to " + level);
}
public void off() {
System.out.println("Sound System is off.");
}
}
// 子系统类 3:Projector
public class Projector {
public void on() {
System.out.println("Projector is on.");
}
public void adjustScreen() {
System.out.println("Projector screen adjusted.");
}
public void off() {
System.out.println("Projector is off.");
}
}
Step 2: 创建外观类
定义外观类 HomeTheaterFacade
,它封装了对各个子系统的调用,提供简化的接口。
// 外观类:HomeTheaterFacade
public class HomeTheaterFacade {
private DVDPlayer dvdPlayer;
private SoundSystem soundSystem;
private Projector projector;
public HomeTheaterFacade(DVDPlayer dvdPlayer, SoundSystem soundSystem, Projector projector) {
this.dvdPlayer = dvdPlayer;
this.soundSystem = soundSystem;
this.projector = projector;
}
// 启动家庭影院
public void startMovie() {
System.out.println("Starting the home theater...");
dvdPlayer.on();
soundSystem.on();
soundSystem.setVolume(10);
projector.on();
projector.adjustScreen();
dvdPlayer.playMovie();
}
// 关闭家庭影院
public void endMovie() {
System.out.println("Shutting down the home theater...");
dvdPlayer.off();
soundSystem.off();
projector.off();
}
}
Step 3: 客户端调用外观类
客户端通过外观类 HomeTheaterFacade
来控制整个家庭影院系统。
public class Client {
public static void main(String[] args) {
// 创建子系统对象
DVDPlayer dvdPlayer = new DVDPlayer();
SoundSystem soundSystem = new SoundSystem();
Projector projector = new Projector();
// 创建外观对象
HomeTheaterFacade homeTheater = new HomeTheaterFacade(dvdPlayer, soundSystem, projector);
// 启动家庭影院
homeTheater.startMovie();
// 结束家庭影院
homeTheater.endMovie();
}
}
输出结果:
Starting the home theater...
DVD Player is on.
Sound System is on.
Sound System volume set to 10
Projector is on.
Projector screen adjusted.
DVD Player is playing the movie.
Shutting down the home theater...
DVD Player is off.
Sound System is off.
Projector is off.
外观类:HomeTheaterFacade
封装了多个子系统的操作,通过提供简化的接口 startMovie()
和 endMovie()
,客户端不再需要关心各个子系统的细节。
子系统:DVDPlayer
、SoundSystem
和 Projector
是复杂的子系统,每个子系统都有自己的操作,但通过外观类,客户端可以轻松地控制整个家庭影院。
外观模式在 MyBatis 应用的源码分析
在 MyBatis 中,MetaObject
是一个帮助类,用于对目标对象进行操作,如获取或设置属性值、查找属性、检查属性是否可读或可写等。
-
功能:封装了对对象属性的操作,提供了一个统一的接口,使得 MyBatis 不需要直接与底层反射机制打交道。
-
外观类:
MetaObject
-
子系统
ObjectWrapper
:包装对象,用于处理对象的属性操作。Reflector
:缓存反射信息,简化反射调用。PropertyTokenizer
:解析属性表达式(如user.name
),用于处理多层次的对象属性。
MetaObject 类的结构:
MetaObject
提供了一个简化的接口,用于封装多个复杂类的操作。它将 ObjectWrapper
和 Reflector
这些类的功能整合起来,外部用户无需直接和这些类打交道,只需通过 MetaObject
提供的方法即可操作对象。
public class MetaObject {
private final Object originalObject; // 原始对象
private final ObjectWrapper objectWrapper; // 对象包装器
private final ReflectorFactory reflectorFactory;
private final ObjectFactory objectFactory;
private final WrapperFactory wrapperFactory;
// 构造方法
private MetaObject(Object object, ObjectFactory objectFactory, ObjectWrapperFactory objectWrapperFactory, ReflectorFactory reflectorFactory) {
this.originalObject = object;
this.objectFactory = objectFactory;
this.wrapperFactory = objectWrapperFactory;
this.reflectorFactory = reflectorFactory;
this.objectWrapper = wrapperFactory.getWrapper(this, object); // 包装原始对象
}
// 获取属性值
public Object getValue(String name) {
PropertyTokenizer prop = new PropertyTokenizer(name);
if (prop.hasNext()) {
MetaObject metaValue = metaObjectForProperty(prop.getName());
if (metaValue == SystemMetaObject.NULL_META_OBJECT) {
return null;
} else {
return metaValue.getValue(prop.getChildren());
}
} else {
return objectWrapper.get(prop);
}
}
// 设置属性值
public void setValue(String name, Object value) {
PropertyTokenizer prop = new PropertyTokenizer(name);
if (prop.hasNext()) {
MetaObject metaValue = metaObjectForProperty(prop.getName());
if (metaValue == SystemMetaObject.NULL_META_OBJECT) {
throw new ReflectionException("The property '" + prop.getName() + "' is null.");
}
metaValue.setValue(prop.getChildren(), value);
} else {
objectWrapper.set(prop, value);
}
}
// 创建 MetaObject
public static MetaObject forObject(Object object, ObjectFactory objectFactory, ObjectWrapperFactory objectWrapperFactory, ReflectorFactory reflectorFactory) {
if (object == null) {
return SystemMetaObject.NULL_META_OBJECT;
} else {
return new MetaObject(object, objectFactory, objectWrapperFactory, reflectorFactory);
}
}
}
MetaObject
如何简化操作
通过 MetaObject
,用户不需要关心底层是如何通过反射、包装器、属性解析等机制操作对象的属性。用户只需要调用 getValue()
和 setValue()
方法,MetaObject
会在内部处理这些复杂的逻辑。
操作流程:
PropertyTokenizer
解析属性表达式:user.name
会被解析为user
和name
,支持嵌套属性。ObjectWrapper
处理属性获取或设置:ObjectWrapper
封装了对目标对象属性的操作。Reflector
缓存反射信息:通过反射获取目标对象的属性信息,但为了提高性能,Reflector
缓存了反射的结果。
通过这些子系统类的配合,MetaObject
提供了一个统一的接口来操作对象的属性,隐藏了底层的复杂性。
MetaObject
使用外观模式的优点
简化对象操作:通过 MetaObject
,用户可以轻松读取和修改对象的属性,而不需要关心底层的反射机制。
解耦复杂逻辑:MetaObject
将属性解析、对象包装、反射操作等复杂逻辑封装起来,客户端代码不需要直接与这些复杂的子系统交互。
提高代码可维护性:MetaObject
提供了统一的接口,封装了多个子系统的操作,使得代码更加简洁和可维护。
总结
外观模式通过引入一个简化的接口,将复杂的子系统隐藏在外观类之后,降低了系统的复杂性和耦合度。它在系统需要解耦客户端与多个子系统,或简化复杂操作时,非常有用。
通过上面的家庭影院案例,你可以看到外观模式如何通过外观类将多个复杂的子系统操作简化为统一的接口,提升代码的可读性和维护性。