Фабричный метод против Конструктора с ServletContext в качестве параметра - PullRequest
0 голосов
/ 17 сентября 2018

Для проверки работоспособности всех наших приложений мы включаем сервлет Healthcheck в каждое приложение. Эти проверки работоспособности просто проверяют зависимости каждого приложения. Одним из этих типов зависимостей являются соединения с Sql-сервером. Для проверки соединений у нас есть метод с именем HealthCheckRunner.Result run(). (Показано в коде ниже). Метод примет URL, имя пользователя и пароль и попытается подключиться к серверу.

Этот метод работает нормально, но я обнаружил, что во всех наших приложениях я все еще повторяю много кода, чтобы извлечь URL, имя пользователя, пароль и драйвер из context.xml. Чтобы сэкономить время и повторение, я хотел бы выполнить рефакторинг с помощью другого конструктора или фабричного метода, показанного ниже в опциях 1 и 2.

Ни один из методов не кажется мне очень привлекательным. Во-первых, конструктор довольно грязный и не очень удобен для пользователя. Во-вторых, статический метод может быть трудно проверить. И наконец, они оба принимают ServletContext в качестве параметра.

Будет ли модульное тестирование статическим методом трудным? Для простоты я бы предпочел держаться подальше от PowerMock и использовать только Mockito. А также, будут ли копии ServletContext создаваться для каждого создаваемого мной SqlHealthCheck? Или все они будут использовать одну и ту же ссылку? И, поскольку я использую только несколько значений из контекста, было бы лучше создать другой класс и передать только те значения, которые мне нужны? Решения, которые я нашел, невелики, и я знаю, что должен быть лучший путь.

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 Ответ

0 голосов
/ 17 сентября 2018

Я обнаружил, что во всех наших приложениях я все еще повторяю много кода, чтобы извлечь URL, имя пользователя, пароль и драйвер из context.xml

4 строки кода - это далеко-далеко от большого количества кода. Но если вы на самом деле копируете и вставляете этот класс во все свои приложения, то вам просто не следует этого делать Создайте отдельный проект, содержащий многоразовые проверки работоспособности, такие как этот, и создайте банку, и используйте эту банку в каждом приложении, которое нуждается в проверках работоспособности.

конструктор довольно грязный и не очень удобен для пользователя

Честно говоря, это не так грязно. Но это может быть менее беспорядочным, если вы не повторяете себя, все равно инициализируете приватные поля и если вы группируете сопоставимый код вместе:

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);
}

Будет ли модульное тестирование статическим методом трудным?

Нет. ServletContext - это интерфейс. Таким образом, вы можете создать свою собственную фальшивую реализацию или использовать фальшивый фреймворк. Затем вы можете просто вызвать конструктор фабричного метода, запустить проверку работоспособности и посмотреть, вернет ли он правильное значение.

Будут ли созданы копии ServletContext для каждого созданного мной SqlHealthCheck?

Конечно нет. Java передает ссылки на объекты по значению.

было бы лучше создать другой класс и передать только те значения, которые мне нужны?

Вы могли бы сделать это, но тогда логика получения значений из контекста сервлета будет просто в другом месте, и вам придется проверить это тоже, в основном так же, как вы проверяли бы этот класс.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...