详细介绍:设计模式第五章(门面模式)

设计模式第五章(门面模式)

​ 门面模式(Facade Pattern)是一种结构型设计模式,它为子系统中的一组接口提供了一个统一的高层接口,使得子系统更容易使用。

门面模式主要包含以下几个角色:

  • Facade(门面角色):是门面模式的核心,为子系统提供统一接口,屏蔽子系统的复杂性。它知道哪些子系统负责处理请求,并将请求代理给适当的子系统对象。
  • SubSystem Classes(子系统角色):实现子系统的功能,这些子系统对客户端来说是黑盒,客户端通过门面角色与它们交互。
  • Client(客户角色):与门面角色交互,无需了解子系统的具体实现。

​ 例如,在一个电商系统中,订单处理涉及库存系统、支付系统、物流系统等多个子系统。可以设计一个订单处理门面,将这些子系统的功能整合在一起,客户端只需与订单处理门面交互,无需了解库存、支付、物流等子系统的具体实现细节,就能完成订单处理的操作。

门面模式的优点包括简化客户端调用、减少系统依赖、提高代码的可维护性和可读性等。缺点是不符合开闭原则,当增加子系统和扩展子系统行为时,可能需要修改门面类,并且在某些情况下,可能会导致门面类变得庞大,承担过多的责任。

引言

我们模拟一个mysql 和tomcat 的启动过程,mysql 的启动过程是不是分为好几个步骤,tomcat 启动是不是也分为多个步骤,接下来我们用代码表示一下。

mysql 启动过程

public class Mysql {
public void initData() {
System.out.println("初始化mysql......");
}
public void checkLog() {
System.out.println("校验日志,恢复未提交的数据......");
}
public void unlock() {
System.out.println("释放锁");
}
public void listerPort() {
System.out.println("监听端口 3306 ");
}
}

tomcat 启动过程

public class MyTomcat {
public void initEngine() {
System.out.println("初始化tomcat引擎....");
}
public void initWeb() {
System.out.println("加载web应用。。。");
}
}

客户端调用

public class Main {
public static void main(String[] args) {
Mysql mysql = new Mysql();
mysql.initData();
mysql.checkLog();
mysql.unlock();
mysql.listerPort();
System.out.println("-------------->");
MyTomcat tomcat = new MyTomcat();
tomcat.initEngine();
tomcat.initWeb();
}
}

在这里插入图片描述

问题点

​ 我们看到,作为第一个客户端,我们调用需要经过多个步骤,有没有一种简单的方法,我们只需要调用,某一个方法就能实现这种场景呢,那么接下来就是一个门面的封装了。

门店模式封装启动过程

  • 抽象启动类
  • 实现类

抽象类

我们定义了一个抽象启动的门店接口,里面只有一个方法,start()

public interface ServiceFace {
void start();
}

mysql 实现

public class MySql1 implements ServiceFace {
@Override
public void start() {
Mysql mysql = new Mysql();
mysql.initData();
mysql.checkLog();
mysql.unlock();
mysql.listerPort();
}
}

tomcat实现

public class MyTomcat1 implements ServiceFace {
@Override
public void start() {
MyTomcat tomcat = new com.fashion.ori.MyTomcat();
tomcat.initEngine();
tomcat.initWeb();
}
}

客户端

public class Main {
public static void main(String[] args) {
ServiceFace tomcat = new MyTomcat1();
tomcat.start();
ServiceFace mysql = new MySql1();
mysql.start();
}
}

在这里插入图片描述

我们看到,调用的客户端只需要知道门面接口定义的抽象方法即可,这样调用的过程封装到实现里面,使客户端使用更简单。

自定义门面插件类

需求背景,我需要统计一个接口每次被调用都进行一个计次数的动作,当然我们可以直接写到方法中,但是如果这是一个插件呢,我们需要如何实现。

  • 插件api 模块
  • 插件实现类
  • 调用方

插件api

public interface MyPlugin {
void beforeGetTime();
}
<modelVersion>4.0.0</modelVersion>
<groupId>com.fashion</groupId>
<artifactId>my_plugin_api</artifactId>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
</project>

插件实现类

public class CountPlugin implements MyPlugin {
AtomicInteger count = new AtomicInteger(0);
@Override
public void beforeGetTime() {
System.out.println("访问次数:"+count.incrementAndGet());
}
}
<artifactId>count_plugin</artifactId>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
  <dependency>
  <groupId>com.fashion</groupId>
  <artifactId>my_plugin_api</artifactId>
  <version>1.0-SNAPSHOT</version>
  </dependency>
</dependencies>
</project>

客户端

@RestController
public class TimeController {
private final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
private MyPlugin myPlugin;
@GetMapping("/time")
public String getTime() {
if (myPlugin != null) {
myPlugin.beforeGetTime();
}
return LocalDateTime.now().format(dateTimeFormatter);
}
/**
*  加载插件  必须有一个文件叫 liuqiang.plug
*/
@GetMapping("/load/plugin/{path}")
public String loadPlugin(@PathVariable("path") String path) {
File file = new File("face-pattern\\face-boot\\"+path);
try(
URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{file.toPath().toUri().toURL()});
InputStream lqPlugin = urlClassLoader.getResourceAsStream("chajian.plugin");
) {
//插件的实现类
String myPluginClassName = new String(lqPlugin.readAllBytes());
Class<?> aClass = urlClassLoader.loadClass(myPluginClassName.trim());
  // 生成构建方法
  Constructor<?> constructor = aClass.getConstructor();
    myPlugin = (MyPlugin)constructor.newInstance();
    return "插件加载成功----"+aClass.getName();
    } catch (Exception e) {
    return "插件记载失败"+e.getMessage();
    }
    }
    }

pom文件

<properties>
<java.version>17</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring-boot.version>2.6.13</spring-boot.version>
</properties>
<dependencies>
  <dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
  </dependency>
  <dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-test</artifactId>
  <scope>test</scope>
  </dependency>
  <dependency>
  <groupId>com.fashion</groupId>
  <artifactId>my_plugin_api</artifactId>
  <version>1.0-SNAPSHOT</version>
  </dependency>
</dependencies>
<dependencyManagement>
  <dependencies>
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-dependencies</artifactId>
    <version>${spring-boot.version}</version>
    <type>pom</type>
    <scope>import</scope>
    </dependency>
  </dependencies>
</dependencyManagement>
<build>
  <plugins>
    <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>3.8.1</version>
      <configuration>
      <source>17</source>
      <target>17</target>
      <encoding>UTF-8</encoding>
      </configuration>
    </plugin>
    <plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <version>${spring-boot.version}</version>
      <configuration>
      <mainClass>com.fashion.face.FaceBootApplication</mainClass>
      <skip>true</skip>
      </configuration>
      <executions>
        <execution>
        <id>repackage</id>
          <goals>
          <goal>repackage</goal>
          </goals>
        </execution>
      </executions>
    </plugin>
  </plugins>
</build>

启动流程

我们先加载插件,当插件加载后,那么我们访问 time 接口就会调用插件中的实现方法

在这里插入图片描述

在这里插入图片描述

调用接口

在这里插入图片描述

在这里插入图片描述

posted on 2025-09-30 21:58  slgkaifa  阅读(9)  评论(0)    收藏  举报

导航