Java设计模式之《外观模式》

发布于:2025-08-28 ⋅ 阅读:(16) ⋅ 点赞:(0)

目录

1、外观模式

1.1、定义

1.2、核心思想:

1.3、角色

1.4、优缺点

2、实现

3、使用场景


前言

关于Java的设计模式分类如下:

关于亨元模式的结构如下:


1、外观模式

1.1、定义

        外观模式是一种结构型设计模式,它为一组复杂的子系统接口提供了一个更简洁、统一的接口。这个模式定义了一个更高层次的接口,使得子系统更容易使用。

1.2、核心思想:

         隐藏系统的复杂性,并向客户端提供一个可以访问系统的、简化的接口。它就像是整个子系统的一个“门面”或“接待员”,客户端不需要了解系统内部的细节,只需要和这个“门面”打交道即可。

类比:

        想象一下餐厅的点餐过程。作为一个顾客(客户端),你不需要直接与后厨的厨师、切菜工、洗碗工(各个子系统)进行复杂的交互。你只需要和服务员(外观)沟通,告诉他你想要什么菜。服务员接收你的简单订单,然后他负责去协调后厨各个部门完成一系列复杂的工作(准备食材、烹饪、装盘等),最后将美味的菜肴送到你面前。服务员就充当了这个“外观”的角色。

1.3、角色

外观模式通常包含以下角色:

1、外观:

知道哪些子系统类负责处理客户端的请求。将客户端的请求代理给相应的子系统对象。

2、附加外观:

(可选)当一个外观变得过于复杂时,可以创建多个附加外观,将职责划分得更清晰。客户端和子系统都可以使用附加外观。

3、子系统:

由多个类或组件组成,实现子系统的功能。

处理由外观对象指派的任务,但对客户端一无所知(即不持有外观的引用)。

4、客户端:

通过调用外观提供的方法来与子系统进行交互,而不是直接调用子系统对象。

1.4、优缺点

1、优点:

简化客户端代码: 

客户端不再需要直接与复杂的子系统交互,代码变得清晰简洁。

降低耦合度: 

        将客户端与子系统解耦,使子系统的变化更容易管理。只要外观接口不变,子系统内部的修改不会影响到客户端。

提供了一个清晰的入口点:

 特别是对于层次化的结构,外观定义了系统的入口点,简化了系统的使用和理解。

2、缺点:

不符合开闭原则:

       当子系统增加新功能或改变时,通常需要修改外观类。不过,可以通过引入抽象外观和具体子外观来缓解这个问题,但这会增加系统的复杂性。

可能成为“上帝对象”: 

如果设计不当,外观类可能会承担过多的职责,变成一个庞大而难以维护的类。


2、实现

        用一个家庭影院系统来举例。开启家庭影院看电影需要一系列复杂的操作:开灯、开投影仪、开音响、设置投影仪输入模式、放下屏幕等。

1、没有外观模式的情况:

代码示例如下:

// 子系统类:电灯
class Light {
    public void on() { System.out.println("打开电灯"); }
    public void off() { System.out.println("关闭电灯"); }
    public void dim(int level) { System.out.println("调暗电灯到 " + level + "%"); }
}

// 子系统类:投影仪
class Projector {
    public void on() { System.out.println("打开投影仪"); }
    public void off() { System.out.println("关闭投影仪"); }
    public void setInput(String input) { System.out.println("设置投影仪输入为: " + input); }
}

// 子系统类:音响
class Amplifier {
    public void on() { System.out.println("打开音响"); }
    public void off() { System.out.println("关闭音响"); }
    public void setVolume(int level) { System.out.println("设置音量为: " + level); }
}

// 子系统类:屏幕
class Screen {
    public void up() { System.out.println("收起屏幕"); }
    public void down() { System.out.println("放下屏幕"); }
}

// 客户端代码 - 非常复杂!
public class ClientWithoutFacade {
    public static void main(String[] args) {
        Light light = new Light();
        Projector projector = new Projector();
        Amplifier amp = new Amplifier();
        Screen screen = new Screen();

        // 想看电影,需要一步步操作
        System.out.println("准备看电影...");
        light.dim(10);      // 调暗灯光
        screen.down();      // 放下屏幕
        projector.on();     // 打开投影仪
        projector.setInput("HDMI");
        amp.on();           // 打开音响
        amp.setVolume(5);
        // ... 还有其他操作
        System.out.println("开始播放电影...");

        // 看完电影还要一步步关闭
        System.out.println("\n电影结束,关闭设备...");
        // ... 反向操作所有步骤
        amp.off();
        projector.off();
        screen.up();
        light.on();
    }
}

2、使用外观模式后:

代码示例:

