Fluent API β€” это ΡΡ‚ΠΈΠ»ΡŒ проСктирования API, Π² ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠΌ ΠΌΠ΅Ρ‚ΠΎΠ΄Ρ‹ Π²ΠΎΠ·Π²Ρ€Π°Ρ‰Π°ΡŽΡ‚ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚, ΠΊ ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠΌΡƒ ΠΎΠ½ΠΈ ΠΏΡ€ΠΈΠ½Π°Π΄Π»Π΅ΠΆΠ°Ρ‚, позволяя Π²Ρ‹Π·Ρ‹Π²Π°Ρ‚ΡŒ ΠΌΠ΅Ρ‚ΠΎΠ΄Ρ‹ Ρ†Π΅ΠΏΠΎΡ‡ΠΊΠΎΠΉ (chaining).

ΠžΡΠ½ΠΎΠ²Π½Ρ‹Π΅ ΠΊΠΎΠ½Ρ†Π΅ΠΏΡ†ΠΈΠΈ

  • Method Chaining. Fluent API позволяСт Π²Ρ‹Π·Ρ‹Π²Π°Ρ‚ΡŒ ΠΌΠ΅Ρ‚ΠΎΠ΄Ρ‹ ΠΎΠ΄ΠΈΠ½ Π·Π° Π΄Ρ€ΡƒΠ³ΠΈΠΌ, Ρ‡Ρ‚ΠΎ ΡƒΠΌΠ΅Π½ΡŒΡˆΠ°Π΅Ρ‚ количСство ΠΏΡ€ΠΎΠΌΠ΅ΠΆΡƒΡ‚ΠΎΡ‡Π½Ρ‹Ρ… ΠΏΠ΅Ρ€Π΅ΠΌΠ΅Π½Π½Ρ‹Ρ… ΠΈ ΡƒΠ»ΡƒΡ‡ΡˆΠ°Π΅Ρ‚ Ρ‡ΠΈΡ‚Π°Π΅ΠΌΠΎΡΡ‚ΡŒ.
  • Π‘Π°ΠΌΠΎΠΎΠΏΠΈΡΡ‹Π²Π°ΡŽΡ‰ΠΈΠΉΡΡ ΠΊΠΎΠ΄. ИспользованиС Ρ†Π΅ΠΏΠΎΡ‡ΠΊΠΈ ΠΌΠ΅Ρ‚ΠΎΠ΄ΠΎΠ² Π΄Π΅Π»Π°Π΅Ρ‚ ΠΊΠΎΠ΄ Π±ΠΎΠ»Π΅Π΅ понятным ΠΈ Π»ΠΎΠ³ΠΈΡ‡Π½Ρ‹ΠΌ, приблиТая Π΅Π³ΠΎ ΠΊ СстСствСнному языку.

Π“Π΄Π΅ встрСчаСтся?

  • Π€Ρ€Π΅ΠΉΠΌΠ²ΠΎΡ€ΠΊΠΈ с Ρ€Π΅Π°ΠΊΡ‚ΠΈΠ²Π½Ρ‹ΠΌ ΠΏΠΎΠ΄Ρ…ΠΎΠ΄ΠΎΠΌ.
  • Java Stream
  • Π‘ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠΈ для Ρ€Π°Π±ΠΎΡ‚Ρ‹ с Π±Π°Π·Π°ΠΌΠΈ Π΄Π°Π½Π½Ρ‹Ρ…. Π’Π°ΠΊΠΈΠ΅ Ρ„Ρ€Π΅ΠΉΠΌΠ²ΠΎΡ€ΠΊΠΈ, ΠΊΠ°ΠΊ JPA ΠΈΠ»ΠΈ Hibernate, ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΡŽΡ‚ Fluent API для создания запросов. НапримСр, запросы ΠΌΠΎΠ³ΡƒΡ‚ Π²Ρ‹Π³Π»ΡΠ΄Π΅Ρ‚ΡŒ Ρ‚Π°ΠΊ
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<Car> query = builder.createQuery(Car.class);
query.select(query.from(Car.class))
     .where(builder.equal(root.get("color"), "Red"));
  • Настройка ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠ². Fluent API часто ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ Π² Builder Pattern, Π³Π΄Π΅ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ строится поэтапно Ρ‡Π΅Ρ€Π΅Π· Ρ†Π΅ΠΏΠΎΡ‡ΠΊΡƒ ΠΌΠ΅Ρ‚ΠΎΠ΄ΠΎΠ².
  • ΠšΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€Π°Ρ†ΠΈΡ. НапримСр Spring Security, Kafka Streams
  • Π€Ρ€Π΅ΠΉΠΌΠ²ΠΎΡ€ΠΊΠΈ для тСстирования. НапримСр, Π² JUnit ΠΈΠ»ΠΈ AssertJ ΠΌΠΎΠΆΠ½ΠΎ ΡΡ‚Ρ€ΠΎΠΈΡ‚ΡŒ Ρ†Π΅ΠΏΠΎΡ‡ΠΊΠΈ ΡƒΡ‚Π²Π΅Ρ€ΠΆΠ΄Π΅Π½ΠΈΠΉ:

