BUAA_OOP_2024_碎碎念总结

架构设计

学习心得体会

类与对象

翻出去年的代码,看到下面的杰作,不禁感叹诗人握持啊!

1
2
3
4
5
6
//2023_oopre
adventure.addBottle(adventure.getBottles(), botId, botName, capacity);
public void addBottle(HashMap<Integer, Bottle> bottles, int botId,
String botName, int capacity) {
//do something
}

当时并未理解面向对象中“类”这一概念,按照C语言的想法把所有需要的信息作为函数参数传入类的方法中,忽视属性在类内部的可见性o(~ヘ~o#)

对象管理与容器使用

经过OO正课U3的TLE折磨,回看当初的代码略显稚嫩,没有做到容器的高效管理o(~ヘ~o#)

一个典型的用Botname作为键映射的HashMap,也让我用HashSet这么水灵灵的遍历上了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//2023_oopre
private HashSet<Bottle> bagBottles;

public void carryBot(HashMap<Integer, Bottle> bottles, HashSet<Bottle> bagBottles, String id) {
.....
Bottle bot1 = bottles.get(botId);
if (!bagBottles.contains(bot1)) {
//我嘞个遍历TLEEEEEE
for (Bottle obj : bagBottles) {
if (obj.getName().equals(bot1.getName())) {
num++;
}
}
if (num < max) {
bagBottles.add(bot1);
}
}
}

今年直接火速爆改,HashMapHashMap轻松拿下(。•̀ᴗ-)✧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//2024_oop
private HashMap<String, HashMap<Integer, Bottle>> bagBottles;

public void carryBot(int botId) {
int max = getCE() / 5 + 1;
Bottle bot1 = bottles.get(botId);
if (bagBottles.containsKey(bot1.getName())) {
HashMap<Integer, Bottle> botMap = bagBottles.get(bot1.getName());
if (botMap.size() < max) {
botMap.put(botId, bot1);
}
} else {
HashMap<Integer, Bottle> botMap = new HashMap<>();
botMap.put(botId, bot1);
bagBottles.put(bot1.getName(), botMap);
}
}

今年oop没有针对ArrayListHashSet卡TLE,正课不会这么心慈手软了,xd接招(。•̀ᴗ-)✧

继承和接口

作为两种不同的代码复用机制,父类是通过提炼多个类的重复部分形成的,而接口服务于多个类之间共性的行为。好好读指导书的我当然在去年就明确了概念,做的超棒(。•̀ᴗ-)✧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
//2023_oop 继承
class Bottle implements Commodity { //父类
public Bottle(int id, String name, int capacity, long price) {
this.id = id;
this.name = name;
this.capacity = capacity;
this.isFilled = true;
this.price = price;
}
public void usedBy(Adventure adventurer) {
adventurer.setHitPoint(adventurer.getHitPoint() + capacity);
}
}
public class RecoverBottle extends Bottle { //使用关键字 extends 来建立继承关系
private double ratio;

public RecoverBottle(int id, String name, int capacity, long price, double ratio) {
super(id, name, capacity, price); //调用父类的构造方法
this.ratio = ratio;
}

@Override
public void usedBy(Adventure adventurer) { //子类根据需要Override
adventurer.setHitPoint(adventurer.getHitPoint()
+ (int) Math.floor(adventurer.getHitPoint() * ratio));
}

}
//2024_oop 继承
//和上面的类似omit

//2023_oop 接口
interface Commodity { //使用关键字 interface 定义一个接口。
public void printInfo();

public int getId();

public long getPrice();
}
public class Bottle implements Commodity { //使用关键字 implements 来实现一个接口
@Override x3 // 该接口的所有方法都要实现
......
}
//2024_oop 接口
interface Guard {
boolean fight(Adventure adv);
String getType();
}
public class Shd implements Guard {
@Override x2
......
}

大三最近在上的编译课中,也体现了继承思想的使用,不禁感叹学会OO受益终生,背景是这样的:

从前小汀种了一棵树,叫做抽象语法树AST,这颗树的不同节点Node,有着不同的类型NodeType。现在我们考虑根节点的类型为CompUnit,它有一些孩子节点,类型分别为 Decl、FuncDef等。

小汀利用继承的思想顺利完成了如下的作业!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
//父类
public class Node {
protected ArrayList<Node> children;
protected NodeType nodeType;

public Node(NodeType type, ArrayList<Node> children) {
this.children = children;
this.nodeType = type;
}
}
//节点类型枚举类
public enum NodeType {
CompUnit,
Decl,
FuncDef
}
//子类
public class CompUnit extends Node {
public CompUnit(NodeType type, ArrayList<Node> children) {
super(type, children);
}
}
public class Decl extends Node {
public Decl(NodeType type, ArrayList<Node> children) {
super(type, children);
}
}
public class FuncDef extends Node {
public FuncDef(NodeType type, ArrayList<Node> children) {
super(type, children);
}
}

设计模式

观察者模式与去年考察类似,不同的是,今年oop重磅推出了工厂设计模式的练习,体现出课程组向真正生产场景逐步靠拢的决心和魄力(。•̀ᴗ-)✧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// 宝物工厂  
class TreasureFactory {
public static Treasure createTreasure(Guard guard) {
switch (guard.getType()) {
case "Shd":
return new ShdTreasure();
case "Flm":
return new FlmTreasure();
case "Stn":
return new StnTreasure();
case "Wnd":
return new WndTreasure();
case "Frz":
return new FrzTreasure();

default:
throw new IllegalArgumentException("Unknown guard type");
}
}

public static void factory(Adventure adv) {
Guard shd = new Shd();
if (shd.fight(adv)) {
Treasure treasure = TreasureFactory.createTreasure(shd);
treasure.showInfo(); // 显示获得的宝物信息
treasure.useBy(adv);
} else {
return;
}
....
}
}

这又让我想起了编译课中,也体现了工厂模式的使用,不禁再次感叹学会OO受益终生,书接上文:

小汀想只传入节点类型NodeType和节点名称name就能得到不同子类的具体对象。

小汀利用工厂模式的设计思想顺利完成了如下的作业!

1
2
3
4
5
6
7
8
9
10
11
12
13
//芝士JDK17的语法 请勿在oo课程使用(补丁)
public class NodeFactory {
public static Node newNode(NodeType type, ArrayList<Node> children) {
switch (type) {
case CompUnit -> {
return new CompUnit(type, children);
}
case Decl -> {
return new Decl(type, children);
}
....
}
}

输入解析

去年没有实现层次化架构,输入解析和业务处理一起耦合在我的Main里┐=͟͟͞͞( ̄ー ̄)┌

今年架构先行,起手就是一个Manager类,来进行不同指令对应业务的分发

1
2
3
4
5
6
7
8
9
public void handle(String[] strings) {
if (strings[0].equals("1")) {
addAdv(strings);
} else if (strings[0].equals("2")) {
addBot(strings);
} else if (strings[0].equals("3")) {
addEqu(strings);
}//......
}

拜读助教czx的标程之后,顿觉其设计之优雅简洁,雅!!!!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//czx_code
public class Solver {
private final HashMap<Integer, SolveFunction> solvers = new HashMap<>();
private final HashMap<Integer, Adventurer> adventurers = new HashMap<>();

public Solver() {
solvers.put(1, this::solve1);
solvers.put(2, this::solve2);
//.....
}

public void solve(ArrayList<String> input) {
int id = Integer.parseInt(input.get(0));
SolveFunction func = solvers.get(id);
if (func != null) {
func.solve(input);
} else {
System.err.println("Error! No solver for " + id);
}
}
}

Debug的心得体会

今年即使偷看标程偷看数据点,验题的时候还是特别狼狈啊!!!

我的bug包括但不限于

  • 删除Equipment时没有没有判断HashMap背包中的装备是否id相同(因为HashMap使用装备名作为索引)
  • 援助被雇佣者的次数要重新从零计算
  • 所有冒险者失去的体力值的总和输出了负值
  • 携带和拥有总是搞混
  • ……..

我debug的方法包括但不限于

  • 偷看标程偷看数据点
  • 使用printf,追溯每一位冒险者的活动轨迹,我想给它完整的一生
  • 使用debug,re的时候巨好用
  • 幻想有人能开发测评机,这个到正课真的很重要(つ﹏⊂)

后话

两次挑战oopre,变的是我的知识储备、代码能力、debug水平,我发现

  • 没有人是生来会写代码,oop练习迭代的意义就是在实践中提升代码能力,现在我可以很自豪的说,窝辉鞋黛玛啊!
  • 没有人是生来懂得debug的,只要耐心阅读测试数据,反复推敲指导书,没有测评机的帮助我也找到了自己的问题!
  • 没有人是生来懂得架构的,在不断设计与不断重构中,锤炼自己的设计思想,方能自然写出适应自己业务需求的代码!

岁月更迭,纵使架构不断迭代,性能不断优化,我们从未改变的是

  • 不忘来时路,oop课程的初心是,大家一起努力提升优化,今后能从容应对实际生产研发中遇到的困难
  • 不忘来时路,oop课程的目标是,收获知识方法,吸取经验教训,更好与oo正课衔接

下一次正课再见,我们来日方长!!!( ´͈ ᵕ `͈ )◞♡