// 外观类:家庭影院外观
class HomeTheaterFacade {
    private Light light;
    private Projector projector;
    private Amplifier amp;
    private Screen screen;

    public HomeTheaterFacade(Light light, Projector projector, Amplifier amp, Screen screen) {
        this.light = light;
        this.projector = projector;
        this.amp = amp;
        this.screen = screen;
    }

    // 提供一个高度简化的“一键观影”方法
    public void watchMovie() {
        System.out.println("准备看电影...");
        light.dim(10);
        screen.down();
        projector.on();
        projector.setInput("HDMI");
        amp.on();
        amp.setVolume(5);
        System.out.println("影院已就绪,开始享受电影吧!");
    }

    // 提供一个高度简化的“一键结束”方法
    public void endMovie() {
        System.out.println("关闭家庭影院...");
        amp.off();
        projector.off();
        screen.up();
        light.on();
        System.out.println("影院已关闭。");
    }
}

// 客户端代码 - 变得极其简单!
public class ClientWithFacade {
    public static void main(String[] args) {
        // 初始化子系统组件(通常由依赖注入框架完成)
        Light light = new Light();
        Projector projector = new Projector();
        Amplifier amp = new Amplifier();
        Screen screen = new Screen();

        // 创建外观,并组合所需的子系统
        HomeTheaterFacade homeTheater = new HomeTheaterFacade(light, projector, amp, screen);

        // 使用外观提供的高级接口
        homeTheater.watchMovie(); // 一键开启
        System.out.println("\n正在播放《教父》...\n");
        homeTheater.endMovie();   // 一键关闭
    }
}

3、使用场景

1. Spring Framework 中的外观模式

代码示例如下:

// Spring的JdbcTemplate就是一个经典的外观
@Repository
public class UserRepository {
    
    @Autowired
    private JdbcTemplate jdbcTemplate; // 外观
    
    public void saveUser(User user) {
        // 极其简单的接口,隐藏了所有JDBC的复杂性
        jdbcTemplate.update(
            "INSERT INTO users (name, email) VALUES (?, ?)",
            user.getName(), user.getEmail()
        );
    }
    
    public List<User> findAll() {
        // 简化的查询接口
        return jdbcTemplate.query(
            "SELECT * FROM users",
            (rs, rowNum) -> new User(
                rs.getInt("id"),
                rs.getString("name"),
                rs.getString("email")
            )
        );
    }
}

2. SLF4J 日志框架

代码示例如下:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class MyService {
    // 获取外观接口
    private static final Logger logger = LoggerFactory.getLogger(MyService.class);
    
    public void doSomething() {
        try {
            // 业务逻辑
            logger.info("开始执行操作"); // 简化的日志接口
            // ...
            logger.debug("调试信息");
        } catch (Exception e) {
            logger.error("操作失败", e); // 统一的错误日志接口
        }
    }
}

3. 微服务中的API网关

代码示例如下:

// 模拟API网关外观
public class ApiGateway {
    private UserService userService;
    private OrderService orderService;
    private PaymentService paymentService;
    private AuthService authService;
    
    public ApiGateway() {
        this.userService = new UserService();
        this.orderService = new OrderService();
        this.paymentService = new PaymentService();
        this.authService = new AuthService();
    }
    
    // 提供统一的API入口
    public Response handleRequest(Request request) {
        // 身份验证
        if (!authService.validateToken(request.getToken())) {
            return Response.unauthorized();
        }
        
        // 路由到相应的服务
        switch (request.getPath()) {
            case "/users":
                return userService.handle(request);
            case "/orders":
                return orderService.handle(request);
            case "/payments":
                return paymentService.handle(request);
            default:
                return Response.notFound();
        }
    }
}

最佳实践和注意事项

  1. 不要过度使用:如果子系统本身很简单,就不需要外观模式,否则会增加不必要的抽象层。

  2. 保持外观简洁:外观应该提供真正简化的接口,而不是成为另一个复杂的层。

  3. 考虑使用接口:可以为外观定义接口,这样更容易替换不同的实现或进行测试。


总结

        外观模式其核心价值在于简化接口,降低复杂度。当你有一个复杂的子系统,并且希望为其提供一个更简单、更清晰的接口时,或者当你想将子系统与客户端解耦时,外观模式是一个绝佳的选择。

        它在 Java 的日志系统、框架和工具库中得到了广泛的应用。


参考文章:

1、设计模式结构型——外观模式-CSDN博客https://blog.csdn.net/tszc95/article/details/131817470?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522bba385534f448f909cb3ef323553d396%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=bba385534f448f909cb3ef323553d396&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~top_click~default-2-131817470-null-null.nonecase&utm_term=%E5%A4%96%E8%A7%82%E6%A8%A1%E5%BC%8F&spm=1018.2226.3001.4450