第二次结对编程作业
1、在文章开头给出结对同学的博客链接、本作业博客的链接、你所Fork的同名仓库的Github项目地址(2分)
UI视频地址:https://pan.baidu.com/s/1Lv-FIDN4rfea41MYvaKYoA
陈钰蕙(本作业):https://www.cnblogs.com/cyhui/p/11681503.html
郑雅芳:https://www.cnblogs.com/YvonneLhy/p/11681526.html
Github地址: https://github.com/YvonneLhy/ThirteenWater
2、给出具体分工(2分)
陈钰蕙:负责前端和美工
郑雅芳:负责后端和美工
3、给出PSP表格(2分)
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 60 | 50 |
Estimate | 估计这个任务需要多少时间 | 60 | 50 |
Development | 开发 | 3600 | 4500 |
Analysis | 需求分析 (包括学习新技术) | 420 | 540 |
Design Spec | 生成设计文档 | 60 | 60 |
Design Review | 设计复审 | 30 | 40 |
Coding Standard | 代码规范 (为目前的开发制定合适的规范) | 30 | 20 |
Design | 具体设计 | 360 | 400 |
Coding | 具体编码 | 1440 | 2000 |
Code Review | 代码复审 | 60 | 90 |
Test | 测试(自我测试,修改代码,提交修改) | 1200 | 1350 |
Reporting | 报告 | 140 | 180 |
Test Report | 测试报告 | 60 | 80 |
Size Measurement | 计算工作量 | 20 | 20 |
Postmortem & Process Improvement Plan | 事后总结, 并提出过程改进计划 | 60 | 80 |
合计 | 3800 | 4730 |
4、解题思路描述与设计实现说明(15分)
网络接口的使用(3分)
参考博客: https://www.cnblogs.com/zhuawang/archive/2012/12/08/2809380.html
主要是http的get和post请求,有尝试过用Apache Jakarta Common下的子项目HttpClient(
参考博客: https://blog.csdn.net/wangpeng047/article/details/19624529
),但是感觉不太能理解,而且需要添加新的依赖有点麻烦的样子(?),用传统JDK自带的URLConnection比较看得懂的样子。就选择了第一篇参考博客提供的方法。
在下定决心搞懂Connection后,被憨憨嘲讽了:)
永福提供的提供的接口可以直接复制使用,只是需要添加一些依赖包,详细可见参考博客:https://blog.csdn.net/bigbigsman/article/details/90707669
代码组织与内部实现设计(类图)(6分)
参考代码: https://github.com/WangYunZYJ/PokerProvider
一开始没有什么头绪,开会的时候问了一下其他人,说GitHub上有开源码,就去找了一下,这个参考代码包括了发牌系统,加上对JAVA不太熟悉,就研究了很久。最后以其中的包AlgorithmByBai为基础,进行修改和编码。
代码组织和内部实现设计类图如下
前端部分:
算法部分:
说明算法的关键与关键实现部分流程图(6分)
建立了Card和Choice对象,用于存储牌和三墩,同时建立array对象,记录有1、2、3、4张同花色牌型和同大小牌型的情况,便于算法分析整理。主要实现算法的类是Player,流程图如下所示
//xmind画流程图好像有点丑……
5、关键代码解释(3分)
贴出你认为重要的/有价值的代码片段,并解释(3分)
前端部分:
//POST
new Thread(new Runnable() {
@Override
public void run() {
try {
URL url = new URL("https://api.shisanshui.rtxux.xyz/auth/login");
// 2. 创建HttpURLConnection对象
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
// 3. 设置请求参数等
// 请求方式
connection.setRequestMethod("POST");
// 超时时间
connection.setConnectTimeout(30000);
connection.setReadTimeout(30000);
// 设置是否输出
connection.setDoOutput(true);
// 设置是否读入
connection.setDoInput(true);
// 设置是否使用缓存
connection.setUseCaches(false);
// 设置此 HttpURLConnection 实例是否应该自动执行 HTTP 重定向
connection.setInstanceFollowRedirects(true);
// 设置使用标准编码格式编码
connection.setRequestProperty("Content-Type", "application/json;charset=UTF-8");//json格式
// 连接
connection.connect();
// 4. 处理输入输出
// 写入参数到请求中
//Content-Type:application/json;charset=UTF-8 对应json格式参数数据
String params = "{" +
"\"username\":\"" + login_name.getText() + "\"" + "," +
"\"password\":\"" + login_password.getText() + "\"" +
"}";
OutputStream out = connection.getOutputStream();
out.write(params.getBytes());
out.flush();
out.close();
// 从连接中读取响应信息
String msg = "";
int code = connection.getResponseCode();
if (code == 200) {
BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
msg += line + "\n";
}
Log.d("BaoFuPay", "Result:" + msg);
reader.close();
}
// 5. 断开连接
connection.disconnect();
}
}).start();
//GET
public void HistoryByGet() {
new Thread(new Runnable() {
@Override
public void run() {
try {
//get请求的url
URL url=new URL( "https://api.shisanshui.rtxux.xyz/history"+ "?player_id=" + user_id + "&limit=100&page=10"); //TODO:
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
//设置请求方式,请求超时信息
conn.setRequestMethod("GET");
conn.setReadTimeout(30000);
conn.setConnectTimeout(30000);
conn.setRequestProperty("X-Auth-Token", token);
//开启连接
conn.connect();
InputStream inputStream=null;
BufferedReader reader=null;
//如果应答码为200的时候,表示成功的请求带了,这里的HttpURLConnection.HTTP_OK就是200
if(conn.getResponseCode()==HttpURLConnection.HTTP_OK){
//获得连接的输入流
inputStream=conn.getInputStream();
//转换成一个加强型的buffered流
reader=new BufferedReader(new InputStreamReader(inputStream));
//把读到的内容赋值给result
String result = reader.readLine();
// msg = result;
Message msg = new Message();
Bundle data = new Bundle();
//将获取到的String装载到msg中
data.putString("value", result);
msg.setData(data);
msg.what = 1;
//发消息到主线程
handler.sendMessage(msg);
}
//关闭流和连接
reader.close();
inputStream.close();
conn.disconnect();
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (msg.what == 1) {
Bundle data = msg.getData();
String val = data.getString("value");
//设置UI
Gson gson = new Gson();
// 将 json 转化 成 List泛型
java.lang.reflect.Type type = new TypeToken<History>() {}.getType();
final History history = gson.fromJson(val, type);
// hisList = gson.fromJson(val, new TypeToken<List<History>>() {}.getType());
HistoryAdapter adapter = new HistoryAdapter(HistoryActivity.this,R.layout.history_item,history.data);
ListView listView = findViewById(R.id.history_list_view);
listView.setAdapter(adapter);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
History.His his = history.data.get(position);
Intent intent = new Intent(HistoryActivity.this,DetailActivity.class);
intent.putExtra("id",his.id);
startActivity(intent);
}
});
Log.i("msg", "请求结果:" + val);
} else if (msg.what ==0) {
Toast.makeText(getApplicationContext(),"请求资源不成功",Toast.LENGTH_LONG).show();
}
}
};
算法部分:
for(int i=0;i<handCard.size();i++)
{
if((i+1)<handCard.size()&&handCard.get(i).rank==handCard.get(i+1).rank)
if((i+2)<handCard.size()&&handCard.get(i).rank==handCard.get(i+2).rank)
if((i+3)<handCard.size()&&handCard.get(i).rank==handCard.get(i+3).rank) //四张相同的牌
{
arr.ranknum4.addAll(handCard.subList(i,i+4));
i+=3;
}
else //三张相同的牌
{
arr.ranknum3.addAll(handCard.subList(i,i+3));
i+=2;
}
else //两张相同的牌
{
arr.ranknum2.addAll(handCard.subList(i,i+2));
i+=1;
}
else //没有相同的牌
{
arr.ranknum1.add(handCard.get(i));
}
}
for(int i=0;i<handCard.size();i++)
{
switch(handCard.get(i).type)
{
case 1:arr.typenum1.add(handCard.get(i));break;
case 2:arr.typenum2.add(handCard.get(i));break;
case 3:arr.typenum3.add(handCard.get(i));break;
case 4:arr.typenum4.add(handCard.get(i));break;
}
}
}
上面所示代码十分简单,但通过统计到手十三张牌的花色、大小相同的张数的情况(我不知道这样有没有描述清楚,大概就是,ranknum1存储的是仅一张这样大小的牌的数量),大大简化了后面算法过程中判断特殊牌型和一部分普通牌型的算法,减少了冗余代码,提高程序效率。如判断特殊牌型三分天下代码如下图所示:
if(arr.ranknum4.size()==12) //三分天下
{
choice=tochoice(handCard);
choice.headType="sanfentianxia";
return;
}
6、性能分析与改进(6分)
描述你改进的思路(5分)
前端部分:
本来直接在主线程里接了接口,运行后发现会出现些问题,于是将接口放在子线程里。但是又发现了一个问题,子线程中没法直接设置界面资源,经过参考了一些博客的解决方法,最后借助Handler处理异步消息,实现子线程中获得的参数对界面的设置.
算法部分:
普通牌型的选择思路还存在许多冗余的代码可以进行简化
展示性能分析图和程序中消耗最大的函数(1分)
7、单元测试(5分)
展示出项目部分单元测试代码,并说明测试的函数,构造测试数据的思路
8、遇到的代码模块异常或结对困难及解决方法(8分)
问题描述(2分)
-
扑朔迷离的接口和间歇性抽风的Android Studio(手动再见),当事人表示非常后悔,改bug的时间都够学web了吧இAஇ
-
api调用:没有接触过api调用,一脸懵逼,百度之后,大概就是实例代码看不懂,理论知识学不会(;´༎ຶД༎ຶ`)
-
算法实现:一开始的疑问,暴力模拟(大概就是最死板的那样打的意思)会超时吗。后来,你告诉我怎么直接打,被各种情况逼疯இAஇ
做过哪些尝试(2分)
- 面向百度好像没有什么结果,后来开会闲聊才想起来可以去GitHub找开源码。搜索十三水真的有项目,后来抱着求知的心
(看不懂前面找的算法),还搜索了shisanshui、ThirteenWater,事实证明善用搜索引擎很重要。而且有的项目里有shisanshui的字样的代码,命名和描述与十三水无关的,就可以找到更多,跑题了(。 - 虽然算法还是一知半解,但是学习开源码的过程中收获很多,学习了用Maven组织项目,入门了Spring框架,尝试用springboot解决问题……(一点点感想:我发现比起跟着书本一步步的打代码,这样通过实践项目的学习更会让人有上瘾的感觉,就像解密一样,一步步的学习。而且这样的学习与实践需要关联度更大。算是学到一种新的学习方法吧。去找一些优秀的开源项目,然后把它从头学到尾,可以收获很多。
是否解决(2分)
- 算法大概就是实现到可以正儿八经出牌,但是不保证胜利的程度,但是api调用还是有问题,需要进一步解决。
有何收获(2分)
- 自我学习和探索的能力,接触到了新的学习方法。
- 从Android到Eclipse再到IDEA,一个合适的、用着顺手的(、有问题都能百度得到详细教程的)IDE也是很重要的
- 入门了Spring框架,进一步学习了Java
9、评价你的队友(4分)
雅芳说:
值得学习的地方:“今天也是很喜欢陈钰蕙的一天啊”,陈钰蕙真的很讨喜啊,一点点进步就不会毫不吝啬她的夸奖,偶尔暴躁的话也可以很快被她安抚好。我就喜欢这种一言不合就夸我的脾气贼好的遇到问题会一起讨论的小姐姐q(≧▽≦q)
需要改进的地方:如果我是拖延症,那陈钰蕙就是拖延癌了吧,还伴随着没有课就不出门的死宅属性。
钰蕙说:
值得学习的地方:宝藏女孩郑雅芳!一起做事情就是发现她越多隐藏技能的过程,美工超棒,获取、学习信息也贼快,害,抱雅芳学姐大腿太舒服了!
需要改进的地方:就是我们俩的拖延症吧哈哈哈
10、学习进度条(2分)
第N周 | 新增代码(行) | 累计代码(行) | 本周学习耗时(小时) | 累计学习耗时(小时) | 重要成长 |
---|---|---|---|---|---|
1 | 0 | 0 | 7.6 | 7.6 | |
2 | 200 | 200 | 22 | 29.6 | 讨论如何实现算法,对JAVA进一步学习。决定用安卓进行开发 |
3 | 1920 | 1920 | 21 | 50.6 | 学习和研究GitHub开源码,开始用idea实现后端算法 |
4 | 2293 | 2293 | 28 | 78.6 | 完善算法,实现交互,接口调试 |
(我真的有看清楚第二行是累计代码,没有累计的原因是每次都重写了QAQ)