Fluent API часто ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ для построСния спСцифичСских языков (DSL), ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ ΠΈΠΌΠΈΡ‚ΠΈΡ€ΡƒΡŽΡ‚ чСловСчСский язык ΠΈ Π΄Π΅Π»Π°ΡŽΡ‚ ΠΊΠΎΠ΄ максимально ΡΠ°ΠΌΠΎΠΎΠΏΠΈΡΠ°Ρ‚Π΅Π»ΡŒΠ½Ρ‹ΠΌ.

ΠŸΡ€ΠΈΠΌΠ΅Ρ€ ΠΈΠΌΠΏΠ΅Ρ€Π°Ρ‚ΠΈΠ²Π½ΠΎΠ³ΠΎ ΠΊΠΎΠ΄Π°

Instant start = Instant.now();
Duration timeout = Duration.ofSeconds(10);
do {
	Thread.sleep(200);
	var entity = repo.get("id");
	if ("EXPECTED".equals(entity.status)) {
		return;
	}
} while (Instant.now().isBefore(start.plus(timeout)));
throw new AssertionError("Status was not updated to EXPECTED");

И Π°Π½Π°Π»ΠΎΠ³ΠΈΡ‡Π½Ρ‹ΠΉ Π² стилС Fluent API

Awaitility.await("Entity status should be updated to EXPECTED")
	.atMost(Duration.ofSeconds(10))
	.pollDelay(Duration.ofMillis(200))
	.until(() -> "EXPECTED".equals(repo.get("id").status));

ΠŸΡ€ΠΈΠ΅ΠΌΡ‹ ΠΈ ΠΏΠΎΠ΄Ρ…ΠΎΠ΄Ρ‹

Method chaining

Method chaining β€” это Ρ‚Π΅Ρ…Π½ΠΈΠΊΠ°, ΠΏΡ€ΠΈ ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠΉ ΠΌΠ΅Ρ‚ΠΎΠ΄Ρ‹ Π²ΠΎΠ·Π²Ρ€Π°Ρ‰Π°ΡŽΡ‚ Ρ‚Π΅ΠΊΡƒΡ‰ΠΈΠΉ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ (ΠΎΠ±Ρ‹Ρ‡Π½ΠΎ Ρ‡Π΅Ρ€Π΅Π· this), позволяя Π²Ρ‹Π·Ρ‹Π²Π°Ρ‚ΡŒ нСсколько ΠΌΠ΅Ρ‚ΠΎΠ΄ΠΎΠ² ΠΏΠΎΡΠ»Π΅Π΄ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΠ½ΠΎ Π² ΠΎΠ΄Π½ΠΎΠΉ строкС.

public class Car {
    private String engine;
    private int doors;
 
    public Car setEngine(String engine) {
        this.engine = engine;
        return this;  // Π’ΠΎΠ·Π²Ρ€Π°Ρ‰Π°Π΅ΠΌ Ρ‚Π΅ΠΊΡƒΡ‰ΠΈΠΉ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚
    }
 
    public Car setDoors(int doors) {
        this.doors = doors;
        return this;
    }
}

