forked from datawhale/whale-town-end
11 KiB
11 KiB
命名规范
本文档定义了项目中所有代码的命名规范,确保代码风格统一,提高可读性和可维护性。
目录
文件和文件夹命名
规则:使用下划线分隔(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- 布尔判断(如isGameOver、hasPermission)handle- 事件处理(如handlePlayerAttack)calculate- 计算逻辑(如calculateDamage)validate- 验证逻辑(如validateInput)
-
布尔变量使用
is、has、can等前缀: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"]
}
]
}
}
总结
遵循统一的命名规范能够:
- 提高代码可读性
- 减少团队沟通成本
- 降低代码维护难度
- 避免命名冲突
- 提升项目专业度
记住:好的命名是自解释的,不需要额外的注释。