一、外观模式的实现方式
外观模式的核心是通过封装复杂子系统的调用逻辑,为客户端提供一个统一的简单接口。以下是实现步骤及示例:
- 定义子系统类
子系统类负责实现具体功能,与外观类解耦。例如,家庭影院系统中的投影仪、音响等组件:
java
// 子系统接口
interface TheaterControl {
void on();
void off();
void setVolume(int volume);
}
// 具体子系统实现
class Projector implements TheaterControl {
public void on() { System.out.println("投影仪开启"); }
public void off() { System.out.println("投影仪关闭"); }
}
class StereoSystem implements TheaterControl {
public void on() { System.out.println("音响开启"); }
public void off() { System.out.println("音响关闭"); }
public void setVolume(int volume) { System.out.println("音量设为:" + volume); }
}
- 创建外观类
外观类聚合子系统对象,提供高层接口,协调子系统操作:
java
public class HomeTheaterFacade {
private TheaterControl projector;
private TheaterControl stereo;
public HomeTheaterFacade() {
this.projector = new Projector();
this.stereo = new StereoSystem();
}
// 统一接口:观影模式
public void watchMovie(String movie) {
System.out.println("准备观看:" + movie);
projector.on();
stereo.on();
stereo.setVolume(50);
}
}
- 客户端调用
客户端通过外观类简化复杂操作:
java
public class Client {
public static void main(String[] args) {
HomeTheaterFacade facade = new HomeTheaterFacade();
facade.watchMovie("Java设计模式实战");
}
}
二、测试方法与Mockito框架应用
测试外观模式时,需验证其是否正确调用子系统方法。通过Mockito框架模拟子系统对象,确保外观类逻辑正确。
- 基础测试:验证子系统方法调用
java
import static org.mockito.Mockito.*;
import org.junit.Test;
public class HomeTheaterFacadeTest {
@Test
public void testWatchMovie() {
// 创建Mock对象
TheaterControl mockProjector = mock(Projector.class);
TheaterControl mockStereo = mock(StereoSystem.class);
// 创建外观类并注入Mock对象
HomeTheaterFacade facade = new HomeTheaterFacade() {
@Override
public TheaterControl getProjector() { return mockProjector; }
@Override
public TheaterControl getStereo() { return mockStereo; }
};
// 执行外观方法
facade.watchMovie("测试电影");
// 验证子系统方法调用
verify(mockProjector).on();
verify(mockStereo).on();
verify(mockStereo).setVolume(50);
}
}
- 高级测试:异常处理与状态验证
模拟子系统异常,测试外观类的容错逻辑:
java
@Test
public void testWatchMovieWithException() {
TheaterControl mockProjector = mock(Projector.class);
when(mockProjector.on()).thenThrow(new RuntimeException("投影仪故障"));
HomeTheaterFacade facade = new HomeTheaterFacade() {
@Override
public TheaterControl getProjector() { return mockProjector; }
};
try {
facade.watchMovie("测试电影");
fail("预期抛出异常");
} catch (RuntimeException e) {
assertEquals("投影仪故障", e.getMessage());
}
}
三、实现与测试的注意事项
- 实现最佳实践
- 单一职责:外观类仅负责协调子系统,避免包含业务逻辑。
- 接口隔离:子系统应通过接口与外观类交互,增强扩展性。
- 线程安全:若外观类被多线程访问,需同步关键方法。
- 测试关键点
- 覆盖所有子系统调用:确保每个子系统方法在测试中被验证。
- 模拟异常场景:测试子系统故障时外观类的容错机制。
- 避免过度模拟:仅Mock必要子系统,保留真实依赖以测试完整流程。
四、总结
外观模式通过封装复杂子系统的调用逻辑,显著降低客户端耦合度。实现时需遵循接口隔离与单一职责原则,测试时利用Mockito框架验证子系统交互。其核心价值在于简化接口设计,但需注意对开闭原则的潜在违背及测试复杂度问题。