В этой статье мы обсудим пункт 1 книги «Эффективная Java», рекомендованный Джошуа Блохом.

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

Публичный конструктор

Когда у класса есть общедоступный конструктор, конструктор доступен из любого класса в коде. Конструктор — это первый метод, который вызывается, когда клиент создает новый экземпляр класса.

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

Однако есть и некоторые недостатки использования общедоступных конструкторов. Они не могут возвращать значение, отличное от экземпляра объекта, не имеют отдельного имени, которое идентифицирует различные процессы построения, и на них могут распространяться ограничения по перегрузке. Кроме того, наличие конструктора со многими параметрами может привести к сложности, что затруднит чтение и поддержку кода. [1] [2]

DatabaseConnection.java


public class DatabaseConnection {

    private final String connectionString;
    private Integer poolSize = 8;

    public DatabaseConnection(String connectionString) {
        this.connectionString = connectionString;
    }

    public DatabaseConnection(String connectionString, Integer poolSize) {
        this.connectionString = connectionString;
        this.poolSize = poolSize;
    }

    public void connect() {
        System.out.println("---> connected successfully! \n connection-string: " + connectionString + "\n poolSize: " + poolSize);

    }

}

Test.java

public class Test {
    public static void main(String[] args) {
        DatabaseConnection databaseConnection = new DatabaseConnection("jdbc:postgresql://localhost:5432/test");
        databaseConnection.connect();

        DatabaseConnection databaseConnectionWithPool = new DatabaseConnection("jdbc:postgresql://localhost:5432/test", 10);
        databaseConnectionWithPool.connect();
    }
}

Цель кода — создать новый экземпляр класса DatabaseConnection, который имеет два конструктора. Один конструктор принимает только строку подключения и использует размер пула по умолчанию, а другой конструктор позволяет указать как строку подключения, так и пользовательский размер пула. Предоставляя эти два конструктора, класс предлагает гибкость в установлении соединения, используя параметры по умолчанию или позволяя настройку по мере необходимости.

Как видите, использование общедоступных конструкторов обеспечивает простоту, непротиворечивость и обнаруживаемость. Базовый код становится легко понять при использовании общедоступных конструкторов, поскольку они делают процесс создания новых экземпляров довольно простым и понятным. Разработчики, как правило, знакомы с использованием конструкторов для создания экземпляров объектов, и этот подход является обычной практикой в ​​мире программирования.

Статические фабричные методы

Статические фабричные методы обеспечивают более гибкие конструкции вместо общедоступных конструкторов.

DatabaseConnection.java

import java.util.Objects;

public class DatabaseConnection {

    private final String connectionString;
    private Integer poolSize = 8;

    private static DatabaseConnection databaseConnection = null;

    private DatabaseConnection(String connectionString) {
        this.connectionString = connectionString;
    }

    private DatabaseConnection(String connectionString, Integer poolSize) {
        this.connectionString = connectionString;
        this.poolSize = poolSize;
    }

    public void connect() {
        System.out.println("---> connected successfully! \n connection-string: " + connectionString + "\n poolSize: " + poolSize);
    }

    /**
     * This method provide singleton instance
     * @param connectionString
     * @return
     */
    public static DatabaseConnection getInstance(String connectionString) {
        if (Objects.isNull(databaseConnection)) {
            databaseConnection = new DatabaseConnection(connectionString);
        }
        return databaseConnection;
    }

    public static DatabaseConnection getInstanceWithPoolSize(String connectionString, Integer poolSize) {
        return new DatabaseConnection(connectionString, poolSize);
    }

}

Test.java

public class Test {
    public static void main(String[] args) {
        DatabaseConnection databaseConnection = DatabaseConnection.getInstance("jdbc:postgresql://localhost:5432/test");
        databaseConnection.connect();

        DatabaseConnection databaseConnectionWithPool = DatabaseConnection.getInstanceWithPoolSize("jdbc:postgresql://localhost:5432/test", 10);
        databaseConnectionWithPool.connect();

    }
}

Цель этого кода — создать новый экземпляр из класса DatabaseConnection, который использует статические фабричные методы. Метод getInstance() позволяет создать один экземпляр, используя строку подключения и размер пула по умолчанию. Метод getInstanceWithPoolSize() упрощает создание экземпляра как со строкой подключения, так и с указанным размером пула. Эти фабричные методы обеспечивают структурированный способ создания экземпляров с различными конфигурациями в зависимости от требований.

Как видите, статические фабричные методы обеспечивают читабельность, гибкость, ясность, управление созданием, описательное имя метода и потенциальное повторное использование.

Инкапсуляция логики в статический метод позволяет лучше контролировать, как и когда создаются объекты. Это открывает возможности для реализации шаблонов проектирования, таких как синглтон, добавление проверок и т. д. В отличие от конструкторов, статические фабричные методы имеют определенные имена, предоставляя больше информации о том, что они делают. Эта функция повышает ясность и удобочитаемость, делая код более понятным. Эта функция напрямую повышает четкость и читабельность. Более того, статические фабричные методы обеспечивают потенциальное повторное использование, когда нам нужно существующее соединение или подтип, который мы можем вернуть.

Преимущества статических фабричных методов, упомянутых Джошуа Блохом в «Эффективной JAVA».

1. Статические фабричные методы имеют имена, в отличие от конструкторов. [2]

повысить читабельность и ясность.

2. Статические фабричные методы не требуются для создания нового экземпляра при каждом вызове. [2]

Эта функция обеспечивает одноэлементное использование. И это может значительно улучшить производительность. Избегайте ненужных повторяющихся объектов.

3. Они могут возвращать объект любого подтипа возвращаемого типа. [2]

Эта функция дает нам гибкость и возможность повторного использования

4. Статические фабрики — это объекты, в которых класс возвращаемого объекта может меняться от вызова к вызову в зависимости от входных параметров. [2]

Эта функция обеспечивает гибкость и инкапсуляцию логики

Основное ограничение предоставления только статических фабричных методов заключается в том, что классы без общедоступных или защищенных конструкторов не могут быть подклассами.

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

Наконец, статические фабричные методы могут уменьшить и испортить дизайн ООП.

ПРИМЕЧАНИЕ. Статические фабричные методы — это не то же самое, что шаблон фабричного метода.

Заключение

Разумно используйте статические фабричные методы. Для конкретных требований статические фабричные методы могут быть очень полезными. Мы узнали, что они повышают гибкость, удобочитаемость, ясность, возможность повторного использования и производительность. Однако у них также могут быть недостатки, которые потенциально уменьшают или подрывают принципы проектирования ООП. Это полностью зависит от требований.

Рекомендации

https://www.quora.com/What-are-the-disadvantages-of-Java-constructors [1]

Блох, Джошуа. Эффективное программирование на Java, третье издание. [2]

Роберт С. Мартин, Чистый код. [3]