博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
浅谈 Facade 模式
阅读量:4069 次
发布时间:2019-05-25

本文共 4102 字,大约阅读时间需要 13 分钟。

Facade 模式

所谓 Facade 模式,是一个可以让事情变得有点头绪的好东西。

 

一个 Facade 肯定是一位某方面的“行家”,例如数据库操作。它对来自上层的请求屏蔽了具体的业务逻辑细节,任何程序需要对数据库进行 CRUD 操作时,只需要告诉 Facade 层“我要做什么”,而 Facade 层则知道“到哪里去做”,于是它根据请求的具体内容,调用相应的底层模块(在那里解决“到底怎样做”的问题)。进行具体的操作。当它从底层模块中取得所需的数据后,再将结果返回给上层程序。

Facade 具体应用

一个 Facade 模块可以针对一种需求,处理来自各方面的请求。例如在一个用户注册模块中,当前的需求描述为“只要用户名没有冲突,就允许注册”。因为这是一种很宽松的注册策略,所以我们不妨把相应的 Facade 类称为 LenientFacade。

这个 Facade 类首先根据用户输入的信息构造出一个 UserBean,然后检测该用户名是否已被占用。如果未被占用,就调用 DAO 执行增加记录操作,否则就抛出一个“用户名已存在”的异常。

 

 

public class LenientFacade {

    public int insertUser (ActionForm actionForm) throws ... {
        int row;
        UserForm userForm = (UserForm) actionForm;
        // 根据formBean生成UserBean
        UserBean userBean = new UserBean();
        BeanUtils.copyProperties(userBean, userForm);
        // 执行数据库插入操作,首先查看该用户是否存在。
        setUserDAO(new UserDAOibatis());
        UserBean user = dao.loadUserByUsername(userBean.getUsername());
        if (user != null) {
        // 同名用户已存在,抛出一个自定义的异常 UserAlreadyExistException。
            throw new UserAlreadyExistException("User already exist.");
            }
        row = dao.insertUser(userBean);
        return row; // 返回该用户的id(AUTO_INCREMENT)。
    }
    public void setUserDAO (UserDAO dao) {
        this.dao = dao;
    }
    private UserDAO dao;
}

 

 

在某一时期,可能因为各种政策的原因,网站需要对注册用户进行更为严格的审核。所以我们需要 Facade 具有不同的业务逻辑。不妨把负责这种更严格的注册过程的 Facade 类称为 StrictFacade。

 

StrictFacade 除了验证用户名是否可用外,还要验证用户的国籍、年龄、学历等等一大堆事情。下面是伪代码:

 

 

public class StrictFacade {

    public int insertUser (ActionForm actionForm) throws ... {
       
        ...  //和前面相同的代码
        // 执行数据库插入操作,首先查看该用户是否存在。
        setUserDAO(new UserDAOibatis());
        UserBean user = dao.loadUserByUsername(userBean.getUsername());
        if (user != null) {
        // 同名用户已存在,抛出一个自定义的异常 UserAlreadyExistException。
            throw new UserAlreadyExistException("User already exist.");
            }
        try {
            // 许多许多更严格的审查
            // 许多许多更严格的审查
        }
        catch (... ) {
            ...
            throw 五花八门的异常           
        }

        row = dao.insertUser(userBean);
        return row; // 返回该用户的id(AUTO_INCREMENT)。
    }
    public void setUserDAO (UserDAO dao) {
        this.dao = dao;
    }
    private UserDAO dao;
}

 

 

执行更加严格的注册过程,并不意味着原有的“宽松式”注册就没有用了。网站可能需要随时根据情况在两种策略之间切换。显然,每次切换注册过程就停掉服务器,重新编译 Facade 层的代码,然后再重启服务器,会让老板和所有的用户疯掉!

 

这时,使用灵活的抽象工厂模式就可以解决这个问题。

.

结合抽象工厂模式的 Facade 模式

抽象工厂模式可以根据初始参数的不同,生产出适应各种需求的具体实现类。我们可以设计两种工厂,一种是 LenientFacadeFactory,另一种是 StrictFacadeFactory,二者分别负责生产“宽松式”和“严格式”的 Facade。

 

