/OOP-Cash

Yet another cash management tool, project of Object Oriented course, Spring 2017, Tsinghua University

Primary LanguageCApache License 2.0Apache-2.0

Expensé 项目说明

> Yet another accounting tool

写在前面

The project is made with ❤ by:

  • 2016010981 陈晟祺 (代码中作者标识为 Harry Chen)
  • 2016010912 王安冬 (代码中作者标识为 Ice Coffee)
  • 2016010997 牛辰昊 (代码中作者标识为 牛辰昊)
  • 2015010467 钮泽平 (代码中作者标识为 Zeping Niu)

除了本页以外,整个项目的文档使用 Doxygen 生成,文中提到的各个实现细节均配置了相应的跳转链接,点击可直接导向相应的类/文件进行查看。我们基本为每个类、每个源文件都加上了可读的注释(包括版权头和简要说明),这些均可以直接在本 HTML 文档中进行查看(需要进入相应的 .h/.cpp 文件,而非类本身,才能看到版权文件头)。文档均使用英语撰写。

由于我们使用的演示平台比较特殊,只有付费用户才能进行离线导出,而采用逐一截图的方法让我无法接受。故我在本文档中基本包含了展示的 slides 的所有内容,未将其一同打包。

如果尝试编译项目时遇到问题,请参见本文最后的“编译说明”部分。如有任何问题,可联系我:harry-chen at outlook.com

背景介绍

日常生活中,我们经常会面临几个重要的问题:我的钱去哪了?为什么我又没钱了?隔壁老王还欠我多少钱?

对于这些问题,养成记账的习惯可以避免很多不必要的麻烦。目前移动端,各色记账软件层出不穷;但在 PC 端,专门软件并不多,而且有着种种问题:太复杂(如 GNU Cash)、收费(如金蝶随手记)、杀鸡用牛刀(如 Excel)等等。

因此 ,我们开发了 Expensé,一款很 naïve 的记账软件,用来满足这个需求。

需求分析

  • 基本记账功能:简单记账法,含收入、支出、转账、借还款
  • 多账户支持:钱包、在线支付、银行卡等
  • 多用户支持:注册与登陆系统、用户数据隔离
  • 多货币支持:支持不同汇率的货币
  • 收支分类支持:饮食、交通、学习、在马路边捡到的……
  • 多条件组合查询:金额范围、日期范围、账户、时间、备注、状态等参数,其中的一个或多个
  • 统计与可视化:选定时间段的统计(年月日周)、条件查询结果的统计、统计图表(折线图、柱状图)的绘制
  • 数据持久化与备份:数据库连接与存储、数据导入导出(JSON)
  • 多前端实现:GUI、CLI(最终未实现)
  • 日志与审计:调试模式下完整的日志记录,生产模式则只记录错误

人员分工

注:任务量与列出的具体条数并非成正比

  • 陈晟祺(组长)

    • 项目整体架构、模型抽象
    • 需(xiang)求(mu)控(jing)制(li)
    • 后端数据综合处理、数据库连接
    • 账单组合查询
    • 模块整合、代码规范、文档统筹
    • 程序 i18n 框架与多语言翻译
    • 程序员催促师兼 Bug Hunter
  • 王安冬

    • 美工、GUI 设计
    • 主程序框架
    • 登陆窗口
    • 用户管理、设置
    • 数据导入导出
  • 钮泽平

    • 账单、增、删、改
    • 分类、账户、货币管理
    • 性能调优
  • 牛辰昊

    • 综合查询模块
    • 数据可视化(绘图)
    • 账单查询与详情呈现

框架设计

项目整体架构大致如下图:

本项目采用了比较经典的存储——持久——业务——展现四层逻辑,每层通过对应的接口与上下层沟通,用户请求通过每层的处理得到实现。

项目整体的 UML 图略去。各个类的 UML 图,以及各类之间的调用、依赖关系,均在文档中给出。

技术细节

各个核心模块在上述的需求分析以及项目架构中已有提及,解释说明与详细实现可见各个模块对应的类对应的文档(包括且不限于类页面、头文件页面、实现页面)。本节只补充代码文档中未涉及的部分。

设计模式(部分):

  • 模板方法(Template Method),见 [ItemManager](@ref ItemManager)
  • 生成器模式(Builder Pattern),见 [Query](@ref Query)
  • 单例模式(Singleton Pattern),见 [DatabaseHelper](@ref DatabaseHelper) 以及 [ItemSearcher](@ref ItemSearcher)
  • 策略模式(Strategy Pattern),见 [ApplyChangeStrategy](@ref ApplyChangeStrategy)
  • 抽象工厂模式(Abstract Factory Pattern),见 [PlotSystem](@ref PlotSystem)

设计**:

  • 借鉴与分享
    • 多使用现成的开源方案,不重复发明轮子
    • 项目以 Apache 2.0 协议开源
  • 前后端分离与解耦合
    • 后端:底层基础接口(增删改查)
    • 前端:增强功能(绘图、统计等)
  • 多线程并行
    • 异步进行耗时操作,渐进式加载
    • 分离UI线程与IO线程

开源资源

编译说明

环境说明

本项目在以下环境编译通过且能运行:

  • Qt 5.8.0 (windows-x86-msvc2015_64) with MSVC 14.0 (shipped with VS 2015 Update 3)
  • Qt 5.8.0 (windows-x86-msvc2015_64) with MSVC 14.1 (shipped with VS 2017 RC)
  • Qt 5.8.0 (mac-x64-clang) with Clang 8.1.0 (shipped with XCode 8.3.2)

