1.本节内容
- 门面模式的意图介绍
- 门面模式带来的好处
- 门面模式的应用场景
- 实现模式的3中方式:POJO、无状态与有状态回话Bean门面
- 有状态与无状态回话Bean门面的重要差别
- 关于门面模式使用的警告以及潜在陷阱
2.门面模式简介
GoF一书中是这样描述门面模式的:“为子系统中的一组接口提供一个统一的接口”。Head First Design Patterns给出了相同的解释,同时又指出,在隐藏子系统复杂性的同时,门面模式又通过一个易于使用的接口提供子系统的全部功能。其基本类图如图2-1
图2-1 门面模式类图
3.门面模式带来的好处
(1)降低了耦合度,因为客户端对子系统一无所知。
(2)在变更时增强了可维护性和可管理性。
(3)实现功能重用,因为门面模式鼓励控制重用以及细粒度逻辑。
(4)每次方法调用时都会调用相同的方法,从而确保了服务执行的一致性。
(5)分组相关方法并从一个方法调用中来调用它们,从而降低业务逻辑复杂度。
(6)*化的安全与事务控制管理。
(7)可测试性与可模拟性的模式实现。
4.门面模式的应用场景
(1)为遗留的后端系统提供简单且统一的访问。
(2)为类创建一个公开的API,如驱动程序。
(3)为可用服务提供粗粒度访问。
(4)减少网络调用。门面会对子系统发起多次调用,而远程客户端只对门面进行一次调用。
(5)将应用的流程与内部细节封装起来,从而提升安全性并简化操作。
5.实现门面模式
5.1 POJO方式实现
public class WashingMachine {
/**
* 洗衣机呈现给用户的功能是
* 1.强力洗涤 2.轻度洗涤
* 那么用户在操作时只需要设定是强力洗涤还是轻度洗涤,不用取关心水温,转速等等
* 细粒度的功能
* 这就是一个典型的门面模式
*/
public void heavilySoiled(){
setWaterTemperature(100);
setWashCycleDuration(90);
setSpinCycleDuration(10);
addDetergent();
addBleach();
addFabricSoftener();
heatWater();
startWash();
} public void lightlySoiled(){
setWaterTemperature(40);
setWashCycleDuration(20);
setSpinCycleDuration(10);
addDetergent();
heatWater();
startWash();
}
private void startWash() {
} private void heatWater() {
} private void addFabricSoftener() { } private void addBleach() { } private void addDetergent() {
} private void setSpinCycleDuration(int i) { } private void setWashCycleDuration(int i) {
} private void setWaterTemperature(int i) {
}
}
5.2 无状态Bean的门面模式
@Stateless
public class CustomerService {
public long getCustomer(int sessionID){
return 100005L;
}
public boolean checkId(long x){
return true;
}
}
@Stateless
public class LoanService {
public boolean checkCreditRating(long id,double amount){
// check if customer is eligible for the amount
return true;
} }
public class AccountService {
public boolean getLoan(double amount){
//check if bank value has enough
return true;
}
public boolean setCustomerbalance(long id,double amount){
//set new customer balance
return true;
}
}
@Stateless
public class BankServiceFacade { @Inject
CustomerService customerService;
@Inject
LoanService loanService;
@Inject
AccountService accountService; public boolean getLoan(int sessionId,double amount){
boolean result = false;
long id = customerService.getCustomer(sessionId); if(customerService.checkId(id)){
if(loanService.checkCreditRating(id, amount)){
if(accountService.getLoan(amount)){
result = accountService.setCustomerbalance(id, amount);
}
}
}
return result;
}
}
以上就是门面模式在javaEE中的应用,注意两个注解
@Stateless:表示无状态session Bean
@Inject:表示依赖注入
从以上代码可以看出一个门面可以调用其他子系统中的门面,这反过来又会封装他们自己的逻辑与流程。这展示了使用门面的一个好处:简化了方法调用层级。
5.3 有状态Bean实现门面
用状态Bean实现门面大体与无状态Bean实现门面一样,唯一不同的就是将@Stateless注解改为@Stateful注解,该注解会将Bean标记为有状态。
6 有关使用门面模式的警告和潜在威胁
门面模式应该应用与高层封装复杂(业务)逻辑,并通过一个API提供整洁的访问单点。
如果要为其他人提供一个接口或API,请首先考虑逻辑的复杂性,以及可能会出现的变更。门面模式在提供一个简洁的API的同时,又隐藏掉可能会发生变化的部分方面很有优势。
不过,毫无必要的将方法包装到门面中却不是一个好的做法,还会增加不必要的层次。不当的封装可能会导致太多的调用,以及毫无价值的层次。
在实现会话门面时,需要确定用例是否需要维持状态,只调用门面的一个方法来接受所需服务的用例是非对话式的,因此没有必要在方法调用间保持会话状态,另一方面,如果必须在方法调用间保持会话状态,那么最恰当的方式就是将这种门面实现为有状态会话Bean。
需要注意的就是,有状态会话门面使用时,他会占用服务器资源,直到客户端触发会话将其释放掉或时超时。这意味着在大多数时间内,有会话Bean门面会绑定到客户端,但却什么都没做,它仅维持着状态并使用资源。与无状态会话Bean门面不同的是,它无法在其他客户端之间重用和共享,因为,每个请求都会创建无状态门面的新实例,并且维持着对该客户端会话的状态。
7.小结
可将门面模式实现为POJO、无状态会话Bean、有状态会话Bean。实现门面模式的各种方式解决了不同用例场景下的不同问题。不过在实现并没有背离其主要意图:为复杂的子系统提供一个高层次、简单的接口。
在决定将门面实现为有状态会话Bean时要小心,确保它不会导致资源消耗问题。
设计良好的应用会充分利用门面模式来封装复杂逻辑,并将子系统与客户端解耦;不过,对门面模式的不当使用和过度使用会导致更加复杂、拥有多个层次的系统。
会话门面模式类似于实体-控制-边界架构模式中的边界,并且也与适配器和包装模式有关。