Facade这是一个很摸不着头脑的词,翻译是门面模式,简直就是一脸懵逼。理解为店铺模式,估计更好。
曾看到过一个更有好玩的形象例子:
我有一个专业的Nikon相机,我就喜欢自己手动调光圈、快门,这样照出来的照片才专业,但MM可不懂这些,教了半天也不会。幸好相机有Facade设计模式,把相机调整到自动档,只要对准目标按快门就行了,一切由相机自动调整, 这样MM也可以用这个相机给我拍张照片了。
外部与一个子系统的通信必须通过一个统一的门面对象进行。门面模式提供一个高层次的接口,使得子系统更易于使用。每一个子系统只有一个门面类,而且此门面类只有一个实例,也就是说它是一个单例模式。但整个系统可以有多个门面类。
Facade模式正是这样一个”门面”:我们本来需要与后台的多个类或者接口打交道,而Facade模式是客户端和后台之间插入一个中间层——门面,这个门面跟后台的多个类或接口打交道,而客户端只需要跟门面打交道即可。
使用Facade模式可以说是后台设计和编码人员的一个必备素质。我不止碰到过一个这样的后台开发人员,他们认为只要把后台功能完成了就万事大吉,而没有站在后台使用者的角度来看一看自己写出来的代码。其实,我们写出来的后台代码是要给别人使用的,所以我们提供给使用者的接口要越简单越好,这不单是对使用者好,同时对开发者也是好处多多的,至少你的接口简单了,你和使用者的交流就容易了。
而Facade模式中的Facade类正是这样一个用户接口,它和后台中的多个类产生依赖关系,而后台的客户类则只跟Facade类产生依赖关系。为什么要这么做?其中的原因十分简单:后台的开发者熟悉他自己开发的各个类,也就容易解决和多个类的依赖关系,而后台的使用者则不太熟悉后台的各个类,不容易处理和它们之间的依赖;因此,后台的开发者自己在Facade类中解决了与后台多个类之间的依赖,后台的使用者只需要处理和Facade类的依赖即可。
好了,闲话少说。我们下面就以几个具体的例子来看一看Facade模式是怎么使用的。实际编程中,能使用到Facade模式的情况有很多,以下就分两种情况来具体说一说Facade模式的使用。可能还会有其他的情况,大家在实践中也可以加以补充。
第一种情况,客户类要使用的功能分布在多个类中,这些类可能相互之间没有什么关系;客户在使用后台的时候,必须先初始化要使用到的功能所在的类,然后才能使用。这时候,适合将这些功能集中在一个Facade类里,还可以替用户做一些初始化的工作,以减轻用户的负担。
例如,以商店为例。假如商店里出售三种商品:衣服、电脑和手机。这三种商品都是由各自的生产厂商卖出的,如下:
public class CoatFactory
{
public Coat saleCoat()
{
……
return coat;
}
……
}
然后是电脑的厂家类:
public class ComputerFactory
{
public Computer saleComputer()
{
……
return computer;
}
……
}
最后是手机商类:
public class MobileFactory
{
public Mobile saleMobile()
{
……
return mobile;
}
……
}
如果没有商店,我们就不得不分别跟各自的生产商打交道,如下:
//买衣服
CoatFactory coatFactory = new CoatFactory();
coatFactory.saleCoat();
//买电脑
ComputerFactory computerFactory = new ComputerFactory();
computerFactory.saleComputer ();
//买手机
MobileFactory mobileFactory = new MobileFactory();
mobileFactory.saleMobile();
对我们顾客来说,和这么多的厂家类打交道,这显然是够麻烦的。
这样,我们就需要创建一个商店类了,让商店类和这些厂家打交道,我们只和商店类打交道即可,如下:
public class StoreFacade
{
CoatFactory coatFactory = new CoatFactory();
ComputerFactory computerFactory = new ComputerFactory();
MobileFactory mobileFactory = new MobileFactory();
public Coat saleCoat()
{
return coatFactory.saleCoat();
}
public Computer saleComputer()
{
return computerFactory.saleComputer();
}
public Mobile saleMobile()
{
return mobileFactory.saleMobile ();
}
}
好了,现在我们要买东西,不用去跟那么多的厂家类打交道了。
StoreFacade store =new StoreFacade();
//买衣服
store.saleCoat();
//买电脑
store.saleComputer();
//买手机
store.saleMobile();
这里一般会调用到后台一些业务,因为毕竟展现给用户看到是前端,然后请求后台的数据。
public class SaleOrderEditUIPIEx extends SaleOrderEditUI
{
public boolean beforeAction(BatchActionEnum bizAction, BatchSelectionEntries selectionEntries, ActionEvent event)
{
SaleOrgUnitInfo saleOrgInfo = (SaleOrgUnitInfo) prmtSaleOrgUnit.getData();
if (BatchActionEnum.SUBMIT.equals(bizAction)){
//逻辑1:根据物理id获取详细物料(eas内部方法)
MaterialInfo material=entryInfo.getMaterial();
try {
material = MaterialFactory.getRemoteInstance().getMaterialInfo(new ObjectUuidPK(material.getId()));
} catch (EASBizException e1) {
e1.printStackTrace();
} catch (BOSException e1) {
e1.printStackTrace();
}
//根据物理处理其他操作,省略
//逻辑2:获取政策价格(自己写的后台逻辑,在SaleOrderPolicyPriceSyncFacadeBean)
BigDecimal policyPrice=BigDecimal.ZERO;
try {
policyPrice = SaleOrderPolicyPriceSyncFacadeFactory.getRemoteInstance().getPolicyPriceNew(cuId, material.getId()+"", customer.getId()+"");
} catch (EASBizException e) {
e.printStackTrace();
} catch (BOSException e) {
e.printStackTrace();
}
}
return super.beforeAction(bizAction, selectionEntries, event);
}
}
相当于可以看到这个Facade可以做什么(类似门店简介,看到这家店卖什么东东)
业务逻辑都在这里实现即可。
/**
* 政策价格3.0(2018版)
* @author zhengkai
*/
@Override
public BigDecimal getPolicyPriceNew(Context ctx, String orgunitid,
String materialid, String customerid) throws BOSException,
EASBizException {
StringBuffer sqlString = new StringBuffer( " /*dialect*/ select ppe.fprice ");
sqlString.append(" from t_scm_pricepolicyentry ppe ");
sqlString.append(" left join t_scm_pricepolicy pp ");
sqlString.append(" on pp.fid = ppe.fparentid ");
sqlString.append(" left join t_bd_material m ");
sqlString.append(" on ppe.fmaterialid=m.fid ");
sqlString.append(" left join t_bd_customer c ");
sqlString.append(" on ppe.fcustomerid=c.fid ");
sqlString.append(" left join t_org_company comp ");
sqlString.append(" on comp.fid=pp.fcontrolunitid ");
sqlString.append(" where pp.FBLOCKEDSTATUS=1 and pp.FCHECKEDSTATUS=2 ");
sqlString.append(" and pp.fsaleorgunitid=? ");
sqlString.append(" and m.fid=? ");
sqlString.append(" and c.fid=? ");
sqlString.append(" and sysdate between ppe.feffectivedate and ppe.fexpiredate ");
sqlString.append(" order by pp.FPriceCompositionPriority ");
Connection paramConn = null;
RowSet rs = null;
try {
rs=DbUtil.executeQuery(ctx, sqlString.toString(), new Object[] { orgunitid,materialid,customerid });
if(rs.next()){
return rs.getBigDecimal(1);
}
} catch (Exception e) {
e.printStackTrace();
} finally{
SQLUtils.cleanup(paramConn);
SQLUtils.cleanup(rs);
}
return BigDecimal.ZERO;
}