Π‘ΠΌΠ΅Π½Π° контСкста

Π‘ ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ method chaining

ΠŸΡ€Π΅Π΄ΡΡ‚Π°Π²ΠΈΠΌ, Ρ‡Ρ‚ΠΎ ΠΌΡ‹ настраиваСм сСрвСрноС ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅ с нСсколькими аспСктами: базовая настройка, настройка бСзопасности, логирования ΠΈ Ρ‚.Π΄. Π—Π΄Π΅ΡΡŒ ΠΊΠ°ΠΆΠ΄Ρ‹ΠΉ Π²Ρ‹Π·ΠΎΠ² ΠΌΠ΅Ρ‚ΠΎΠ΄Π° ΠΏΠ΅Ρ€Π΅ΠΊΠ»ΡŽΡ‡Π°Π΅Ρ‚ нас Π½Π° Π½ΠΎΠ²Ρ‹ΠΉ β€œΠΊΠΎΠ½Ρ‚Π΅ΠΊΡΡ‚β€, Π³Π΄Π΅ ΠΌΡ‹ ΠΏΡ€ΠΎΠ΄ΠΎΠ»ΠΆΠ°Π΅ΠΌ Π½Π°ΡΡ‚Ρ€Π°ΠΈΠ²Π°Ρ‚ΡŒ ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅, Π½ΠΎ Π² Ρ€Π°ΠΌΠΊΠ°Ρ… Π΄Ρ€ΡƒΠ³ΠΎΠΉ области (Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€, с бСзопасности ΠΏΠ΅Ρ€Π΅ΠΊΠ»ΡŽΡ‡Π°Π΅ΠΌΡΡ Π½Π° Π»ΠΎΠ³ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅).

public class ServerConfig {
 
    public ServerConfig http() {
        System.out.println("HTTP basic configuration");
        return this; // Π’ΠΎΠ·Π²Ρ€Π°Ρ‰Π°Π΅ΠΌ Ρ‚ΠΎΡ‚ ΠΆΠ΅ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ для продолТСния Ρ†Π΅ΠΏΠΎΡ‡ΠΊΠΈ
    }
 
    public ServerConfig security() {
        System.out.println("Security configuration");
        return this; // ΠŸΠ΅Ρ€Π΅ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠ΅ Π½Π° контСкст бСзопасности
    }
 
    public ServerConfig authorizeRequests() {
        System.out.println("Authorization configuration");
        return this; // ΠŸΠ΅Ρ€Π΅ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠ΅ Π½Π° настройку Π°Π²Ρ‚ΠΎΡ€ΠΈΠ·Π°Ρ†ΠΈΠΈ запросов
    }
 
    public ServerConfig requestMatchers(String pattern) {
        System.out.println("Configuring request matchers for: " + pattern);
        return this; // ΠŸΡ€ΠΎΠ΄ΠΎΠ»ΠΆΠ΅Π½ΠΈΠ΅ Ρ€Π°Π±ΠΎΡ‚Ρ‹ Π² контСкстС Π°Π²Ρ‚ΠΎΡ€ΠΈΠ·Π°Ρ†ΠΈΠΈ
    }
 
    public ServerConfig csrf() {
        System.out.println("CSRF protection disabled");
        return this; // ΠŸΠ΅Ρ€Π΅ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠ΅ Π½Π° настройку Π·Π°Ρ‰ΠΈΡ‚Ρ‹ CSRF
    }
 
    public ServerConfig exceptionHandling() {
        System.out.println("Exception handling configuration");
        return this; // ΠŸΠ΅Ρ€Π΅ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠ΅ Π½Π° ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΡƒ ΠΈΡΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠΉ
    }
 
    public Server build() {
        System.out.println("Server is configured and built");
        return new Server();
    }
}
 
