欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Seam 使用心得(二)接口实现

程序员文章站 2022-07-15 19:33:59
...
Java里提倡使用面向接口编程以减弱组件之间的耦合,在上一篇里已经定义了
@Local
public interface Register {
	public String register();
}

这一节里面将实现这个接口来通过测试。

现在应用系统有很多良好的分层方法,但是我还是喜欢Evans的领域驱动设计的方法。
1,视图层将使用RichFaces。
2,使用无状态SessionBean作为Facade对领域逻辑进行封装。
3,领域层实现核心业务逻辑,主要包括有实体,值对象,仓库,服务等。

源文件目录结构大致如下:
src
  |----action  (SLSB)
  |----model  (Service, Entity, Repository, Vo)
  |----test
view
  |----css
  |----js
……

经过简单的分析model层向facade暴露UserView接口
package org.qpt.domain.user;
import org.qpt.domain.user.User.RegState;
/**
 * 用户接口
 * @author xiaoqiu
 */
public interface UserView {
    String getConfirmPwd();
    String getEmail();
    String getName();
    String getPwd();
    /**
     * 注册一个新用户
     * @param ur 用户仓库接口
     * @return 注册结果状态码(共5种状态,上一节已经提到过)
     */
    RegState register(UserRepository ur);
    void setConfirmPwd(String confirmPwd);
    void setEmail(String email);
    void setName(String name);
    void setPwd(String pwd);
}


注:因为EJB2严重的依赖于容器的基础设施,如果在实体bean中包含业务逻辑,将很难进行测试,所以实体bean只能作为数据容器,而背离面向对象的分析和设计方法。但是EJB3已经可以对实体进行透明持久化,并且很容易在容器外进行测试,所以就用User实体实现UserView接口,这里用户注册的并没有跨越多个实体,只涉及用户实体,所以包含了业务方法register的User类代码如下:

/**
 * 用户实体
 * 
 * @author xiaoqiu
 */
@Entity
@Name("user")
@Scope(SESSION)
@Table(name = "users")
public class User implements Serializable, UserView {

    private static final long serialVersionUID = 3836699581343986461L;
    @Id
    @NotNull
    @Length(min = 6, max = 15)
    private String name;
    @NotNull
    @Length(min = 6, max = 15)
    private String pwd;
    @Transient
    private String confirmPwd;
    @NotNull
    @Email
    private String email;
    @Transient
    private String confirmEmail;

    public enum RegState {
        EXISTS_EMAIL, EXISTS_NAME, PWD_NOT_EQ_IN_TWICE, EMAIL_NOT_EQ_IN_TWICE, SUCCESS
    }

    @Override
    public RegState register(UserRepository ur) {
        if (!pwd.equals(confirmPwd)) {
            return PWD_NOT_EQ_IN_TWICE;
        } else if (!email.equals(confirmEmail)) {
            return EMAIL_NOT_EQ_IN_TWICE;
        } else if (ur.findByProperty("name", name)) {
            return EXISTS_NAME;
        } else if (ur.findByProperty("email", email)) {
            return EXISTS_EMAIL;
        } else {
            ur.save(this);
            return SUCCESS;
        }
    }

……
get&set method
}

考虑到视图层RichFaces和hibernate验证结合可以进行很简单的零脚本ajax验证,所以本例子里面使用了hibernate验证。
由于register方法需要用的UserRepository来访问数据库,所以现在来看一下UserRepository接口。通过隔离基础设施,这就是GOF设计模式中著名的Strategy模式,由于Java语言想要在实体中注入UserRepository在技术上存在困难,所以把UserRepository作为方法的参数传递我个人认为也是一种较为简洁的方法,而且通过接口的隔离不会和基础设施产生强烈的耦合,UserRepository代码如下:

/**
 * 用户仓库接口
 * @author xiaoqiu
 */
@Local
public interface UserRepository {
    void save(User user);
    User findByName(String name);
    List<User> findAllUser();
    /**
     * 找出该属性是否存在当前值
     * @param name 属性名
     * @param value 属性值
     * @return 该属性存在这个值返回true,否则返回false
     */
    boolean findByProperty(String name, String value);
}


在本例里面UserRepository的实现使用的JPA,这个接口的实现相当容易所以它的实现类UserRepositoryJPAImp代码就不贴出来了,可以直接看提供的源码。

model层的接口已经实现完毕,接下来实现facade,代码如下:
@Stateless
@Name("register")
public class RegisterBean implements Register {

    @Logger
    private Log log;
    @In
    FacesMessages facesMessages;
    @In
    private UserView user;
    @In("userRepository")
    UserRepository ur;

    public String register() {
        RegState state = user.register(ur);
        switch (state) {
            case PWD_NOT_EQ_IN_TWICE:
                log.info("the register state is " + state);
                facesMessages.addFromResourceBundle("myapp.register.pwd.noteq");
                break;
            case EMAIL_NOT_EQ_IN_TWICE:
                log.info("the register state is " + state);
                facesMessages.addFromResourceBundle("myapp.register.email.noteq");
                break;
            case EXISTS_NAME:
                log.info("the register state is " + state);
                facesMessages.addFromResourceBundle("myapp.register.name.exists");
                break;
            case EXISTS_EMAIL:
                log.info("the register state is " + state);
                facesMessages.addFromResourceBundle("myapp.register.email.exists");
                break;
            case SUCCESS:
                log.info("register a new user #{user.name}" + "\nthe register state is " + state);
                facesMessages.addFromResourceBundle("myapp.register.success");
                break;
        }
        return state.toString();
    }
}

这里facade是很薄的一层,不能包含任何业务逻辑,它主要根据model返回的状态是向显示层发送信息,使用facesMessages组件就可以很容易向Richfaces发送ajax验证的响应信息,并且事务处理统一在facade层进行,由于使用EJB,所以就可以使用容器提供的事务管理。

上述类的结构如下图所示:
facade: Register-->RegisterBean-->UserView&UserRepository
domain:UserView-->User(Eneity)<-->UserRepository-->UserRepositoryJPAImp
至此为止,注册用户所需要的全部接口都已经实现,现在就可以用seam test命令来运行上一节写好的测试代码了。