Featured image of post 運用ArrayList與自動配置將Swich語法拆解

運用ArrayList與自動配置將Swich語法拆解

前幾天在寫程式時,想到一個把Switch語法拆掉的方式,特此紀錄

如果依照存在即合理的說法來看,Switch的存在確實是有解決一些問題,比如說一些要依照不同情況來回傳不同結果的Function,相較於用冗長的if-else,選擇用Switch確實是個不錯的解法。但當今天的Switch Case會增長的情況,在選擇使用它就會違反了OCP(開放擴充、封閉修改)的原則,亦即每次有新的情況出現,我們就得回頭去改Switch語法,新增不同的case,一來一往增加了維護的負擔,這邊分享一個我把switch語法拆解的方式,以供紀錄這樣子。

情境

我在設計一款可以連結不同SQL Engine的程式,當用戶選擇了不同的SQL Engine,我能夠執行不同資料庫的語法來返回結果,圖示如下

image-20230314004011166

修改前

這是我的設計

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
@RestController
public class DatasourceController {

    DatabaseService databaseService;

    public DatasourceController(DatabaseService databaseService) {
        this.databaseService = databaseService;
    }

    @GetMapping("/query")
    public String query(@RequestBody QueryRequest queryRequest) throws SQLException, JsonProcessingException, ClassNotFoundException {
        return databaseService.query(queryRequest);
    }


}

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
@Service
public class DatabaseServiceImp implements DatabaseService{

    DatabaseDao databaseDao;

    public DatabaseServiceImp(DatabaseDao databaseDao) {
        this.databaseDao = databaseDao;
    }

    @Override
    public String query(QueryRequest queryRequest) throws SQLException, JsonProcessingException, ClassNotFoundException {
        String databaseName = queryRequest.getDatabaseEngine();
        databaseDao = getDatabaseDao(databaseName);
        return databaseDao.connect().query(queryRequest.getQuery());
    }
 

    private DatabaseDao getDatabaseDao(String databaseName) {
        switch (databaseName){
            case "Postgres":
                return new PostgresDaoImpl();
            case  "MsSql":
                return new MySQLDaoImpl();
            default:
                throw DatabaseNotFoundException.createDatabaseNotFoundException("Not this Database");
        }

    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
@Repository

public interface DatabaseDao {

    DatabaseDao connect() throws SQLException, ClassNotFoundException;

    void close() throws SQLException;

    String query(String query) throws SQLException, JsonProcessingException;
}

我在Service層的地方會有一個methodgetDatabaseDao,來依照輸入的databaseName來將不同的Dao賦值進filed中,首先這有兩個問題

  1. 當資料庫在擴充時,必須要回頭修改getDatabaseDao,增加修改的成本
  2. 無法利用SpringBean的IoC,每次有新的Request近來,都會創造一個Dao的Object存在於記憶體中,造成記憶體空間的浪費

修改後

因此後來的修改必須得改善上面兩點,必須要符合OCP的規則,我只要新增新的Dao,而毋需修改就有的程式碼,並且還要使用SpringBean,以下是修改後的版本

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
@Service
public class DatabaseServiceImp implements DatabaseService {
    DatabaseEngineDao databaseEngineDao;
    List<DatabaseEngineDao> databaseDaoList;


    public DatabaseServiceImp(List<DatabaseEngineDao> databaseDaoList) {
        this.databaseDaoList = databaseDaoList;
    }

    @Override
    public String query(QueryRequest queryRequest) throws SQLException, JsonProcessingException, ClassNotFoundException {
        String databaseName = queryRequest.getDatabaseEngine();
        databaseEngineDao = getDatabaseDao(databaseName);
        return databaseEngineDao.connect().query(queryRequest.getQuery());
    }


    private DatabaseEngineDao getDatabaseDao(String databaseEngine) {
        for (DatabaseEngineDao databaseDao : databaseDaoList) {
            if (databaseEngine.equals(databaseDao.getDatabaseEngineName())) {
                return databaseDao;
            }
        }
        throw DatabaseNotFoundException.createDatabaseNotFoundException("Not Found This DatabaseEngine,請檢查是否有相應的databaseEngineName在DAO中");
    }
}
1
2
3
4
5
6
7
8
@Repository
public interface DatabaseEngineDao {
    DatabaseEngineDao connect() throws SQLException;
    void close() throws SQLException;
    String query(String query) throws SQLException, JsonProcessingException;
    public String getDatabaseEngineName();
}

將原本的DatabaseDao databaseDao;修改為,List<DatabaseEngineDao> databaseDaoList;,在SpringBoot啟動時,將所有的DatabaseEngineDao類配置進databaseDaoList中,讓springBoot控制object的創建,並且將getDatabaseDao改寫,改成去for-each databaseDaoList,尋找List中是否有名稱符合的SQL Engine。並在DatabaseEngineDao Interface處新增getDatabaseEngineName,讓每個實作它的Impl都必須去完成這個method,完成了OCP的原則。