如何避免将this作为类参数传入?最佳实践咨询
避免在构造器中传递
this的最佳实践 你说得太对了——在构造器里把this传递给其他对象确实是个风险点,因为此时当前类的初始化还没完成,其他对象如果立刻调用这个未完全初始化的实例的方法或访问属性,很容易触发不可预期的错误,甚至空指针异常。针对你给出的Game和Board的场景,我整理了几个常用的最佳实践:
1. 延迟依赖注入(初始化后再设置依赖)
把Board的初始化和Game实例的传递分开,先完成Game的所有字段初始化,再通过 setter 方法把Game实例传给Board。这样能确保Game处于完全可用的状态,不会出现半初始化的问题。
示例代码:
public class Game { private Player p1, p2, currentPlayer; private Board board; Game() { board = new Board(); // 先初始化Board,不传递this // 先完成Game自身字段的初始化 p1 = new Player(); p2 = new Player(); currentPlayer = p1; // 此时Game已完全初始化,再设置依赖 board.setGame(this); } private boolean hasFieldsToBeClicked() { return board.checkFieldsToBeClicked(); } } public class Board { private Game game; public void setGame(Game game) { this.game = game; // 这里可以执行需要依赖Game的初始化逻辑 } public boolean checkFieldsToBeClicked() { // 业务逻辑实现 return true; } }
2. 使用静态工厂方法代替构造器
静态工厂方法能让我们更灵活地控制对象的初始化流程:先完成Game的所有初始化步骤,再创建Board并传递完整的Game实例。这种方式还能让代码的意图更清晰,避免外部直接调用构造器带来的风险。
示例代码:
public class Game { private Player p1, p2, currentPlayer; private Board board; // 私有构造器,强制外部使用工厂方法创建实例 private Game() { // 先初始化Game的所有字段 p1 = new Player(); p2 = new Player(); currentPlayer = p1; } // 静态工厂方法,封装完整的初始化流程 public static Game createGame() { Game game = new Game(); // 此时Game已完全初始化,再创建Board并传递this game.board = new Board(game); return game; } private boolean hasFieldsToBeClicked() { return board.checkFieldsToBeClicked(); } }
3. 重构依赖关系,避免Board直接持有Game引用
如果业务逻辑允许,可以调整类之间的依赖方向,或者提取公共逻辑到第三方类,让Board不需要直接持有Game的引用。比如把Board需要的游戏状态数据单独封装,作为参数传递给Board的方法,而不是让Board依赖整个Game实例。
示例代码:
public class Game { private Player p1, p2, currentPlayer; private Board board; Game() { board = new Board(); p1 = new Player(); p2 = new Player(); currentPlayer = p1; } private boolean hasFieldsToBeClicked() { // 把Board需要的信息作为参数传递,而非让Board持有Game引用 return board.checkFieldsToBeClicked(new GameState(p1, p2, currentPlayer)); } } public class Board { public boolean checkFieldsToBeClicked(GameState gameState) { // 使用传入的游戏状态数据执行逻辑判断 return true; } } // 提取的游戏状态类,封装Board需要的所有数据 class GameState { private Player p1; private Player p2; private Player currentPlayer; public GameState(Player p1, Player p2, Player currentPlayer) { this.p1 = p1; this.p2 = p2; this.currentPlayer = currentPlayer; } // 提供必要的getter方法 }
4. 禁止Board在构造器中执行依赖Game的逻辑
如果必须在构造器中传递this,那一定要确保Board的构造器中不直接调用Game的任何方法或访问其字段,把依赖Game的逻辑延迟到专门的初始化方法中,等Game完全初始化后再调用这个方法。
示例代码:
public class Game { private Player p1, p2, currentPlayer; private Board board; Game() { board = new Board(this); // 先完成Game自身的所有初始化 p1 = new Player(); p2 = new Player(); currentPlayer = p1; // 通知Board可以执行依赖Game的初始化逻辑了 board.init(); } private boolean hasFieldsToBeClicked() { return board.checkFieldsToBeClicked(); } } public class Board { private Game game; public Board(Game game) { this.game = game; // 构造器中只保存引用,不做任何依赖Game的操作 } public void init() { // 这里才执行需要使用Game实例的初始化逻辑 } public boolean checkFieldsToBeClicked() { // 业务逻辑实现 return true; } }
内容的提问来源于stack exchange,提问作者Fabian Zbinden
相关产品推荐
相关产品推荐

