如何消除代码中的switch-case?以数据库连接器场景为例
Great question! That switch statement is a textbook example of code that breaks the Open/Closed Principle—every time you add a new database type, you have to dig into that existing switch block, modify it, and risk breaking existing functionality. Let's cover a couple of clean, maintainable alternatives that align with OCP.
Option 1: Registry-Based Factory + Strategy Pattern
This is the most flexible approach, especially if you might need to add new database types dynamically (like via plugins later on). Here's how to implement it:
Step 1: Define a Common Interface
First, create an interface that all database connectors will implement to enforce a consistent contract:
public interface DatabaseConnector { void connect(); // Example common method; add others your system needs }
Step 2: Implement Connector Classes
Each database connector implements the interface, with its own initialization logic:
public class MSSQLConnector implements DatabaseConnector { public MSSQLConnector(ConnectorConfig config) { // Use config (host, port, credentials, etc.) to initialize } @Override public void connect() { // MSSQL-specific connection logic } } public class MYSQLConnector implements DatabaseConnector { public MYSQLConnector(ConnectorConfig config) { // MySQL-specific initialization } @Override public void connect() { // MySQL-specific connection logic } } // Repeat for OracleConnector, PostgreSQLConnector, etc.
Pro tip: Instead of passing messy, inconsistent argument lists, create a ConnectorConfig class to hold all connection details. This keeps your code type-safe and clean:
public class ConnectorConfig { private String host; private int port; private String username; private String password; // Add database-specific fields (e.g., mssqlInstanceName, mysqlSslMode) as needed // Getters and setters }
Step 3: Build a Registry Factory
This factory will store connector creation logic and let you retrieve instances without hardcoding conditions:
import java.util.HashMap; import java.util.Map; import java.util.function.Function; public class ConnectorFactory { private static final Map<String, Function<ConnectorConfig, DatabaseConnector>> REGISTRY = new HashMap<>(); // Register existing connectors on startup static { REGISTRY.put("MSSQL", MSSQLConnector::new); REGISTRY.put("MYSQL", MYSQLConnector::new); REGISTRY.put("ORACLE", OracleConnector::new); } // Open method to register new connectors at runtime (no need to modify the factory!) public static void registerConnector(String dbType, Function<ConnectorConfig, DatabaseConnector> creator) { REGISTRY.put(dbType, creator); } public static DatabaseConnector getConnector(String dbType, ConnectorConfig config) { Function<ConnectorConfig, DatabaseConnector> creator = REGISTRY.get(dbType); if (creator == null) { throw new IllegalArgumentException("Unsupported database type: " + dbType); } return creator.apply(config); } }
Usage
Now your original switch-case is replaced with a single, clean line:
ConnectorConfig config = buildConfigForType(Type); // Fetch or construct your config DatabaseConnector connector = ConnectorFactory.getConnector(Type, config);
When you add a new database type, you just:
- Create a new
XYZConnectorclass implementingDatabaseConnector - Call
ConnectorFactory.registerConnector("XYZ", XYZConnector::new)
No changes to existing factory or business code required—perfect for OCP!
Option 2: Enum-Driven Implementation
If you don't need dynamic registration (all database types are known at compile time), an enum-based approach is more concise:
Define the Enum
The enum will map each database type to its connector creation logic:
public enum DatabaseType { MSSQL(MSSQLConnector::new), MYSQL(MYSQLConnector::new), ORACLE(OracleConnector::new); private final Function<ConnectorConfig, DatabaseConnector> creator; DatabaseType(Function<ConnectorConfig, DatabaseConnector> creator) { this.creator = creator; } public DatabaseConnector createConnector(ConnectorConfig config) { return creator.apply(config); } }
Usage
Retrieve the connector directly from the enum:
ConnectorConfig config = buildConfigForType(Type); DatabaseConnector connector = DatabaseType.valueOf(Type).createConnector(config);
Adding a new database type only requires adding a new enum entry—no other code changes needed. This is great for small, fixed sets of database types.
Why These Work for OCP
Both approaches follow the core OCP rule:
- Open for extension: You can add new database connectors without modifying existing code.
- Closed for modification: The factory/enum and existing connector classes don't need to change when you add new types.
内容的提问来源于stack exchange,提问作者jhthewow