class Server {
    // Π˜ΠΌΠΈΡ‚Π°Ρ†ΠΈΡ Π·Π°ΠΏΡƒΡ‰Π΅Π½Π½ΠΎΠ³ΠΎ сСрвСра
}
Server server = new ServerConfig()
    .http()                     // ΠšΠΎΠ½Ρ‚Π΅ΠΊΡΡ‚ Π±Π°Π·ΠΎΠ²ΠΎΠΉ настройки HTTP
    .security()                  // ΠŸΠ΅Ρ€Π΅ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠ΅ Π½Π° контСкст бСзопасности
    .authorizeRequests()         // Настройка Π°Π²Ρ‚ΠΎΡ€ΠΈΠ·Π°Ρ†ΠΈΠΈ запросов
        .requestMatchers("/")    // Настройка доступа для Π³Π»Π°Π²Π½ΠΎΠΉ страницы
        .requestMatchers("/api") // Настройка доступа ΠΊ API
    .csrf()                      // ΠžΡ‚ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠ΅ CSRF
    .exceptionHandling()         // Настройка ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠΈ ΠΈΡΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠΉ
    .build();                    // Π—Π°Π²Π΅Ρ€ΡˆΠ°Π΅ΠΌ ΠΊΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€Π°Ρ†ΠΈΡŽ ΠΈ запускаСм сСрвСр

Π‘ ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ лямбда-Π²Ρ‹Ρ€Π°ΠΆΠ΅Π½ΠΈΠΉ

public class ServerConfig {
 
    public ServerConfig http(Consumer<HttpConfig> httpConfig) {
        System.out.println("Entering HTTP configuration context");
        httpConfig.accept(new HttpConfig());
        return this; // Π’ΠΎΠ·Π²Ρ€Π°Ρ‰Π°Π΅ΠΌ Ρ‚ΠΎΡ‚ ΠΆΠ΅ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ для дальнСйшСй ΠΊΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€Π°Ρ†ΠΈΠΈ
    }
 
    public ServerConfig security(Consumer<SecurityConfig> securityConfig) {
        System.out.println("Entering Security configuration context");
        securityConfig.accept(new SecurityConfig());
        return this; // ΠŸΠ΅Ρ€Π΅ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠ΅ Π½Π° контСкст бСзопасности
    }
 
    public ServerConfig logging(Consumer<LoggingConfig> loggingConfig) {
        System.out.println("Entering Logging configuration context");
        loggingConfig.accept(new LoggingConfig());
        return this; // ΠŸΠ΅Ρ€Π΅ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠ΅ Π½Π° контСкст логирования
    }
 
    public Server build() {
        System.out.println("Server is configured and built");
        return new Server();  // Π€ΠΈΠ½Π°Π»ΡŒΠ½Ρ‹ΠΉ этап β€” запуск сСрвСра
    }
 
    // Π’Π»ΠΎΠΆΠ΅Π½Π½Ρ‹Π΅ классы ΠΊΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€Π°Ρ†ΠΈΠΉ для Ρ€Π°Π·Π½Ρ‹Ρ… контСкстов
    public static class HttpConfig {
        public HttpConfig enableHttp2() {
            System.out.println("HTTP/2 enabled");
            return this;
        }
 
        public HttpConfig port(int port) {
            System.out.println("Server will listen on port: " + port);
            return this;
        }
    }
 
    public static class SecurityConfig {
        public SecurityConfig enableTLS() {
            System.out.println("TLS enabled");
            return this;
        }
 
        public SecurityConfig authorizeRequests(Consumer<RequestAuthorization> authorizationConfig) {
            System.out.println("Authorizing requests...");
            authorizationConfig.accept(new RequestAuthorization());
            return this;
        }
    }
 
    public static class LoggingConfig {
        public LoggingConfig level(String level) {
            System.out.println("Logging level set to: " + level);
            return this;
        }
    }
 
    public static class RequestAuthorization {
        public RequestAuthorization permitAll() {
            System.out.println("All requests are permitted");
            return this;
        }
 
        public RequestAuthorization authenticated() {
            System.out.println("Authenticated requests only");
            return this;
        }
    }
}
Server server = new ServerConfig()
    .http(http -> http.enableHttp2().port(8080))  // Настройка HTTP с использованиСм лямбда-выраТСния
    .security(security -> security
        .enableTLS()  // Настройка бСзопасности
        .authorizeRequests(auth -> auth.authenticated()))  // Π‘ΠΌΠ΅Π½Π° контСкста Π²Π½ΡƒΡ‚Ρ€ΠΈ лямбды
    .logging(log -> log.level("INFO"))  // Настройка логирования с ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ лямбда
    .build();  // Ѐинальная сборка сСрвСра

