软件工程结对作业(第二次之程序实现)
这个作业属于哪个课程 | 软件工程 |
---|---|
这个作业要求在哪里 | 作业要求 |
这个作业的目标 | 通过与队友的协作交流完成一个软件的设计,并进行测试与运用 |
学号 | 102202107&&102202140 |
2024秋软件工程第二次结对作业报告
项目信息
GitHub仓库地址: 102202107-102202140仓库
项目名称: ProjectPartner
结对成员与分工:
本次结对编程作业中,我们根据个人的技能和兴趣进行了明确的分工,以确保项目的高效和顺利进行。
郭心怡 (102202140)
- 使用Flutter框架进行应用的界面设计和开发。
- 实现用户认证和授权机制。
- 进行前端单元测试,确保代码质量和功能正确性。
王勤琛 (102202107)
- 使用Flutter框架进行应用的界面设计和开发。
- 实现项目发布模块,包括表单输入和数据展示。
共同责任
- 项目管理:使用GitHub进行代码版本控制,并通过Pull Request进行代码审查。
- 文档编写:共同维护项目文档,包括README和PSP表格的更新。
- 测试和部署:协作进行系统集成测试,确保项目按时发布。
PSP2.1 表格
个人软件开发流程阶段 | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|
Planning | 计划 | 60 |
Estimate | 估计 | 40 |
Development | 开发 | 640 |
Analysis | 需求分析 | 120 |
Design Spec | 生成设计文档 | 40 |
Design Review | 设计复审 | 30 |
Coding Standard | 代码规范 | 20 |
Design | 具体设计 | 80 |
Coding | 具体编码 | 500 |
Code Review | 代码复审 | 60 |
Test | 测试 | 160 |
Reporting | 报告 | 60 |
Test Report | 测试报告 | 20 |
Size Measurement | 计算工作量 | 20 |
Postmortem & Process Improvement Plan | 事后总结与过程改进计划 | 40 |
合计 | 总计 | 2000 |
代码实现思路
本次项目“ProjectPartner”使用Flutter框架进行移动端应用开发,实现了用户界面和用户体验。以下是具体的实现思路:
-
需求分析:通过用户访谈,我们确定了应用需要实现的核心功能,包括项目发布、项目浏览和实时交流等。
-
系统设计:基于需求分析,我们设计了应用的用户界面和用户交互流程。
-
前端开发:使用Flutter框架构建应用的用户界面和用户体验。
-
数据存储:使用本地存储(如
shared_preferences
)或Flutter推荐的数据库(如sqflite
)来保存项目数据。 -
测试:编写单元测试和集成测试,确保代码质量和系统稳定性。
-
部署:将应用打包并发布到相应的平台。
关键实现的流程图
以下是使用Mermaid格式绘制的项目发布流程图:
重要的/有价值的代码片段
首页底部导航栏实现
return Scaffold(
appBar: AppBar(
title: Text('欢迎, ${widget.userName}'),
),
body: pages[_selectedIndex],
bottomNavigationBar: BottomNavigationBar(
items: const [
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: '主页',
),
BottomNavigationBarItem(
icon: Icon(Icons.message),
label: '消息',
),
BottomNavigationBarItem(
icon: Icon(Icons.person),
label: '个人',
),
],
currentIndex: _selectedIndex,
selectedItemColor: Colors.blue,
unselectedItemColor: Colors.grey,
onTap: onItemTapped,
),
);
解释:此代码段实现了一个底部导航栏,允许用户在不同的页面(主页、消息、个人)之间切换。BottomNavigationBar控件使用onTap回调来处理用户的点击事件,更新当前选中的页面。
消息列表和点击事件处理
class MessageList extends StatelessWidget {
final List<Message> messages;
final ValueChanged<String> onMessageTap;
const MessageList({
Key? key,
required this.messages,
required this.onMessageTap,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: messages.length,
itemBuilder: (context, index) {
final message = messages[index];
return ListTile(
leading: CircleAvatar(
backgroundImage: NetworkImage(message.avatarUrl),
),
title: Text(message.sender),
subtitle: Text(message.content),
onTap: () => onMessageTap(message.userId),
);
},
);
}
}
}
解释:MessageList是一个无状态组件,用于展示一个消息列表。每个消息项是一个ListTile,包含发送者的头像、名称和消息内容。当用户点击某个消息时,会触发onMessageTap回调,传递被点击消息的用户ID。
附加特点设计与展示
设计的创意独到之处
意义:在本次项目中,我们特别注重提升用户体验和应用的实用性。一个独特的设计是我们实现了一个离线浏览功能,它允许用户在没有网络连接的情况下,依然可以浏览本地保存的项目信息。这个设计的意义在于提高了应用的可用性和用户体验,尤其是在网络不稳定或无法连接到服务器的情况下,用户仍然能够访问和查看项目信息。
实现思路
为了实现离线浏览功能,我们采用了Flutter的shared_preferences
插件来保存用户浏览过的项目信息。这样,即使在离线状态下,用户也可以访问这些信息。我们首先将项目数据保存到本地存储中,然后在应用启动或用户请求时,从本地存储中读取数据并显示给用户。
重要的代码片段
Future<void> _saveProjectToLocalStorage(String projectName) async {
final storage = await SharedPreferences.getInstance();
storage.setString('project_name', projectName);
}
// 在应用启动时读取本地存储的项目信息
Future<void> _loadProjectsFromLocalStorage() async {
final storage = await SharedPreferences.getInstance();
String? projectName = storage.getString('project_name');
if (projectName != null) {
// 加载项目信息到UI
}
}
解释:上述代码片段展示了如何使用shared_preferences来保存和读取项目名称到本地存储。_saveProjectToLocalStorage函数用于保存项目名称,而_loadProjectsFromLocalStorage函数用于在应用启动或需要时从本地存储中读取项目信息。这样,即使在没有网络的情况下,用户也能够查看之前保存的项目信息。
实现成果展示
具体代码组成:
-
项目结构:首先,定义整个项目的结构,包括主页面(HomeScreen)、登录页面(LoginPage)、注册页面(RegisterPage)、聊天页面(ChatPage)、课程页面(CoursePage)以及项目详情页面(ProjectDetailPage 和 TeamInfoPage)。
-
状态管理:使用Flutter的状态管理功能,如
StatefulWidget
和setState
,来更新页面中的UI元素。对于登录和注册页面,使用TextEditingController
和FocusNode
来管理输入框的文本和焦点。
-
数据传递:通过构造函数或全局变量(如
SharedPreferences
)来传递数据,如在登录页面验证成功后,将用户名和密码保存到SharedPreferences
中,并在后续页面中使用。
-
网络通信:与服务器进行通信,使用Flutter的HTTP客户端库来发送请求和接收响应。
-
页面导航:使用
Navigator
来在不同页面之间进行导航,如从主页面跳转到聊天页面或项目详情页面。
-
UI设计:使用Flutter的Material Design组件来构建UI,如
AppBar
、ListView
、TextField
、ElevatedButton
等。
ProjectPartner 项目目录结构
以下是 ProjectPartner 项目的目录组织结构,描述了项目中每个主要文件夹和文件的用途:
ProjectPartner/
├── .vscode/ # Visual Studio Code 配置文件
├── android/ # Android 项目配置和资源文件
├── ios/ # iOS 项目配置和资源文件
├── lib/ # Dart 源代码,包含应用的逻辑
│ ├── models/ # 数据模型
│ ├── pages/ # 应用的主要页面
│ │ ├── academic_page.dart
│ │ ├── chat_page.dart
│ │ ├── competition_page.dart
│ │ ├── course.dart
│ │ ├── create_team_page.dart
│ │ ├── home_page.dart
│ │ ├── login_page.dart
│ │ ├── notification_page.dart
│ │ ├── personal_information.dart
│ │ ├── profile.dart
│ │ ├── register_page.dart
│ │ ├── team_info_page.dart
│ │ └── testlogin.dart
│ ├── widget/ # 应用的自定义组件和部件
│ ├── message_list.dart
├── main.dart # 应用的入口文件
├── linux/ # Linux平台的配置文件
├── macos/ # macOS平台的配置文件
├── test/ # 测试目录
└── README.md # 项目说明文档
使用说明
下载apk文件,在Android手机上安装程序(注意:本应用仅在Android手机上可安装);注册账号,进入应用,开始使用。
单元测试
由于篇幅限制,这里仅展示我们为Message
类创建的一个单元测试示例。
选用的测试工具
本项目使用Flutter自带的测试框架进行单元测试,即package:test
。
如何学习单元测试
- 阅读官方文档:阅读
package:test
的官方文档,了解其基本用法和API。 - 观看教程:在线有许多关于Flutter单元测试的教程,例如在Bilibili上搜索Flutter单元测试教程。
- 实践:通过实际编写测试代码来加深理解。
简易教程
-
添加依赖
在
pubspec.yaml
文件中添加test依赖:dev_dependencies: flutter_test: sdk: flutter test: ^1.16.8
2.创建测试文件
在 test
目录下创建测试文件,例如 message_test.dart
。
3.编写测试代码
import 'package:flutter_test/flutter_test.dart';
import 'package:your_project/models/message.dart';
void main() {
group('Message class', () {
test('should create a message with correct properties', () {
final message = Message(
sender: 'Alice',
content: 'Hello, Bob!',
time: '10:00',
avatarUrl: 'https://example.com/avatar.png',
userId: '12345',
);
expect(message.sender, 'Alice');
expect(message.content, 'Hello, Bob!');
expect(message.time, '10:00');
expect(message.avatarUrl, 'https://example.com/avatar.png');
expect(message.userId, '12345');
});
test('should throw if sender is null', () {
expect(
() => Message(sender: null, content: 'Hello, Bob!', time: '10:00', avatarUrl: 'https://example.com/avatar.png', userId: '12345'),
throwsAssertionError,
);
});
});
}
4.运行测试
在终端中执行以下命令来运行测试:
flutter test
5.说明测试的函数
第一个测试
- 目的:验证
Message
类是否可以正确地创建一个消息对象,并验证其属性是否符合预期。 - 操作:创建一个
Message
对象,并检查其sender
、content
、time
、avatarUrl
和userId
属性是否与创建时传入的值一致。
第二个测试
- 目的:验证当
sender
参数为null
时,构造函数是否抛出AssertionError
。 - 操作:尝试创建一个
Message
对象,其中sender
参数为null
,并捕获预期的异常。
构造测试数据的思路
正常情况
- 操作:构造符合预期的正常数据,确保对象可以正确创建。例如,使用有效的字符串、数字等作为属性值。
边界情况
- 操作:考虑边界值,例如字符串为空、数字为0或负数等,以确保对象在极端情况下的行为。
异常情况
- 操作:构造会导致错误或异常的数据,例如传递非法格式的数据,确保代码能正确处理这些异常情况。
如何考虑将来测试人员的***难
覆盖所有分支
- 策略:确保测试覆盖所有的代码分支,包括正常路径和异常路径,以确保代码在各种情况下都能正常工作。
使用Mock
- 策略:对于依赖外部资源或服务的部分,使用Mock对象来模拟行为,确保测试的独立性和稳定性。
随机数据
- 策略:使用随机数据生成测试数据,以覆盖更多未预见的情况,增强测试的全面性。
通过这些方法,我们可以确保单元测试的全面性和健壮性,应对未来可能的挑战。
Github的代码签入记录
遇到的代码模块异常或结对困难及解决方法
问题描述
在实现项目伙伴招募功能时,我们遇到了一个难题:如何确保用户在离线状态下仍然可以访问和查看项目信息。此外,在结对编程过程中,我们发现代码风格不一致和沟通不畅也严重影响了开发效率。
做过哪些尝试
-
离线数据访问问题:
- 初步尝试使用本地数据库存储项目信息,但在应用重启后发现数据丢失的问题。
- 尝试使用
shared_preferences
插件来持久化存储项目信息。
-
代码风格不一致:
- 分别按照个人习惯编写代码,导致代码难以阅读和维护。
- 尝试统一代码风格,但缺乏明确的编码规范。
-
沟通不畅:
- 开发过程中缺乏有效沟通,导致开发进度不同步。
- 尝试使用即时通讯工具加强沟通,但效果有限。
是否解决
-
离线数据访问问题:
- 通过使用
shared_preferences
成功解决了数据持久化问题,现在用户即使在离线状态下也能访问项目信息。
- 通过使用
-
代码风格不一致:
- 制定了一套明确的编码规范,并使用代码格式化工具如
Dartfmt
来统一代码风格。
- 制定了一套明确的编码规范,并使用代码格式化工具如
-
沟通不畅:
- 开始定期举行简短的站立会议,讨论进展和问题,大大提升了团队协作效率。
有何收获
通过解决这些问题,我们学会了以下几点:
- 使用
shared_preferences
进行数据持久化:对于离线数据访问的处理有了更深的理解。 - 制定编码规范:明白了统一代码风格对于项目维护的重要性。
- 团队协作:加强沟通对于提升团队合作效率的重要性,学会了更有效的沟通方法。
这些经验不仅帮助我们完成了项目,也为未来的开发工作提供了宝贵的经验。
队友评价 - 王勤琛
值得学习的地方
- 技术能力:王勤琛在Flutter框架的使用上非常熟练,能够快速实现复杂的UI和交云逻辑。
- 问题解决:面对开发中的难题,她总能冷静分析并找到有效的解决方案。
- 责任心:王勤琛对自己的工作非常负责,总是确保代码的质量和功能的正确性。
需要改进的地方
- 时间管理:在项目截止日期临近时,王勤琛有时会因为时间管理不当而感到压力。
队友评价 - 郭心怡
值得学习的地方
- 创新思维:郭心怡在设计应用功能时总能提出创新的想法,为项目增添亮点。
- 团队合作:她非常注重团队合作,总是愿意协助队友解决问题,是团队中不可或缺的一员。
- 学习态度:郭心怡对新技术充满好奇心,总是积极学习并将其应用到项目中。
需要改进的地方
- 细节关注:在编码过程中,有时会忽略一些细节问题。