Files
whale-town-front/docs/02-开发规范/开发哲学与最佳实践.md
moyin 1ff677b3b2 docs: 重新组织文档结构,按开发阶段分类
新的目录结构:
  01-项目入门/     # 新人必读,项目基础
  02-开发规范/     # 编码标准和规范
  03-技术实现/     # 具体开发指导
  04-高级开发/     # 进阶开发技巧
  05-部署运维/     # 发布和部署
  06-功能模块/     # 特定功能文档

 新增导航文档:
- docs/README.md - 完整的文档导航和使用指南
- 各目录下的README.md - 分类说明和使用指导

 优化效果:
- 开发者可以按阶段快速定位需要的文档
- 新人有清晰的学习路径
- 不同角色有针对性的文档推荐
- 提供了问题导向的快速查找功能
2025-12-31 18:02:16 +08:00

11 KiB
Raw Blame History

开发哲学与最佳实践

本文档阐述了WhaleTown项目的开发哲学和编程最佳实践旨在指导团队创造高质量、可维护的代码。

🧘 开发哲学

核心理念

  • 用户体验至上 - 每个功能都要考虑用户感受
  • 代码即文档 - 代码应该自解释,清晰易懂
  • 简洁胜于复杂 - 优先选择简单直接的解决方案
  • 质量重于速度 - 宁可慢一点,也要做对

设计原则

1. 流畅体验 (Juice or Death)

每个用户交互都必须有视觉反馈和动画效果:

# ✅ 正确为UI交互添加动画
func show_dialog() -> void:
    dialog.modulate.a = 0.0
    dialog.scale = Vector2(0.8, 0.8)
    dialog.visible = true
    
    var tween = create_tween()
    tween.parallel().tween_property(dialog, "modulate:a", 1.0, 0.3)
    tween.parallel().tween_property(dialog, "scale", Vector2.ONE, 0.3)
    tween.set_ease(Tween.EASE_OUT)
    tween.set_trans(Tween.TRANS_BACK)

# ❌ 错误:没有动画的生硬切换
func show_dialog() -> void:
    dialog.visible = true  # 突然出现,体验差

2. 零魔法数字 (Zero Magic Numbers)

所有数值都应该有明确的含义和来源:

# ✅ 正确:使用导出变量或配置文件
@export var move_speed: float = 200.0
@export var jump_height: float = 400.0
@export var health_max: int = 100

# 或从配置文件加载
const CONFIG_PATH = "res://data/player_config.json"
var config_data: Dictionary

func _ready() -> void:
    config_data = load_config(CONFIG_PATH)
    move_speed = config_data.get("move_speed", 200.0)

# ❌ 错误:硬编码的魔法数字
func _physics_process(delta: float) -> void:
    velocity.x = input_direction.x * 200  # 200是什么
    if position.y > 1000:  # 1000代表什么
        respawn()

3. 函数单一职责

每个函数只做一件事,做好一件事:

# ✅ 正确:职责分离
func handle_player_input() -> void:
    var input_direction = get_input_direction()
    apply_movement(input_direction)
    check_interaction_input()

func get_input_direction() -> Vector2:
    return Input.get_vector("move_left", "move_right", "move_up", "move_down")

func apply_movement(direction: Vector2) -> void:
    velocity = direction * move_speed
    move_and_slide()

func check_interaction_input() -> void:
    if Input.is_action_just_pressed("interact"):
        try_interact()

# ❌ 错误:一个函数做太多事
func handle_everything() -> void:
    # 处理输入
    var direction = Input.get_vector("move_left", "move_right", "move_up", "move_down")
    # 处理移动
    velocity = direction * move_speed
    move_and_slide()
    # 处理交互
    if Input.is_action_just_pressed("interact"):
        # 检查交互对象
        var interactables = get_nearby_interactables()
        # 执行交互
        for obj in interactables:
            obj.interact()
    # 更新UI
    update_health_bar()
    # 播放音效
    play_footstep_sound()

4. 隐藏复杂性

复杂的逻辑应该被封装,对外提供简洁的接口:

# ✅ 正确:封装复杂逻辑
class_name NetworkManager