Step building

ΠŸΠΎΠ·Π²ΠΎΠ»ΡΠ΅Ρ‚ ΠΎΡ€Π³Π°Π½ΠΈΠ·ΠΎΠ²Π°Ρ‚ΡŒ процСсс создания ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠ² ΠΈΠ»ΠΈ выполнСния ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠΉ Ρ‡Π΅Ρ€Π΅Π· строго упорядочСнныС шаги. Π₯отя этот ΠΏΠΎΠ΄Ρ…ΠΎΠ΄ часто ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ Π² Builder Pattern, ΠΎΠ½ ΠΏΡ€ΠΈΠΌΠ΅Π½ΠΈΠΌ ΠΈ Π² Π΄Ρ€ΡƒΠ³ΠΈΡ… контСкстах, Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€, ΠΏΡ€ΠΈ Π²Ρ‹Π·ΠΎΠ²Π΅ API, ΠΊΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€Π°Ρ†ΠΈΠΈ слоТных процСссов, построСнии запросов ΠΈ Π΄Π°ΠΆΠ΅ Π² Ρ€Π°Π±ΠΎΡ‡ΠΈΡ… процСссах (workflow).

ΠžΡΠ½ΠΎΠ²Π½Ρ‹Π΅ ΠΊΠΎΠ½Ρ†Π΅ΠΏΡ†ΠΈΠΈ

  • УпорядочСнныС шаги. ΠŸΡ€ΠΎΡ†Π΅ΡΡ выполнСния ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠΈ ΠΈΠ»ΠΈ создания ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Π° Ρ€Π°Π·Π΄Π΅Π»Π΅Π½ Π½Π° нСсколько этапов (шагов), ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ Π΄ΠΎΠ»ΠΆΠ½Ρ‹ Π²Ρ‹ΠΏΠΎΠ»Π½ΡΡ‚ΡŒΡΡ Π² ΠΎΠΏΡ€Π΅Π΄Π΅Π»Ρ‘Π½Π½ΠΎΠΉ ΠΏΠΎΡΠ»Π΅Π΄ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΠ½ΠΎΡΡ‚ΠΈ. ΠšΠ°ΠΆΠ΄Ρ‹ΠΉ шаг ΠΌΠΎΠΆΠ΅Ρ‚ ΠΏΡ€Π΅Π΄ΡΡ‚Π°Π²Π»ΡΡ‚ΡŒ собой настройку, ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠ΅ состояния ΠΈΠ»ΠΈ Π²Ρ‹ΠΏΠΎΠ»Π½Π΅Π½ΠΈΠ΅ ΠΎΡ‚Π΄Π΅Π»ΡŒΠ½ΠΎΠΉ ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠΈ.
  • ΠšΠΎΠ½Ρ‚Ρ€ΠΎΠ»ΡŒ ΠΎΠ±ΡΠ·Π°Ρ‚Π΅Π»ΡŒΠ½Ρ‹Ρ… шагов. Пошаговая сборка Π³Π°Ρ€Π°Π½Ρ‚ΠΈΡ€ΡƒΠ΅Ρ‚, Ρ‡Ρ‚ΠΎ ΠΎΠΏΡ€Π΅Π΄Π΅Π»Π΅Π½Π½Ρ‹Π΅ Π²Π°ΠΆΠ½Ρ‹Π΅ шаги Π½Π΅ Π±ΡƒΠ΄ΡƒΡ‚ ΠΏΡ€ΠΎΠΏΡƒΡ‰Π΅Π½Ρ‹. Π­Ρ‚ΠΎ особСнно ΠΏΠΎΠ»Π΅Π·Π½ΠΎ для процСссов, Π³Π΄Π΅ Π²Π°ΠΆΠ½ΠΎ соблюдСниС ΠΏΠΎΡΠ»Π΅Π΄ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΠ½ΠΎΡΡ‚ΠΈ дСйствий ΠΈΠ»ΠΈ ΠΊΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€Π°Ρ†ΠΈΠΈ ΠΎΠ±ΡΠ·Π°Ρ‚Π΅Π»ΡŒΠ½Ρ‹Ρ… ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€ΠΎΠ².

