You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JavaScript抽象类使用最佳实践:国际象棋应用中isLegalMove方法的实现方案抉择

Best practices for implementing abstract methods with shared logic in JavaScript for a chess app

I'm building a chess application using HTML, CSS, and JavaScript, and I'm unsure about the best practices for constructing JavaScript abstract classes. I've defined a Piece abstract class containing common getter/setter methods, with subclasses like Knight, Pawn, Queen all inheriting from it. I've set up an abstract method isLegalMove(tile) to determine if a piece can move to the specified tile.

While each piece has unique movement rules (hence the abstract method), there are also universal rules—like not moving outside the board, black pieces can only move on black's turn, etc. My question is: which approach is more aligned with best practices when implementing the isLegalMove(tile) abstract method?

Option 1: Check both common and unique conditions in each subclass's isLegalMove

// Queen.js
isLegalMove(tile) {
  if (this.getBoard().getTurn() != this.getColor() || tile.getRow() > 8 || tile.getRow() < 1 || tile.getCol() > 8 || tile.getCol() < 1) {
    // common illegal conditions
    return false;
  }
  if (isPieceBetween(this.getTile(), tile)) {
    // unique illegal condition for a queen
    return false;
  }
  return true;
}

Option 2: Create a non-abstract commonConditions(tile) method in the Piece class, then call it in each subclass's isLegalMove

// Piece.js
commonConditions(tile) {
  if (this.getBoard().getTurn() != this.getColor() || tile.getRow() > 8 || tile.getRow() < 1 || tile.getCol() > 8 || tile.getCol() < 1) {
    // common illegal conditions
    return false;
  }
  return true;
}

// Queen.js
isLegalMove(tile) {
  if (!this.commonConditions(tile)) {
    return false;
  }
  if (isPieceBetween(this.getTile(), tile)) {
    // unique illegal condition for a queen
    return false;
  }
  return true;
}

This might be a matter of personal preference or the extent of redundant common conditions, but I'd like to clarify which approach is more in line with front-end development best practices.


Answer

Option 2 is absolutely the better approach, and it aligns with core software engineering principles like DRY (Don't Repeat Yourself) and maintainability—both critical for keeping your chess app scalable and bug-free as it grows.

Here's why it stands out:

  • Minimized redundancy: If you ever need to update a universal rule (like adjusting board bounds for a chess variant, or tweaking turn validation logic), you only have to modify the commonConditions method in the Piece parent class once. With Option 1, you'd have to hunt down and edit every single subclass's isLegalMove method—tedious and ripe for human error.
  • Cleaner subclass focus: Each subclass's isLegalMove can zero in solely on the unique movement rules that define that piece. When you look at Queen.js, you immediately see the logic that makes a queen distinct, not boilerplate that applies to every piece on the board. This makes the code easier to read, test, and debug.
  • Guaranteed consistency: Centralizing common rules ensures all pieces follow the same universal constraints. There's no risk of a subclass accidentally omitting a critical check (like forgetting to validate the turn color) which would break core game logic.

You can even level up this approach with the Template Method Pattern to make the structure more robust. Instead of leaving subclasses responsible for calling commonConditions, define the full validation flow in the parent Piece class:

// Piece.js
isLegalMove(tile) {
  // Enforce common rules first
  if (!this.commonConditions(tile)) {
    return false;
  }
  // Delegate to subclass-specific logic
  return this.isLegalPieceSpecificMove(tile);
}

// Abstract method all subclasses must implement
isLegalPieceSpecificMove(tile) {
  throw new Error("Subclasses must implement the isLegalPieceSpecificMove method");
}

commonConditions(tile) {
  return this.getBoard().getTurn() === this.getColor() &&
         tile.getRow() >= 1 && tile.getRow() <= 8 &&
         tile.getCol() >= 1 && tile.getCol() <= 8;
}

Then your Queen subclass only needs to handle its unique movement rules:

// Queen.js
isLegalPieceSpecificMove(tile) {
  return !isPieceBetween(this.getTile(), tile);
}

This pattern ensures common checks are always run before any piece-specific logic, eliminating the chance of a subclass forgetting to call commonConditions. It also makes the parent class the single source of truth for the overall move validation workflow.

In short, Option 2 (and its Template Method enhancement) is the standard best practice for this scenario—keep shared logic centralized, let subclasses handle what makes them unique.


内容的提问来源于stack exchange,提问作者Ares Stavropoulos

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.29 23:27:34