这两个工厂都继承自他们的父类 —— 一个抽象的 FacadeFactory。这个抽象的 Factory 使用一个静态方法返回具体的 Factory 类,并且为它的子类们定义了获得Facade的get方法:public abstract Facade getFacade();,所有继承了抽象工厂的具体工厂类,都要负责为其调用者返回一个实际可用的 Facade。

 

抽象的 Facade 工厂代码如下:

 

 

public abstract class FacadeFactory {

    public static FacadeFactory getInstance(int facadeFactoryType) {
        switch (facadeFactoryType) {
            case LENIENT:
                return new LenientFacadeFactory();
            case STRICT:
                return new StrictFacadeFactory();
            default:
                return null;
        }
    }
    public abstract Facade getFacade();
    public static final int LENIENT = 0;
    public static final int STRICT = 1;
}

 

 

下面是 LenientFacadeFactory 的代码,Strict 版本的与其类似。

 

 

public class LenientFacadeFactory extends FacadeFactory {

    public Facade getFacade() {
        return new LenientFacade();
    }
}
 

 

通过这段代码,我们就可以得到本文一开始的那段代码中的 LenientFacade 了。

 

在程序(在Strut中应该是一个Action)中的具体调用方法是:

 

int facadeType = FacadeFactory.LENIENT;
FacadeFactory factory = FacadeFactory.getInstance(facadeType);
Facade facade = factory.getFacade();
try {
    row = facade.insertUser(userForm);
}
catch (...) {
    ...
}
 

 

这样做的好处很明显:Servlet (或具体为一个Action)不需要去产生出一个具体的 Facade,所有的方法调用都是建立在统一的接口之上。当我们需要一个不同类型的 Facade 的时候,只需要调整上面代码中第一行的 facadeType 变量,就能产生出相应的 Facade。因此实现了 Controller 层和 Model 层的松耦合

 

.

通过配置初始化参数实现“更松的”耦合

首先声明!这并不是一个很好解决方案,这仅仅是为了复习巩固所学的知识而自创的办法。

 

上面的代码中,问题没有得到根本的解决,因为那个代表着 facade 类型的 facadeType 变量仍旧被硬编码在程序中了。不过这个问题并不难解决,只要考虑到 ActionServlet 的本质是一个 Servlet,我们就可以通过为该 Servlet 设置一个初始化参数解决它。

 

可以在 web.xml 配置描述符中,为 ActionServlet 增加一个初始化参数:

 

<servlet>
    <servlet-name>action</servlet-name>
    <servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
    <init-param>
        <param-name>config</param-name>
        <param-value>/WEB-INF/struts-config.xml</param-value>
    </init-param>
    <init-param>
        <param-name>facadeType</param-name>
        <param-value>LENIENT</param-value>
    </init-param>
    <load-on-startup>0</load-on-startup>
</servlet>
 

然后,我们在程序中就可以读取这个初始化参数,进而得到适用的 FacadeFactory 了。

 

 String facadeType = this.getServlet().getInitParameter("facadeType");
 

 

转载地址:http://qwaji.baihongyu.com/

你可能感兴趣的文章
Yotta企业云盘助力科技行业创高峰
查看>>
Yotta企业云盘更好地为教育行业服务
查看>>
Yotta企业云盘怎么帮助到能源化工行业
查看>>
企业云盘如何助力商业新发展
查看>>
医疗行业运用企业云盘可以带来什么样的提升
查看>>
能源化工要怎么管控核心数据
查看>>
媒体广告业如何运用云盘提升效率
查看>>
企业如何运用企业云盘进行数字化转型-实现新发展
查看>>
司法如何运用电子智能化加快现代化建设
查看>>
iSecret&nbsp;1.1&nbsp;正在审核中
查看>>
IOS开发的开源库
查看>>
IOS开发的开源库
查看>>
Jenkins - sonarqube 代码审查
查看>>
Jenkins + Docker + SpringCloud 微服务持续集成(一)
查看>>
Jenkins + Docker + SpringCloud 微服务持续集成 - 单机部署(二)
查看>>
Jenkins + Docker + SpringCloud 微服务持续集成 - 高可用集群部署(三)
查看>>
Golang struct 指针引用用法(声明入门篇)
查看>>
Linux 粘滞位 suid sgid
查看>>
C#控件集DotNetBar安装及破解
查看>>
Winform皮肤控件IrisSkin4.dll使用
查看>>