ΠŸΡ€ΠΈΠΌΠ΅Ρ€Ρ‹ примСнСния пошаговой сборки

ΠŸΠΎΡΡ‚Ρ€ΠΎΠ΅Π½ΠΈΠ΅ SQL-запросов

public interface SelectStep {
    FromStep select(String... columns);
}
 
public interface FromStep {
    WhereStep from(String table);
}
 
public interface WhereStep {
    OrderByStep where(String condition);
}
 
public interface OrderByStep {
    BuildStep orderBy(String column);
}
 
public interface BuildStep {
    String build();
}
 
public class SqlQueryBuilder implements SelectStep, FromStep, WhereStep, OrderByStep, BuildStep {
    private String query;
 
    @Override
    public FromStep select(String... columns) {
        query = "SELECT " + String.join(", ", columns);
        return this;
    }
 
    @Override
    public WhereStep from(String table) {
        query += " FROM " + table;
        return this;
    }
 
    @Override
    public OrderByStep where(String condition) {
        query += " WHERE " + condition;
        return this;
    }
 
    @Override
    public BuildStep orderBy(String column) {
        query += " ORDER BY " + column;
        return this;
    }
 
    @Override
    public String build() {
        return query;
    }
 
    public static SelectStep start() {
        return new SqlQueryBuilder();
    }
}

Π‘Π°ΠΌΠΎΠΎΠ±ΠΎΠ±Ρ‰Π΅Π½ΠΈΠ΅

Когда ΠΌΡ‹ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅ΠΌ наслСдованиС для создания подклассов, Π²ΠΎΠ·Π½ΠΈΠΊΠ°Π΅Ρ‚ ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΠ°, Ρ‡Ρ‚ΠΎ ΠΌΠ΅Ρ‚ΠΎΠ΄Ρ‹ Fluent API ΠΌΠΎΠ³ΡƒΡ‚ Π²ΠΎΠ·Π²Ρ€Π°Ρ‰Π°Ρ‚ΡŒ Π½Π΅ подкласс, Π° Π±Π°Π·ΠΎΠ²Ρ‹ΠΉ класс, разрывая Ρ†Π΅ΠΏΠΎΡ‡ΠΊΡƒ Π²Ρ‹Π·ΠΎΠ²ΠΎΠ². Π‘Π°ΠΌΠΎΠΎΠ±ΠΎΠ±Ρ‰Π΅Π½ΠΈΠ΅ Ρ€Π΅ΡˆΠ°Π΅Ρ‚ эту ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΡƒ, позволяя ΠΌΠ΅Ρ‚ΠΎΠ΄Π°ΠΌ Π²ΠΎΠ·Π²Ρ€Π°Ρ‰Π°Ρ‚ΡŒ ΠΏΡ€Π°Π²ΠΈΠ»ΡŒΠ½Ρ‹ΠΉ Ρ‚ΠΈΠΏ подкласса.

ΠŸΡ€ΠΈΠΌΠ΅Ρ€ ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΡ‹ Π±Π΅Π· самообобщСния. Допустим, Ρƒ нас Π΅ΡΡ‚ΡŒ Π±Π°Π·ΠΎΠ²Ρ‹ΠΉ класс с Ρ†Π΅ΠΏΠΎΡ‡ΠΊΠΎΠΉ ΠΌΠ΅Ρ‚ΠΎΠ΄ΠΎΠ², ΠΈ ΠΌΡ‹ Ρ…ΠΎΡ‚ΠΈΠΌ ΡƒΠ½Π°ΡΠ»Π΅Π΄ΠΎΠ²Π°Ρ‚ΡŒ этот класс.

class BaseBuilder {
    public BaseBuilder setName(String name) {
        System.out.println("Name set to: " + name);
        return this;
    }
}
 
class AdvancedBuilder extends BaseBuilder {
    public AdvancedBuilder setFeature(String feature) {
        System.out.println("Feature set to: " + feature);
        return this;
    }
}
 
