外观模式又称门面模式,这是我们在开发过程中经常会用到的一种设计模式,很多时候甚至我们在使用这种设计模式的时候自己都没有意识到,因为这种设计模式的思想非常的简单,笔者也是最近在学习设计模式的时候才知道这种方式叫做外观模式。
一、外观模式概念
外观模式(Facade)属于结构型设计模式,它通过向客户端提供一个可以访问系统的接口,向客户端屏蔽系统底层的复杂实现,使客户端可以只用关注这个接口的功能,从而实现客户端与系统底层实现的解耦。
在上图中,Facade就是系统对外开放的接口组,客户端通过调用Facade中提供的接口来间接的调用系统中的各个模块。最近服务架构上比较流行微服务这种设计,这种架构设计就是将一个大的系统拆分成一个个的子系统,然后通过dubbo接口或者Spring Cloud等将服务接口暴露出去来供其他业务方调用,像这种微服务暴露出来的接口就是类似于使微服务的门面,其他的业务方只需要关心这个接口的功能是什么,至于这些功能是怎么调用微服务中的各个功能的,就只要交给Facade去处理就好了。
二、使用场景
1.在设计系统初期,应该有意识地将系统进行层级拆分,定义每一层的层级接口入口,底层应该尽可能向上屏蔽细节,这样的代码会具有更好的可维护性,也更加的易于理解。
2.随着开发的业务越来越复杂,应该尽可能地将那些复杂的业务代码封装成一个简单易懂的接口,增加一个外观模式来供业务方进行调用,避免业务方陷入细节的洪流中。
三、示例
这里以发送短信的业务为例,发送短信这个业务主要分为短信请求发送方(客户端),短信发送系统,在短信发送系统中并不是直接将短信发送出去就完事的,它会涉及多个业务功能,包括短信文本内容校验、手机号检验、短信记录存数据库、发送短信等。我们首先给出各个子功能的模块:
文本校验:
public class ContentVerify{
public boolean isValidContent(String content){
//内容判空
//敏感词检测
return 是否有效文本
}
}
手机号校验:
public class PhoneVerify{
public boolean isValidPhoneNum(String phoneNum){
//对手机号进行校验
}
}
短信记录存数据库:
public class SmsReocrdService{
@Autowired
private SmsRecordDao smsRecordDao;
public boolean insertSmsRecord(SmsRecord sms){
//进行一些其他的处理
return smsRecordDao.insert(sms);
}
}
发送短信:
public class SmsSender{
@Autowired
private HttpClinet httpClient;
public boolean send(SmsDto sms){
//将短信内容封装成http请求参数
return httpClient.post(url,param,header);
}
}
对于要发送短信的业务方来说,他们才不关心什么时候存数据库、要不要存数据库,也不关心怎么来进行短信的发送,他们的诉求很简单,就是我把要发送的内容和手机号给你,剩下的事情你帮我全做了。OK,那我们就用外观模式对上述的各个功能进行组合封装,然后向业务方暴露最简单的调用接口就行了。
SmsSendFacade:
public class SmsSendFacade{
@Autowwired
private SmsSender smsSender;
@Autowired
private SmsRecordService smsRecordService;
@Autowired
private ContentVerify contentVerify;
@Autowired
private PhoneVerify phoneVerify;
public boolean sendSms(String content,String phoneNum){
if(!contentVerify.isValidContent){
return false;
}
if(!phoneVerify.isValidPhone){
return false;
}
//转换成数据库存储的对象
SmsRecord smsRecord = convert(content,phoneNum);
smsRecordService.insertSmsRecord(smsRecord);
SmsDto smsDto = convertDto(content,phoneNum);
return smsSender.send(smsDto);
}
}
最后我们来看一下业务方:
public class Client{
@Autowired
private SmsSendFacade smsSendFacade;
public void sendSms(){
//生成需要发送的文本内容content
//获取需要发送对象的手机号phoneNum
if(smsSendFacade.send(content,phoneNum)){
System.out.println("短信发送成功");
}
}
}
四、小结
从上面的示例可以看出来,外观模式的概念和应用很简单,主要职责就是将底层实现细节进行封装、对外或对上暴露调用接口,实现客户端与系统以及底层与上层交互的解耦。但是这种设计模式也有一些缺点,比如不符合设计模式的开闭原则,要对它进行修改的话很麻烦,这就需要我们在实际的工作中有所取舍了。