在下列环境编译通过但未测试运行:

  • Arch Linux with Qt 5.9.0
  • Ubuntu 17.04 with Qt 5.8.0

在下列环境确认无法编译通过:

  • Qt <= 5.5.0

编译方法

本项目的编译还需要 qmake, CMake 以及 GNU Make 的支持。一般的编译步骤为:

  1. 单独编译 sqlpp11-connector-sqlite3 库:
git clone https://github.com/rbock/sqlpp11-connector-sqlite3.git
mkdir build && cd build
cmake ../sqlpp11-connector-sqlite3/
make
  1. 将得到的静态库文件(.a或者.lib)复制到本项目的 libs 目录下
  2. OOP-Cash.pro 文件中添加对该库的引用,如 LIBS += -Llibs -lyourlib
  3. 执行 qmake
  4. 执行 make release
  5. 如要得到英语版本,可将源码目录中 lang/en_US.qm 复制到编译得到程序的工作目录

注:上述步骤只可用于编译 Release 版本,编译 Debug 版本需要将上述的库也进行 Debug 编译。我在项目中已经包含了对某些平台预编译的该库(for Windows 10 x64 & macOS 10.10)并在 OOP-Cash.pro 中进行了配置,但依旧不保证可以立刻正确编译运行。

样例数据库

跟随本程序提供一个样例数据库 sample.sqlite,可在程序第一次启动时加载。登录用的用户名为 Test,密码为 1234。

多语言支持

  1. 在项目根目录执行 lupdate lang/en_US.ts
  2. 使用 Qt Linguist 等工具更新翻译
  3. 执行 lrelease lang/en_US.ts
  4. 用新得到的 lang/en_US.qm 覆盖老的文件

项目总结

虽然之前我有过一些开发经验,但这是我第一次用小组合作的形式与他人完整地开发一个程序。尽管代码量不大,也略显简陋,但是不失为一次很好的锻炼。在开发过程中,我们吸取了不少经验教训,在此列举一部分。

关于设计

让程序完全解耦合、每一个人各自为政,独立撰写各自的模块其实是很难的一件事情,这对整个系统的架构与抽象提出了非常高的要求——自然,我没有这样的水平。所以虽然我们的模块划分比较清晰,但在分工上依旧有不小的交叉。比如一个后端 API 设计的缺陷,只有在上层模块进行一些复杂的调用时才会被完整暴露出来;或者前端对接口的不合理使用和错误理解,也能从后端的日志中反映出来。于是每个人其实都对几乎所有的模块有过意见和自己的修改,这其实并非坏事。

另外,程序员们一直黑的产品经理,其实是十分重要的。对需求的控制与解读,是整个设计环节中最重要的部分。我们之前就经历了需求过多,坑填不完的情况。后来仔细思考,其实有一些部分是伪需求,是花架子;如一开始设想的 CLI 前端,其实并没有必要实现。做好每一个真正对用户有用的功能,才是应该追求的。

关于代码

重要的话说三遍: 规范很重要,规范很重要,规范很重要!

虽然向来容易引战,但是不管大括号换不换行、Tab 还是空格缩进、大驼峰还是小驼峰还是下划线,一个可读性良好并且在整个项目中保持一致的代码风格是关键。一方面,它完美地满足了我这类强迫症患者的需要;另一方面,它传递出我们对待这个项目认真的态度。在赶进度那几天,大家的代码都是糙快猛,留下了一堆隐患;其实最后的这几天,我们的一个重点就是规范代码中各种符号的命名。就算完整修改来不及,所有对外暴露的接口都需要得到规范。

另外,文档也是不可或缺的,这一点不言而喻。感谢 Doxygen 这么优秀的工具,能从我们的代码注释中生成美观、方便使用的程序文档。以前我写项目从没有这样的习惯,常常过了一整子回头就看不懂自己究竟干了什么、用了什么黑魔法。这种糟糕的工程习惯,终于在这次得到了改观。

关于合作

作为第一个超过两人合作的大作业,我在这方面可以说颇为费心。一方面,我坚持让队员使用 Git 管理源码,并严格遵循不得在主分支直接 commit 的约定,采用分支——合并的开发流程。事实证明这给我们带来了很大的便利:这样的工作流允许多个人同时并行开发,即使产生冲突也是很少一部分内容;另外,得益于 Git 完善的 blame 机制,写出 bug 的人能被第一时间找到,责任明确。

合作开发时难免会产生一些想法的冲突,作为队长如何妥善处理也很重要。虽然我的确经验比较丰富一些,但很多时候组员的设想与建议的确要比我想到的更好。这时候应该听取大家的建议。另外,作为程序员催促师,适当的提醒和督促有助于项目的稳定推进。

一些缺憾

限于时间、我们的技术水平等一系列因素,项目留下了一些缺憾,现列于于此。诚实地说,这个项目大概从 deadline 的第二天起就不会再进行任何开发了 。但我希望,在今后的其他项目中,我们可以补上这些遗憾。

  • 保留未解决的bug:QListWidget 选择某些项时崩溃,产生不明报错。在 macOS 上稳定复现, Windows 上无法复现。怀疑是 QListWidget 本身内部实现的问题,后期可能继续跟进。
  • GUI 美观度、布局灵活度不够
  • 在处理大量数据时仍然有卡顿现象,性能优化不到位
  • 未尝试现代 C++(如 C++ 14)的一些新特性
  • 程序功能依旧较少,too simple

结语

如果问我们在这个项目中学到了什么,或许不是某些特定的设计模式和语法;从构思到设计到开发的整个过程都给了我们很多启示。希望我们能保持热情,积累经验,继续努力。