public class Main {
    public static void main(String[] args) {
        AdvancedBuilder builder = new AdvancedBuilder();
        builder.setName("MyObject")
               .setFeature("AdvancedFeature"); // Ошибка: возвращаСтся BaseBuilder
    }
}

Π’ этом ΠΏΡ€ΠΈΠΌΠ΅Ρ€Π΅ setName() Π²ΠΎΠ·Π²Ρ€Π°Ρ‰Π°Π΅Ρ‚ Ρ‚ΠΈΠΏ BaseBuilder, поэтому ΠΏΠΎΠΏΡ‹Ρ‚ΠΊΠ° Π²Ρ‹Π·Π²Π°Ρ‚ΡŒ setFeature() Π½Π° Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚ этого Π²Ρ‹Π·ΠΎΠ²Π° ΠΏΡ€ΠΈΠ²Π΅Π΄Π΅Ρ‚ ΠΊ ошибкС. ΠœΠ΅Ρ‚ΠΎΠ΄ setFeature() Π±ΡƒΠ΄Π΅Ρ‚ нСдоступСн.

РСшСниС с использованиСм самообобщСния (Self-type Generics). ΠœΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ Ρ€Π΅ΡˆΠΈΡ‚ΡŒ эту ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΡƒ, ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΡ самообобщСниС с ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ ΠΎΠ±ΠΎΠ±Ρ‰Π΅Π½ΠΈΠΉ (generics). Π­Ρ‚ΠΎ ΠΏΠΎΠ·Π²ΠΎΠ»ΠΈΡ‚ ΠΌΠ΅Ρ‚ΠΎΠ΄Π°ΠΌ Π²ΠΎΠ·Π²Ρ€Π°Ρ‰Π°Ρ‚ΡŒ самый спСцифичный Ρ‚ΠΈΠΏ.

class BaseBuilder<T extends BaseBuilder<T>> {
    public T setName(String name) {
        System.out.println("Name set to: " + name);
        return (T) this;  // Π’ΠΎΠ·Π²Ρ€Π°Ρ‰Π°Π΅ΠΌ Ρ‚Π΅ΠΊΡƒΡ‰ΠΈΠΉ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ с Ρ‚ΠΈΠΏΠΎΠΌ T
    }
}
 
class AdvancedBuilder extends BaseBuilder<AdvancedBuilder> {
    public AdvancedBuilder setFeature(String feature) {
        System.out.println("Feature set to: " + feature);
        return this;  // Π’ΠΎΠ·Π²Ρ€Π°Ρ‰Π°Π΅ΠΌ Ρ‚Π΅ΠΊΡƒΡ‰ΠΈΠΉ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ с Ρ‚ΠΈΠΏΠΎΠΌ AdvancedBuilder
    }
}
 
public class Main {
    public static void main(String[] args) {
        AdvancedBuilder builder = new AdvancedBuilder();
        builder.setName("MyObject")
               .setFeature("AdvancedFeature");  // Π’Π΅ΠΏΠ΅Ρ€ΡŒ Ρ€Π°Π±ΠΎΡ‚Π°Π΅Ρ‚ ΠΏΡ€Π°Π²ΠΈΠ»ΡŒΠ½ΠΎ
    }
}

ΠœΠ΅Ρ‚Π° информация

ΠžΠ±Π»Π°ΡΡ‚ΡŒ:: 00 Java Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚ΠΊΠ°
Π ΠΎΠ΄ΠΈΡ‚Π΅Π»ΡŒ::
Π˜ΡΡ‚ΠΎΡ‡Π½ΠΈΠΊ::
Π‘ΠΎΠ·Π΄Π°Π½Π°:: 2024-10-04
Автор::

Π”ΠΎΠΏΠΎΠ»Π½ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹Π΅ ΠΌΠ°Ρ‚Π΅Ρ€ΠΈΠ°Π»Ρ‹

Π”ΠΎΡ‡Π΅Ρ€Π½ΠΈΠ΅ Π·Π°ΠΌΠ΅Ρ‚ΠΊΠΈ