func login(username: String, password: String, callback: Callable) -> int:
    return _make_request("POST", "/auth/login", {
        "username": username,
        "password": password
    }, callback)

func _make_request(method: String, endpoint: String, data: Dictionary, callback: Callable) -> int:
    # 复杂的网络请求逻辑被隐藏
    var request = HTTPRequest.new()
    var request_id = _generate_request_id()
    
    # 设置请求头、处理认证、错误重试等复杂逻辑
    _setup_request_headers(request)
    _handle_authentication(request)
    _setup_retry_logic(request, callback)
    
    return request_id

# 使用时非常简洁
func _on_login_button_pressed() -> void:
    NetworkManager.login(username_input.text, password_input.text, _on_login_response)

📋 编码最佳实践

1. 类型安全

始终使用严格的类型声明:

# ✅ 正确:明确的类型声明
var player_health: int = 100
var move_speed: float = 200.0
var player_name: String = "Player"
var inventory_items: Array[Item] = []
var config_data: Dictionary = {}

func calculate_damage(base_damage: int, multiplier: float) -> int:
    return int(base_damage * multiplier)

# ❌ 错误:缺少类型信息
var health = 100  # 类型不明确
var speed = 200   # 可能是int也可能是float

func calculate_damage(base, mult):  # 参数类型不明确
    return base * mult  # 返回类型不明确

2. 错误处理

主动处理可能的错误情况:

# ✅ 正确:完善的错误处理
func load_save_file(file_path: String) -> Dictionary:
    if not FileAccess.file_exists(file_path):
        push_warning("存档文件不存在: " + file_path)
        return {}
    
    var file = FileAccess.open(file_path, FileAccess.READ)
    if file == null:
        push_error("无法打开存档文件: " + file_path)
        return {}
    
    var json_string = file.get_as_text()
    file.close()
    
    if json_string.is_empty():
        push_warning("存档文件为空: " + file_path)
        return {}
    
    var json = JSON.new()
    var parse_result = json.parse(json_string)
    if parse_result != OK:
        push_error("存档文件JSON格式错误: " + file_path)
        return {}
    
    return json.data

# ❌ 错误:没有错误处理
func load_save_file(file_path: String) -> Dictionary:
    var file = FileAccess.open(file_path, FileAccess.READ)
    var json_string = file.get_as_text()
    file.close()
    var json = JSON.new()
    json.parse(json_string)
    return json.data  # 任何步骤出错都会崩溃

3. 资源管理

及时释放不需要的资源:

# ✅ 正确:资源管理
class_name AudioManager

var audio_players: Array[AudioStreamPlayer] = []
var max_concurrent_sounds: int = 10

func play_sound(sound: AudioStream, volume: float = 0.0) -> void:
    # 清理已完成的音频播放器
    _cleanup_finished_players()
    
    # 限制并发音频数量
    if audio_players.size() >= max_concurrent_sounds:
        _stop_oldest_player()
    
    var player = AudioStreamPlayer.new()
    add_child(player)
    player.stream = sound
    player.volume_db = volume
    player.finished.connect(_on_audio_finished.bind(player))
    player.play()
    
    audio_players.append(player)

func _cleanup_finished_players() -> void:
    audio_players = audio_players.filter(func(player): return player.playing)

func _on_audio_finished(player: AudioStreamPlayer) -> void:
    audio_players.erase(player)
    player.queue_free()

4. 性能优化

编写高效的代码:

# ✅ 正确:性能优化的代码
class_name EnemyManager

var enemies: Array[Enemy] = []
var update_timer: float = 0.0
const UPDATE_INTERVAL: float = 0.1  # 每100ms更新一次

func _process(delta: float) -> void:
    update_timer += delta
    if update_timer >= UPDATE_INTERVAL:
        _update_enemies(update_timer)
        update_timer = 0.0

func _update_enemies(delta_time: float) -> void:
    # 只更新屏幕附近的敌人
    var camera_pos = get_viewport().get_camera_2d().global_position
    var screen_size = get_viewport().get_visible_rect().size
    
    for enemy in enemies:
        if _is_enemy_near_screen(enemy, camera_pos, screen_size):
            enemy.update_ai(delta_time)

