Files
whale-town-end/docs/naming_convention.md

11 KiB
Raw Blame History

命名规范

本文档定义了项目中所有代码的命名规范,确保代码风格统一,提高可读性和可维护性。

目录

文件和文件夹命名

规则使用下划线分隔snake_case

文件命名

✅ 正确示例:
- order_controller.ts
- user_service.ts
- game_gateway.ts
- player_entity.ts
- create_room_dto.ts
- database_config.ts

❌ 错误示例:
- OrderController.ts
- userService.ts
- game-gateway.ts
- playerEntity.ts

文件夹命名

✅ 正确示例:
- src/api/
- src/service/
- src/model/dto/
- src/utils/
- src/config/
- test/unit_test/
- test/integration_test/

❌ 错误示例:
- src/API/
- src/Service/
- src/model-dto/
- src/Utils/

特殊说明

  • 所有文件和文件夹名使用小写字母
  • 多个单词之间使用下划线 _ 连接
  • 避免使用缩写,除非是广泛认可的缩写(如 dto、api

变量和函数命名

规则使用小驼峰命名camelCase

变量命名

 正确示例:
const userName = 'Alice';
let playerScore = 100;
const roomId = '12345';
const isGameStarted = false;
const maxPlayerCount = 4;

 错误示例:
const UserName = 'Alice';
const player_score = 100;
const RoomId = '12345';
const is_game_started = false;

函数命名

 正确示例:
function getUserInfo() { }
async function queryUserInfo() { }
function calculateDamage() { }
function isPlayerAlive() { }
function handlePlayerMove() { }

 错误示例:
function GetUserInfo() { }
function query_user_info() { }
function CalculateDamage() { }
function IsPlayerAlive() { }

命名建议

  • 使用动词开头描述函数功能:

    • get - 获取数据(如 getUserById
    • set - 设置数据(如 setPlayerPosition
    • create - 创建实体(如 createRoom
    • update - 更新数据(如 updatePlayerScore
    • delete - 删除数据(如 deleteRoom
    • is/has - 布尔判断(如 isGameOverhasPermission
    • handle - 事件处理(如 handlePlayerAttack
    • calculate - 计算逻辑(如 calculateDamage
    • validate - 验证逻辑(如 validateInput
  • 布尔变量使用 ishascan 等前缀:

    const isActive = true;
    const hasPermission = false;
    const canMove = true;
    

类和构造函数命名

规则使用大驼峰命名PascalCase

类命名

 正确示例:
class UserModel { }
class OrderService { }
class GameController { }
class PlayerEntity { }
class RoomGateway { }
class DatabaseConnection { }

 错误示例:
class userModel { }
class order_service { }
class gameController { }
class player_entity { }

接口命名

 正确示例:
interface User { }
interface GameConfig { }
interface PlayerData { }
interface RoomOptions { }

// 或使用 I 前缀(可选)
interface IUser { }
interface IGameConfig { }

 错误示例:
interface user { }
interface game_config { }
interface playerData { }

DTO 命名

 正确示例:
class CreateUserDto { }
class UpdatePlayerDto { }
class JoinRoomDto { }
class GameStateDto { }

 错误示例:
class createUserDto { }
class update_player_dto { }
class joinRoomDTO { }

装饰器命名

 正确示例:
@Controller('users')
@Injectable()
@Module()
@Get()

// 自定义装饰器
function CustomDecorator() { }

常量命名

规则:全大写 + 下划线分隔SCREAMING_SNAKE_CASE

 正确示例:
const PORT = 3000;
const DB_HOST = 'localhost';
const MAX_PLAYERS = 10;
const API_VERSION = 'v1';
const DEFAULT_TIMEOUT = 5000;
const GAME_STATUS_WAITING = 'waiting';
const GAME_STATUS_PLAYING = 'playing';

 错误示例:
const port = 3000;
const dbHost = 'localhost';
const maxPlayers = 10;
const ApiVersion = 'v1';
const default_timeout = 5000;

枚举命名

 正确示例:
enum GameStatus {
  WAITING = 'waiting',
  PLAYING = 'playing',
  FINISHED = 'finished',
}

enum PlayerRole {
  ADMIN = 'admin',
  PLAYER = 'player',
  SPECTATOR = 'spectator',
}

 错误示例:
enum gameStatus {
  waiting = 'waiting',
  playing = 'playing',
}

enum PlayerRole {
  admin = 'admin',
  player = 'player',
}

接口路由命名

规则:全小写 + 短横线分隔kebab-case

 正确示例:
@Get('user/get-info')
@Post('order/create-order')
@Put('player/update-position')
@Delete('room/delete-room')
@Get('game/get-state')
@Post('room/join-room')

 错误示例:
@Get('user/getInfo')
@Post('order/createOrder')
@Put('player/update_position')
@Delete('room/DeleteRoom')
@Get('game/GetState')

路由结构建议

// 资源型路由
@Controller('api/players')
export class PlayerController {
  @Get()                    // GET /api/players
  @Get(':id')              // GET /api/players/:id
  @Post()                  // POST /api/players
  @Put(':id')              // PUT /api/players/:id
  @Delete(':id')           // DELETE /api/players/:id
}

// 动作型路由
@Controller('api/game')
export class GameController {
  @Post('start-game')      // POST /api/game/start-game
  @Post('end-game')        // POST /api/game/end-game
  @Get('get-state')        // GET /api/game/get-state
}

// 嵌套资源路由
@Controller('api/rooms')
export class RoomController {
  @Post(':id/join')        // POST /api/rooms/:id/join
  @Post(':id/leave')       // POST /api/rooms/:id/leave
  @Get(':id/players')      // GET /api/rooms/:id/players
}

TypeScript 特定规范

类型别名

 正确示例:
type UserId = string;
type PlayerPosition = { x: number; y: number };
type GameCallback = (state: GameState) => void;

 错误示例:
type userId = string;
type player_position = { x: number; y: number };

泛型参数

 正确示例:
function findById<T>(id: string): T { }
class Repository<T, K> { }
interface Response<T> { }

// 使用有意义的名称
function mapArray<TInput, TOutput>(arr: TInput[]): TOutput[] { }

 错误示例:
function findById<t>(id: string): t { }
class Repository<type, key> { }

装饰器参数

 正确示例:
@Column({ name: 'user_name' })
@IsString({ message: 'Name must be a string' })
@ApiProperty({ description: 'User ID' })

 错误示例:
@Column({ name: 'UserName' })
@IsString({ message: 'name_must_be_string' })

命名示例

完整的模块示例

// 文件src/api/player_controller.ts
import { Controller, Get, Post, Body, Param } from '@nestjs/common';
import { PlayerService } from '../service/player_service';
import { CreatePlayerDto } from '../model/dto/create_player_dto';

const MAX_PLAYERS = 100;

@Controller('api/players')
export class PlayerController {
  constructor(private readonly playerService: PlayerService) {}

  @Get()
  async getAllPlayers() {
    return this.playerService.findAll();
  }

  @Get(':id')
  async getPlayerById(@Param('id') playerId: string) {
    return this.playerService.findById(playerId);
  }

  @Post()
  async createPlayer(@Body() createPlayerDto: CreatePlayerDto) {
    return this.playerService.create(createPlayerDto);
  }

  @Post(':id/update-position')
  async updatePlayerPosition(
    @Param('id') playerId: string,
    @Body() body: { x: number; y: number }
  ) {
    const { x, y } = body;
    return this.playerService.updatePosition(playerId, x, y);
  }
}
// 文件src/service/player_service.ts
import { Injectable, NotFoundException } from '@nestjs/common';
import { Player } from '../model/player_entity';
import { CreatePlayerDto } from '../model/dto/create_player_dto';

const DEFAULT_HEALTH = 100;
const DEFAULT_SPEED = 5;

@Injectable()
export class PlayerService {
  private players: Map<string, Player> = new Map();

  findAll(): Player[] {
    return Array.from(this.players.values());
  }

  findById(playerId: string): Player {
    const player = this.players.get(playerId);
    if (!player) {
      throw new NotFoundException(`Player with ID ${playerId} not found`);
    }
    return player;
  }

  create(createPlayerDto: CreatePlayerDto): Player {
    const newPlayer: Player = {
      id: this.generatePlayerId(),
      name: createPlayerDto.name,
      health: DEFAULT_HEALTH,
      speed: DEFAULT_SPEED,
      position: { x: 0, y: 0 },
      isAlive: true,
      createdAt: new Date(),
    };
    this.players.set(newPlayer.id, newPlayer);
    return newPlayer;
  }

  updatePosition(playerId: string, x: number, y: number): Player {
    const player = this.findById(playerId);
    player.position = { x, y };
    return player;
  }

  private generatePlayerId(): string {
    return `player_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
  }

  private calculateDamage(attackPower: number, defense: number): number {
    return Math.max(0, attackPower - defense);
  }

  isPlayerAlive(playerId: string): boolean {
    const player = this.findById(playerId);
    return player.isAlive && player.health > 0;
  }
}
// 文件src/model/player_entity.ts
export interface Player {
  id: string;
  name: string;
  health: number;
  speed: number;
  position: Position;
  isAlive: boolean;
  createdAt: Date;
}

export interface Position {
  x: number;
  y: number;
}

export enum PlayerStatus {
  IDLE = 'idle',
  MOVING = 'moving',
  ATTACKING = 'attacking',
  DEAD = 'dead',
}
// 文件src/model/dto/create_player_dto.ts
import { IsString, IsNotEmpty, MinLength, MaxLength } from 'class-validator';

export class CreatePlayerDto {
  @IsString()
  @IsNotEmpty()
  @MinLength(3)
  @MaxLength(20)
  name: string;
}

检查清单

在提交代码前,请确保:

  • 所有文件和文件夹使用下划线分隔命名
  • 所有变量和函数使用小驼峰命名
  • 所有类、接口、DTO 使用大驼峰命名
  • 所有常量和枚举值使用全大写 + 下划线命名
  • 所有路由使用全小写 + 短横线命名
  • 函数名清晰表达其功能
  • 布尔变量使用 is/has/can 前缀
  • 避免使用无意义的缩写

工具配置

ESLint 配置建议

{
  "rules": {
    "@typescript-eslint/naming-convention": [
      "error",
      {
        "selector": "variable",
        "format": ["camelCase", "UPPER_CASE"]
      },
      {
        "selector": "function",
        "format": ["camelCase"]
      },
      {
        "selector": "class",
        "format": ["PascalCase"]
      },
      {
        "selector": "interface",
        "format": ["PascalCase"]
      }
    ]
  }
}

总结

遵循统一的命名规范能够:

  • 提高代码可读性
  • 减少团队沟通成本
  • 降低代码维护难度
  • 避免命名冲突
  • 提升项目专业度

记住:好的命名是自解释的,不需要额外的注释。