类属性修改与数据库数据修改的差异及代码设计困惑
OOP封装原则在数据库操作中的落地建议
核心矛盾的本质:OOP里禁止直接访问对象私有属性,是为了控制变更入口、降低耦合;数据库操作里直接跨表写SQL,本质也是跳过了“数据访问的封装层”,导致变更成本高、维护困难——比如你现在改dice表的字段名,所有写了
SELECT dice_score FROM dice的地方都要改,和直接改对象私有属性的问题一模一样。必须做的分层:引入DAO(数据访问对象)层,每个数据库表对应一个DAO类,把该表的所有CRUD逻辑、字段映射都封装在这个类里。比如:
DiceDAO:只负责dice表的读写,提供getLastDiceScore()、updateDiceScore(score, isDouble)等方法PlayerDAO:只负责players表的操作,提供updatePlayerBoardPosition(playerId, newPosition)等方法GameLogicDAO:负责game_logic表的查询,比如getCurrentTurnPlayerIds()
业务类的正确写法:按行为分组的业务类(比如处理玩家移动的
GameMoveService),通过依赖注入获取所需的DAO实例,业务逻辑只关注“用骰子点数更新玩家位置”的规则,不直接写SQL。针对你现有代码的重构示例:
先封装DiceDAO:class DiceDAO { async getLastScore(connection) { const [rows] = await connection.execute('SELECT dice_score FROM dice'); return rows[0]?.dice_score; } }再封装GameLogicDAO:
class GameLogicDAO { async getCurrentTurnPlayerIds(connection) { const [rows] = await connection.execute('SELECT player_turn FROM game_logic'); return rows.map(row => row.player_turn); } }最后业务类中使用:
async updateCurrentPlayersSquareCount(connection, diceDAO, gameLogicDAO) { try { const diceScore = await diceDAO.getLastScore(connection); const playerIds = await gameLogicDAO.getCurrentTurnPlayerIds(connection); await connection.execute(` UPDATE PLAYERS SET board_id = CASE WHEN board_id + ? > 40 THEN board_id + ? - 40 ELSE board_id + ? END WHERE id IN (?) `, [diceScore, diceScore, diceScore, playerIds]); } catch (e) { throw new Error("无法将骰子点数添加到玩家的棋盘位置"); } }额外说明:你没有过度思考,这是代码从“能用”到“好维护”的关键转变。数据库的DAO层就相当于OOP中封装了属性的对象——业务类通过调用DAO的方法来操作数据,而不是直接操作数据库表,这样变更的来源清晰,逻辑可追溯,完全对齐OOP的封装原则。
内容的提问来源于stack exchange,提问作者Kevin Greetham
相关产品推荐
相关产品推荐

