Functional Interface Pattern¶
The Functional Interface Pattern is a software design approach utilized to extract and encapsulate repetitive logic into reusable templates, primarily leveraging functional programming constructs.^[600-developer-java-java8-java8-lambda01.md] This pattern is particularly effective for resource management scenarios, such as database operations, where boilerplate code like connection handling and transaction management can be abstracted away from the core business logic.^[600-developer-java-java8-java8-lambda01.md]
Core Concepts¶
At the heart of this pattern lies the Functional Interface, a structure that defines a single abstract method capable of accepting inputs and returning a value.^[600-developer-java-java8-java8-lambda01.md] In languages like Java, this is explicitly annotated with @FunctionalInterface.^[600-developer-java-java8-java8-lambda01.md] These interfaces serve as the contract for the behavior to be executed, allowing specific implementations to be passed as parameters—often using method references (e.g., ClassName::staticMethodName)—to generic template methods.^[600-developer-java-java8-java8-lambda01.md]
Application in Business Objects¶
The pattern is commonly implemented to separate the "template" logic from the "data access" logic:
- Template Extraction: The business object (BO) layer defines the workflow. For example, a generic
readorwritemethod handles opening connections, committing transactions, or managing exceptions in atry-finallyblock.^[600-developer-java-java8-java8-lambda01.md] - Logic Injection: The data access object (DAO) layer provides the specific SQL execution logic. Instead of managing resources directly, the DAO method is passed as a function to the BO template.^[600-developer-java-java8-java8-lambda01.md]
This results in cleaner code where complex resource management is written once in the template and reused across various data access operations.^[600-developer-java-java8-java8-lambda01.md]
Example Implementation¶
The following example demonstrates extracting a database connection template using a custom Functional Interface, BiFunctionUnchecked, which handles checked exceptions (like SQLException) often encountered in database logic.^[600-developer-java-java8-java8-lambda01.md]
1. The Functional Interface¶
This interface defines the contract for a function that takes a Connection and a Map, and returns a result, while permitting a SQLException.^[600-developer-java-java8-java8-lambda01.md]
@FunctionalInterface
public interface BiFunctionUnchecked<K, T, V> {
V applyDao(K k, T t) throws SQLException;
}
2. The Template Method¶
The DefaultBO class provides the reusable skeleton for read operations, handling the connection lifecycle.^[600-developer-java-java8-java8-lambda01.md]
public class DefaultBO {
public static <R> R read(Map map, BiFunctionUnchecked<Connection, Map, R> function) throws Exception {
Connection conn = null;
try {
conn = DBPool.getInstance().getReadConnection();
return function.applyDao(conn, map);
} finally {
DbUtils.close(conn);
}
}
}
3. The Client Code¶
The specific DAO logic (BeanDao::currentSql) is injected into the template via a method reference.^[600-developer-java-java8-java8-lambda01.md]
public class BeanDao {
public static List<OhSystemConfig> currentSql(Connection conn, Map<String, Object> map) throws SQLException {
// Specific SQL logic here
return DBQueryRunner.getBeanList(conn, OhSystemConfig.class, sql, map.get("type"));
}
}
public class TestBo {
private static List<OhSystemConfig> simpleBoMethod(Map<String, Object> map) throws Exception {
// Logic injected: "Classname - StaticMethodName"
return DefaultBO.read(map, BeanDao::currentSql);
}
}
Related Concepts¶
- [[Functional Programming]]
- [[Lambda Expressions]]
- Design Patterns
- [[Aspect-Oriented Programming]] (conceptual alternative for cross-cutting concerns)
Sources¶
^[600-developer-java-java8-java8-lambda01.md]