Backgound
最近在看的「Python Crash Course」的第十二章到第十四章介绍了一个游戏:ALIEN_INVASION.
花了几天的时间把代码copy了一遍,作者的思路还是非常清晰的,遥想当年大一刚学C语言期末设计,接一个「弹球碰撞消除」的游戏,用EasyX实现,算是两人通力合作构建的屎山代码,各种常量乱开,文件引用混乱,对比这个游戏简直惨不忍睹.游戏源码都没有珍藏的必要了直接删了
后来转专业需要重修C++,要求写一个学生成绩管理系统,鼠鼠考完各种期末,已经只剩一天来完成,没办法只能手搓一个黑窗程序跑跑看了,也没有用多文件管理一个main写到底,约600来行的样子,修了些边角也跑的还行,不过后来还是看不下去分了几个头文件放进去了.但从现在的角度来看,这样写工程项目的姿势还是不太规范;
不过趁着这个机会,也能看看优秀的工程项目是如何组织的是怎么样的,这个ALIEN_INVASION毕竟涉及多次重构,因此如果学会版本控制系统可以很方便的回退,整个项目也托管到我github的ALIEN仓库中了,这里是项目的源代码;
可能有些许的注释或者参数调用顺序和书上不一致,不过命名都是一样的(自己敲的时候注意到了不过懒得再重构了,不影响阅读),另外,对比原书的程序,另外添加了记录历史最高分的功能,并用pyinstaller打包一并托管到了git上.
Content
需求分析
需求分析往往是最容易忽视的一步,它能避免你和你的队友在做到项目的一半突然面面相觑突然不知道该做什么。在我还是初学者时,没有认识到老师甚至还让我们做ppt当场做报告的良苦用心,应付老师要的ppt草草了事;
但这是不对的。需求分析应该是不用做ppt也值得**专门撰写报告阐释清楚的**,如果能在网上能找到效果最好能用图片清晰地阐释要完成地效果,如果不能也应该对功能地效果做一波预测,用文字尽量描述清楚。
做需求分析还可以提早建立架构,预测可能会发生的问题,不过这往往需要一点经验,比如这个游戏如果是多波次怪物,应该要及时对已经清理的怪物内存删除,还有已经消失在屏幕之外的子弹,也应该要及时回收内存,防止影响性能。
可惜我到现在也算做了五六个大项目,一次也没有做好这一步,希望能汲取这个ALIEN游戏的经验。不过用这种博客的形式记录下来应该是可以记很久的。
先跑一个demo
面对一个大项目,应该**先搭一个空框架、或者叫空demo**,这个demo应该没有任何功能,比如说这个外星人入侵游戏,第一个demo就是用pygame把窗口调出来,然后关闭。
事实上,这就可以当作第一版了,如果从一开始你就有打包的需求,你还可以验证下这个游戏能否被打包。做完这个demo,你就可以往里面增添功能了。
做demo是很有必要的,它往往与版本控制系统的好处结合起来,大大提高开发效率。
建立一个settings
多数项目或多或少都会用到一些参数,**请将这些参数都放在一起**,用具体有意义的名字去调用它们,而不是要修改它们的时候去整个项目ctrl+F;
就像这个游戏一样,所有的类(Alien,Ship,Bullet,Screen...)都有共同的属性settings,共同享有ai_settings,这可以你方便地修改参数,也可以提高代码的健壮性和鲁棒性。
版本控制
每添加一个功能,应该都要立马调试,或者写测试模块(对于这个游戏当然就是运行),确保你当前的代码没有问题后提交到版本控制中,(或进一步地推到远端,如果只有你一个人开发地话)。
请记住,欲速而不达,错误往往发生在不起眼的小地方,当一味地去追求进度而最终项目一跑有bug,很难定位到bug在什么地方,因为你需要观察整个项目。
如果遇到实在无法解决的bug,你还可以轻松的利用git回退到上个可运行的状态,有时再写一遍可能比改这个bug还要来得方便。
<details>
<summary>当然,如果你的队友不会使用git,你有两个选择:</summary>
1. 教他使用git,发教程或视频(×)
2. 舍弃这个队友,换下一个(√)
当然我还没遇到过协同开发可能会遇到的冲突问题,因此没有发言权,不过我自己有时倒自己折腾可能会出现冲突,到时候另说,但换一个队友确实能让你省下不少烦心事。不过在找队友之前,确保你有独自carry的能力.
</details>
及时重构,封装,写注释和功能文档
一个清晰的代码结构和文件结构是开发能持续进行的基础,因为很难做到天天熬夜开发(选择熬夜开发往往是因为连续的工作才会有连续的思路,但是如果你做到这点,你的工作时间可以被切开并且基本可以随时进行);
一个良好的代码风格应该就是**看一眼模块或者函数的名字就知道在做什么事情**,实在不行就再配合注释和文档,每一个模块都有相应的入口方便再次调用,做到这些,你就可以第二天睡醒也能知道前一天的工作到底做了什么事情,还能方便你的队友接手或者阅读,也能尽量避免项目变成屎山。
如果你在做学校的大作业,最后会让你提交报告的话,这些中间文档也能很好地帮助你。
请再次记住,欲速而不达,因为这不是比赛。
工程目录结构
文章写到这里之前好像都是意淫和不知所云,不过还是打算放点有点意义的东西来记录一下:
-
C/C++ 项目结构 在 C++ 中,一个良好的工程项目结构通常意味着你的代码易于管理、可读性强、易于其他开发人员理解、易于测试和维护。以下是一个典型的C++项目结构:
MyProject/ ├── CMakeLists.txt // 或其他编译系统配置文件 ├── README.md // 项目说明文件 ├── docs/ // 文档目录 ├── third_party/ // 第三方库 ├── include/ // 头文件目录 │ └── MyProject/ // 项目自己的头文件 │ ├── module1.hpp // 模块1的头文件 │ └── module2.hpp // 模块2的头文件 ├── src/ // 源文件目录 │ ├── module1.cpp // 模块1的实现 │ ├── module2.cpp // 模块2的实现 │ └── main.cpp // 主程序入口 ├── libs/ // 如果有静态或动态库 │ ├── libmodule1.a // 静态库文件 │ └── libmodule2.so // 动态库文件 ├── tests/ // 测试代码目录 │ ├── test_module1.cpp // 模块1的测试代码 │ └── test_module2.cpp // 模块2的测试代码 └── build/ // 构建目录(通常在 .gitignore 中) └── ... // 生成文件,例如编译后的目标文件和可执行文件
在这个结构中,
CMakeLists.txt
是一个 CMake 配置文件,你可以用它来定义编译选项,如添加子目录、指定编译器选项、查找依赖库等。如果你不使用 CMake,可能会有一个不同的构建脚本或Makefile。docs
目录用于存放项目文档,如开发者指南、API 文档等。third_party
目录用于所有的第三方依赖,使得它们与主项目代码分开。include
目录通常包含所有公共头文件(.hpp
或.h
),这些文件将会被源文件(.cpp
)或其他项目所包含。src
目录包含所有的源文件,.cpp 文件应该包括与它们相应的头文件,并包含这些类或函数的实现代码。libs
目录如果你有自己的库,这个目录则包含这些库文件。tests
目录包含单元测试或其他测试相关的代码,使用某些测试框架(如 Google Test)可以帮助你执行自动化测试。build
目录是一个用来存放编译生成的所有中间文件、目标文件和最终可执行文件的地方。它不应该被提交到版本控制系统中。为你的项目添加
.gitignore
文件,以避免意外提交编译生成的可执行文件、中间文件或其他不需要的文件。 -
Golang项目结构 在 Go 语言中,一个良好的工程项目结构有助于维护性、可读性和包的适当划分。随着 Go 1.11 引入的 go modules,对于项目的布局变得更加灵活。以下是 Go 项目的推荐结构:
MyGoProject/ ├── go.mod // Go 模块文件,声明模块路径和依赖项 ├── go.sum // go.mod 文件的检查和依赖项的确切版本 ├── README.md // 项目说明 ├── .gitignore // 忽略不需要版本控制的文件 ├── cmd/ // 主要应用程序的目录 │ ├── myapp/ // 'myapp' 应用程序的特定目录 │ │ └── main.go // 'myapp' 应用程序的入口 ├── pkg/ // 外部应用程序可以使用的库代码 │ ├── mylib/ // 可以被外部应用程序引用的包 ├── internal/ // 私有应用程序和库代码 │ ├── mypkg/ // 仅限当前模块的内部包 ├── api/ // API 协议定义,如 OpenAPI/Swagger specs ├── web/ // Web 应用特有的组件: 静态文件, 模板等 ├── configs/ // 配置文件模板或默认配置 ├── init/ // 系统初始化 (systemd, upstart, sysv) 和进程管理/监控 (runit, supervisor) 配置 ├── scripts/ // 构建脚本、分析工具等脚本 ├── build/ // 包装和持续集成 ├── deployments/ // IaaS, PaaS, system and container orchestration deployment配置和模板 (docker-compose, kubernetes/helm, mesos, terraform, bosh) ├── test/ // 额外的外部测试应用和测试数据 └── docs/ // 设计和用户文档
go.mod
和go.sum
文件定义了项目的模块路径和依赖项。cmd
目录包含应用程序的入口点。每个应用程序的目录都应该和你的可执行文件有着同样的名字。pkg
目录包含可以被外部应用程序使用的库代码,它可以被其他项目引用。internal
目录包含私有代码,也就是只有当前模块内的其他代码可以访问的包。api
目录可以包含 API 协议定义文件。web
目录可以包含与 Web 应用相关的组件。configs
目录包含了应用程序的配置文件。scripts
目录用于存储例如构建脚本等工具。build
目录和deployments
目录可以包含与构建、打包和部署相关的配置和模板。test
目录包含了外部的测试脚本和数据。docs
目录包括设计文档、用户文档和其他类型的文档。
记得,以上的目录结构并不是强制性的。在实际工作中,应按项目需求和团队习惯来灵活安排项目结构。例如,对于较小的项目,
pkg
和internal
目录可能不是必需的,因为所有代码可能都在cmd
子目录下。如有必要,可以为项目创建特定的目录结构,以确保项目的组织性和可维护性。 -
Python项目结构 在 Python 中,一个良好的工程项目结构同样有助于代码维护、合作以及确保项目的可扩展。下面是一个 Python 项目的推荐结构:
MyPythonProject/ ├── mypythonproject/ // 源码目录,包名与项目名同名 │ ├── __init__.py // 告诉 Python 这个目录是一个包 │ ├── module1.py // 模块文件 │ ├── module2.py │ └── subpackage1/ // 子包 │ ├── __init__.py │ └── module3.py ├── tests/ // 测试目录 │ ├── __init__.py │ ├── test_module1.py │ └── test_module2.py ├── docs/ // 文档目录 │ └── ... ├── scripts/ // 各种脚本,比如安装脚本、运行脚本等 │ └── ... ├── requirements.txt // 项目依赖文件 ├── setup.py // 安装、部署、打包的脚本 ├── README.md // 项目说明文件 ├── LICENSE // 许可证文件 └── .gitignore // git 忽略文件
mypythonproject/
:该目录包含所有的源代码。下辖的模块(.py
文件)可以按照功能进行划分; 如果模块较多,还可以按照逻辑进行分包处理。tests/
:专门针对测试的目录,存储测试代码和测试文档。通过模拟和测试工具确保代码的质量。通常,每个源代码模块都应该有一个相应的测试模块。docs/
:文档目录用于存储项目文档,如设计文档,用户手册等。若使用Sphinx之类的工具,它的源文件也会存放于此。scripts/
:该目录用于存放用于安装、设置和执行任务的脚本。requirements.txt
:清单文件中列出了项目需要的外部依赖包,可以通过执行pip install -r requirements.txt
来批量安装依赖。setup.py
:Python 的构建脚本,用于打包和安装你的项目。运行python setup.py install
来安装包。README.md
:一个Markdown文件,包含项目的概述、使用方法、安装指南等重要信息。LICENSE
:该文件用来声明项目的许可证类型,指明他人可以如何使用您的代码。.gitignore
:列出不应该上传到 git 版本控制系统中的文件和目录。
一个好的项目结构能够让新团队成员更快地理解项目布局,也能够确保当项目变得复杂时,各个部分之间可以低耦合、高内聚地工作。当然,上述结构并不是固定的,结构应该根据项目的特点和团队的喜好适应性地调整。对于非常小的项目,可能不需要这么多的目录和结构。对于大型或标准化的 Python 项目,可以遵循这个基本骨架作为起点。
Remark
本来随便写点的,结果copy一下还挺多东西的,主要还是无聊的碎碎念,能不能涨教训还另说。
打算再写点数据分析的笔记之类的,把之前matlib笔记也补充完。
下学期的大作业只会更多,要寄...