func _is_enemy_near_screen(enemy: Enemy, camera_pos: Vector2, screen_size: Vector2) -> bool:
    var distance = enemy.global_position.distance_to(camera_pos)
    var max_distance = screen_size.length() * 0.6  # 屏幕对角线的60%
    return distance <= max_distance

# ❌ 错误:性能问题
func _process(delta: float) -> void:
    # 每帧更新所有敌人,无论是否可见
    for enemy in enemies:
        enemy.update_ai(delta)  # 可能有数百个敌人
        # 每帧进行复杂计算
        var path = enemy.find_path_to_player()
        enemy.follow_path(path)

🎯 代码审查标准

审查清单

在提交代码前,请检查以下项目:

功能性

  • 代码实现了预期功能
  • 处理了边界情况和错误情况
  • 添加了必要的测试用例

可读性

  • 变量和函数名称清晰明确
  • 代码结构逻辑清晰
  • 添加了必要的注释

性能

  • 避免了不必要的计算
  • 正确管理了资源生命周期
  • 使用了合适的数据结构

规范性

  • 遵循了项目命名规范
  • 使用了正确的类型声明
  • 符合架构设计原则

代码示例评分

优秀代码示例 (A级)

extends CharacterBody2D
class_name Player

## 玩家角色控制器
## 
## 负责处理玩家输入、移动和基础交互
## 使用事件系统与其他组件通信

@export_group("Movement")
@export var move_speed: float = 200.0
@export var acceleration: float = 1000.0
@export var friction: float = 800.0

@export_group("Interaction")
@export var interaction_range: float = 50.0

@onready var sprite: Sprite2D = %Sprite2D
@onready var animation_player: AnimationPlayer = %AnimationPlayer
@onready var interaction_area: Area2D = %InteractionArea

var _current_interactable: Interactable = null

func _ready() -> void:
    _setup_interaction_area()
    _connect_signals()

func _physics_process(delta: float) -> void:
    _handle_movement(delta)

func _input(event: InputEvent) -> void:
    if event.is_action_pressed("interact"):
        _try_interact()

func _handle_movement(delta: float) -> void:
    var input_direction := _get_movement_input()
    _apply_movement(input_direction, delta)
    _update_animation(input_direction)

func _get_movement_input() -> Vector2:
    return Input.get_vector("move_left", "move_right", "move_up", "move_down")

func _apply_movement(direction: Vector2, delta: float) -> void:
    if direction != Vector2.ZERO:
        velocity = velocity.move_toward(direction * move_speed, acceleration * delta)
    else:
        velocity = velocity.move_toward(Vector2.ZERO, friction * delta)
    
    move_and_slide()

func _update_animation(direction: Vector2) -> void:
    if direction.length() > 0.1:
        animation_player.play("walk")
        sprite.flip_h = direction.x < 0
    else:
        animation_player.play("idle")

需要改进的代码 (C级)

extends CharacterBody2D

var speed = 200
var player
var enemies = []

func _ready():
    player = self

func _process(delta):
    var dir = Vector2()
    if Input.is_action_pressed("ui_left"):
        dir.x -= 1
    if Input.is_action_pressed("ui_right"):
        dir.x += 1
    if Input.is_action_pressed("ui_up"):
        dir.y -= 1
    if Input.is_action_pressed("ui_down"):
        dir.y += 1
    
    velocity = dir * speed
    move_and_slide()
    
    for enemy in enemies:
        if position.distance_to(enemy.position) < 100:
            print("near enemy")

🚀 持续改进

重构指导原则

  1. 小步快跑 - 每次只重构一小部分
  2. 测试保护 - 重构前确保有测试覆盖
  3. 功能不变 - 重构不改变外部行为
  4. 逐步优化 - 持续改进代码质量

技术债务管理

# 使用TODO注释标记技术债务
# TODO: 重构这个函数,职责过多
# FIXME: 这里有性能问题,需要优化
# HACK: 临时解决方案,需要找到更好的方法
# NOTE: 这里的逻辑比较复杂,需要详细注释

记住:优秀的代码不仅能工作,更要易于理解、维护和扩展。追求代码质量是每个开发者的责任!