代码之家  ›  专栏  ›  技术社区  ›  navig8tr

以ServletContext为参数的工厂方法与构造函数

  •  0
  • navig8tr  · 技术社区  · 6 年前

    为了测试所有应用程序的运行状况,我们在每个应用程序中都包含一个Healthcheck servlet。这些健康检查只是测试每个应用程序的依赖关系。其中一种依赖类型是Sql server连接。为了测试连接,我们有一个名为 HealthCheckRunner.Result run() . (如下代码所示)。此方法将获取url、用户名和密码,并尝试连接到服务器。

    这个方法工作得很好,但是我发现在我们所有的应用程序中,我仍然在重复很多代码,以便从context.xml中检索url、用户名、密码和驱动程序。为了节省时间和重复,我希望使用另一个构造函数或工厂方法进行重构,如下选项1和2所示。

    不过,这两种方法对我来说都不是很有吸引力。首先,构造函数相当混乱,看起来不太友好。其次,静态方法可能难以测试。最后,它们都将ServletContext作为参数。

    静态方法的单元测试会很困难吗?为了简单起见,我宁愿远离PowerMock,只使用Mockito。而且,是否会为我创建的每个SqlHealthCheck创建ServletContext的副本?或者他们都会使用相同的参考?而且,由于我只使用上下文中的一些值,所以创建另一个类并只传递我需要的值会更好吗?我提出的解决办法并不好,我知道一定有更好的办法。

    public class SqlHealthCheck extends HealthCheck {
    private String url;
    private String username;
    private String password;
    private String driver;
    
    // Option 1: Constructor with ServletContext as parameter.
    public SqlHealthCheck (ServletContext context, String prefix) {
        this.url = context.getInitParameter(prefix + ".db-url");
        this.username = context.getInitParameter(prefix + ".db-user");
        this.password = context.getInitParameter(prefix + ".db-passwd");
        setType("sqlcheck");
        setDescription("SQL database check: " + this.url);
        this.decodePassword();
        this.setDriver(context.getInitParameter(prefix + ".db-driver"));
    }
    
    // Option 2: Static factory method with ServletContext as parameter
    public static HealthCheck createHealthCheck(ServletContext context, String prefix) {
        String dbUrl = context.getInitParameter(prefix + ".db-url");
        String username = context.getInitParameter(prefix + ".db-user");
        String password =  context.getInitParameter(prefix + ".db-passwd");
        String sqlDriver = context.getInitParameter(prefix + ".db-driver");
    
        SqlHealthCheck healthCheck = new SqlHealthCheck("SQL database check: " + dbUrl, dbUrl, username, password);
        healthCheck.decodePassword();
        healthCheck.setDriver(sqlDriver);
    
        return healthCheck;
    }
    
    public HealthCheckRunner.Result run() {
        Connection connection = null;
        Statement statement = null;
    
        try {
            if (driver != null) { Class.forName(driver); }
            connection = DriverManager.getConnection(this.url, this.username, this.password);
            statement = connection.createStatement();
            statement.executeQuery("SELECT 1");
            return HealthCheckRunner.Result.Pass;
        } catch (SQLException | ClassNotFoundException  ex) {
            setMessage(ex.getMessage());
            return getFailureResult();
        }
        finally {
            try {
                if (statement != null) {statement.close();}
                if (connection != null) {connection.close();}
            } catch (SQLException ex) {
                setMessage(ex.getMessage());
            }
        }
    }
    
    public void decodePassword() {
        // Decode password
        try {
            if (password != null && !"".equals(password)) {
                password = new String(Base64.decode(password.getBytes()));
            }
        } catch (Exception e) {
            if (e.getMessage()!=null) {
                this.setMessage(e.getMessage());}
        }
    }
    

    }

    1 回复  |  直到 6 年前
        1
  •  0
  •   JB Nizet    6 年前

    我发现在我们所有的应用程序中,我仍然在重复很多代码,从context.xml中检索url、用户名、密码和驱动程序

    4行代码远远不是很多代码。但是如果你真的要在你的所有应用中复制和粘贴这个类,那么你不应该这样做。创建一个单独的项目,包含像这样的可重用健康检查,生成一个jar,并在每个需要健康检查的应用中使用这个jar。

    构造器很乱,看起来不太友好

    坦白说,没那么乱。但是,如果不重复自己的操作,以相同的方式初始化私有字段,并且将可比较的代码组合在一起,那么就不会那么混乱:

    public SqlHealthCheck (ServletContext context, String prefix) {
        this.url = getParam(context, prefix, "db-url");
        this.username = getParam(context, prefix, "db-user");
        this.password = getParam(context, prefix, "db-password");
        this.driver = getParam(context, prefix, "db-driver");
    
        this.decodePassword();
    
        setType("sqlcheck");
        setDescription("SQL database check: " + this.url);
    }
    

    静态方法的单元测试会很困难吗?

    会为我创建的每个SqlHealthCheck创建ServletContext的副本吗?

    当然不是。Java通行证 参考文献 以值表示的对象。

    您可以这样做,但是从servlet上下文中获取值的逻辑将在其他地方,您还必须测试它,基本上与测试这个类的方式相同。