文章目录
- Part4: Interacting Objects解答
- 一、The Critter Class
- 1. What methods are implemented in Critter?
- 2. What are the five basic actions common to all critters when they act?
- 3. Should subclasses of Critter override the getActors method? Explain.
- 4. Describe the way that a critter could process actors.
- 5. What three methods must be invoked to make a critter move? Explain each of these methods.
- 6. Why is there no Critter constructor?
- 二、Extending the Critter Class
- 1. Why does act cause a ChameleonCritter to act differently from a Critter even though ChameleonCritter does not override act?
- 2. Why does the makeMove method of ChameleonCritter call super.makeMove?
- 3. How would you make the ChameleonCritter drop flowers in its old location when it moves?
- 4. Why doesn’t ChameleonCritter override the getActors method?
- 5. Which class contains the getLocation method?
- 6. How can a Critter access its own grid?
- 三、Another Critter
- 1. Why doesn’t CrabCritter override the processActors method?
- 2. Describe the process a CrabCritter uses to find and eat other actors. Does it always eat all neighboring actors? Explain.
- 3. Why is the getLocationsInDirections method used in CrabCritter?
- 4. If a CrabCritter has location (3, 4) and faces south, what are the possible locations for actors that are returned by a call to the getActors method?
- 5. What are the similarities and differences between the movements of a CrabCritter and a Critter?
- 6. How does a CrabCritter determine when it turns instead of moving?
- 7. Why don’t the CrabCritter objects eat each other?
Part4: Interacting Objects解答
一、The Critter Class
1. What methods are implemented in Critter?
- 答案: Critter中共实现了6个方法:
- act()
- getActors()
- processActors()
- getMoveLocations()
- selectMoveLocation()
- makeMove()
- 原因及源码支撑:
/* @file: framework/info.gridworld.actor/Critter.java */ /* @line: 38、56、71、88、104、125 */ public void act() public ArrayList<Actor> getActors() public void processActors(ArrayList<Actor> actors) public ArrayList<Location> getMoveLocations() public Location selectMoveLocation(ArrayList<Location> locs) public void makeMove(Location loc)
2. What are the five basic actions common to all critters when they act?
- 答案: 从act方法实现可以知道5个公共方法为:
- getActors()
- processActors()
- getMoveLocations()
- selectMoveLocation()
- makeMove()
- 原因及源码支撑:
/* @file: framework/info.gridworld.actor/Critter.java */ /* @line: 38~47 */ public void act() { if (getGrid() == null) return; ArrayList<Actor> actors = getActors(); processActors(actors); ArrayList<Location> moveLocs = getMoveLocations(); Location loc = selectMoveLocation(moveLocs); makeMove(loc); }
3. Should subclasses of Critter override the getActors method? Explain.
- 答案: 应该
- 原因: 因为不同的Critter选择actors的方式可能有不同,因此我们需要Override该方法以针对性地选择actors
4. Describe the way that a critter could process actors.
- 答案: 以源代码为例,我们可以选择吃掉(即移除)所有不是critter也不是rock的actors
- 原因及源码支撑:
/* @file: framework/info.gridworld.actor/Critter.java */ /* @line: 71~78 */ public void processActors(ArrayList<Actor> actors) { for (Actor a : actors) { if (!(a instanceof Rock) && !(a instanceof Critter)) a.removeSelfFromGrid(); } }
5. What three methods must be invoked to make a critter move? Explain each of these methods.
- 答案: critter移动应调用的3个必备方法为:
- getMoveLocations()
- selectMoveLocation()
- makeMove()
- 解释及源码支撑:
为实现critter的移动,act应按下面3步调用方法
首先act方法会调用getMoveLocations方法获得所有可选位置集: 在标准critter中,它选择周围8格里面所有的空白方格;
/* @file: framework/info.gridworld.actor/Critter.java */ /* @line: 88~91 */ public ArrayList<Location> getMoveLocations() { return getGrid().getEmptyAdjacentLocations(getLocation()); }
然后调用selectMoveLocation方法以某种方法选择其中一个位置: 在标准critter中,会从上一步得到的Location集合中随机选择一个位置并返回。
/* @file: framework/info.gridworld.actor/Critter.java */ /* @line: 104~111 */ public Location selectMoveLocation(ArrayList<Location> locs) { int n = locs.size(); if (n == 0) return getLocation(); int r = (int) (Math.random() * n); return locs.get(r); }
最后调用makeMove方法将critter移动到上一步选定的位置
/* @file: framework/info.gridworld.actor/Critter.java */ /* @line: 125~131 */ public void makeMove(Location loc) { if (loc == null) removeSelfFromGrid(); else moveTo(loc); }
6. Why is there no Critter constructor?
- 答案: 因为Critter是继承自Actor类的,这里Critter类有默认构造器,其中会调用super()方法调用Actor类的构造器,从而创建对象。
二、Extending the Critter Class
1. Why does act cause a ChameleonCritter to act differently from a Critter even though ChameleonCritter does not override act?
- 答案: 因为从上面已知act类中会用到5个基本方法,而在ChameleonCritter类中override了其中的两个方法processActors以及makeMove方法,因此最终导致ChameleonCritter类中的行为与其父类Critter不同。
- 源码支撑:
/* @file: projects/critters/ChameleonCritter.java */ /* @line: 36 */ public void processActors(ArrayList<Actor> actors) /* @line: 50 */ public void makeMove(Location loc)
2. Why does the makeMove method of ChameleonCritter call super.makeMove?
- 答案: 在ChameleonCritter类中的makeMove方法实际上是先将自身方向转至新方向,然后再调用父类的makeMove方法,这样直接复用了父类的代码,减少冗余。
- 源码支撑:
/* @file: projects/critters/ChameleonCritter.java */ /* @line: 50~54 */ public void makeMove(Location loc) { setDirection(getLocation().getDirectionToward(loc)); super.makeMove(loc); }
3. How would you make the ChameleonCritter drop flowers in its old location when it moves?
- 答案: 我们可以重写makeMove方法: 用一个变量记录移动前ChameleonCritter的位置,移动后与原位置比较: 如果一样的话我们就不留花;否则我们就在原位置留下一朵花。
- 代码支撑: 将下面的代码覆盖掉原来的makeMove方法即可实现要求
/* @file: projects/critters/ChameleonCritter.java */ /* @origin_line: 50~54 */ public void makeMove(Location loc) { Location oldLocation = getLocation(); setDirection(getLocation().getDirectionToward(loc)); super.makeMove(loc); if (oldLocation != loc) { Flower flower = new Flower(); flower.putSelfInGrid(getGrid(), oldLocation); } }
4. Why doesn’t ChameleonCritter override the getActors method?
- 答案: 因为ChameleonCritter类对要处理的actors的需求跟父类Critter是一样的,因此不需要override父类的getActors方法,直接继承即可。
5. Which class contains the getLocation method?
- 答案: Actor类,所有Actor的子类通过继承来使用此方法。
- 源码支撑:
/* @file: framework/info.gridworld.actor/Actor.java */ /* @line: 102~105 */ public Location getLocation() { return location; }
6. How can a Critter access its own grid?
- 答案: 通过调用getGrid方法,因为在Actor类中已经实现了此方法,而Critter类是Actor类的子类,直接继承了父类的getGrid方法。
- 源码支撑:
/* @file: framework/info.gridworld.actor/Actor.java */ /* @line: 92~95 */ public Grid<Actor> getGrid() { return grid; }
三、Another Critter
1. Why doesn’t CrabCritter override the processActors method?
- 答案: 因为CrabCritter类对于得到的Actor的处理方法是一样的,都是直接移除,因此我们可以直接继承父类Critter的processActors方法而无需override
- 原因及源码支撑:
/* @file: framework/info.gridworld.actor/Critter.java */ /* @line: 71~78 */ public void processActors(ArrayList<Actor> actors) { for (Actor a : actors) { if (!(a instanceof Rock) && !(a instanceof Critter)) a.removeSelfFromGrid(); } }
2. Describe the process a CrabCritter uses to find and eat other actors. Does it always eat all neighboring actors? Explain.
- 答案及原因: CrabCritter的getActors方法只会在正前方、左前方和右前方这3个方向的相邻格中寻找Actor,而找到之后它会调用processActors方法将其中非石头以及非critter的所有Actor都"吃掉"。因此实际上它只"吃"前方3个方向中的部分Actor,并未"吃掉"所有相邻的Actor。
- 源码支撑:
/* @file: projects/critters/CrabCritter.java */ /* @line: 47~48 */ // 说明只获取前方3个方向 int[] dirs = { Location.AHEAD, Location.HALF_LEFT, Location.HALF_RIGHT }; /* @line: 49~54 */ // 说明只获取非空Actor for (Location loc : getLocationsInDirections(dirs)) { Actor a = getGrid().get(loc); if (a != null) actors.add(a); } /* @file: framework/info.gridworld.actor/Critter.java */ /* @line: 75~76 */ // 说明不"吃"rock和其他critter if (!(a instanceof Rock) && !(a instanceof Critter)) a.removeSelfFromGrid();
3. Why is the getLocationsInDirections method used in CrabCritter?
- 答案:
因为CrabCritter的定义中,吃的时候(getActors)需要得到前3个方向的Location;而移动的时候(getMoveLocations)则需要得到左右两个方向的Location,在这之前并没有哪个方法能实现到按方向范围集合搜索这一点。
因此我们定义该方法,传入需要的Direction值集合作为参数,从而得到对应的Location。这样可以复用代码,减少冗余。- 原因及源码支撑:
/* @file: projects/critters/CrabCritter.java */ /* @line: 44~49 */ public ArrayList<Actor> getActors() { ArrayList<Actor> actors = new ArrayList<Actor>(); int[] dirs = { Location.AHEAD, Location.HALF_LEFT, Location.HALF_RIGHT }; for (Location loc : getLocationsInDirections(dirs)) /* @line: 62~67 */ public ArrayList<Location> getMoveLocations() { ArrayList<Location> locs = new ArrayList<Location>(); int[] dirs = { Location.LEFT, Location.RIGHT }; for (Location loc : getLocationsInDirections(dirs))
4. If a CrabCritter has location (3, 4) and faces south, what are the possible locations for actors that are returned by a call to the getActors method?
- 答案: (4, 3)、(4, 4)和(4, 5)
- 原因: 从CrabCritter的getActors方法实现中可以知道CrabCritter会往其前方3个方向中寻找,而此时CrabCritter正面朝南方,因此它的前方3个方向即为答案中所写的。
- 源码支撑:
/* @file: projects/critters/CrabCritter.java */ /* @line: 44~49 */ public ArrayList<Actor> getActors() { ArrayList<Actor> actors = new ArrayList<Actor>(); int[] dirs = { Location.AHEAD, Location.HALF_LEFT, Location.HALF_RIGHT }; for (Location loc : getLocationsInDirections(dirs))
5. What are the similarities and differences between the movements of a CrabCritter and a Critter?
- 答案及源码支撑:
相似处:
(1) 它们在移动的时候都不会选择转向;(移动的时候
loc == null
为false,都会调用moveTo方法移动)/* @file: framework/info.gridworld.actor/Critter.java */ /* @line: 125~131 */ public void makeMove(Location loc) { if (loc == null) removeSelfFromGrid(); else moveTo(loc); }
(2) 它们都是在getMoveLocations方法得到的集合中随机选择下一个移动的。(代码中loc即为上一步得到的可移动位置集合)
/* @file: framework/info.gridworld.actor/Critter.java */ // selectMoveLocation方法 /* @line: 109~110 */ int r = (int) (Math.random() * n); return locs.get(r);
不同点:
(1) CrabCritter只会往左右两个方向移动;而Critter则可以往8个方向移动。
/* @file: projects/critters/CrabCritter.java */ /* @line: 62~67 */ public ArrayList<Location> getMoveLocations() { ArrayList<Location> locs = new ArrayList<Location>(); int[] dirs = { Location.LEFT, Location.RIGHT }; for (Location loc : getLocationsInDirections(dirs)) /* @file: framework/info.gridworld.actor/Critter.java */ /* @line: 88~91 */ public ArrayList<Location> getMoveLocations() { return getGrid().getEmptyAdjacentLocations(getLocation()); }
(2) CrabCritter不能移动时会随机往两个方向转向,而Critter会选择停在原地不转向
/* @file: projects/critters/CrabCritter.java */ // makeMove方法 /* @line: 79~88 */ if (loc.equals(getLocation())) { double r = Math.random(); int angle; if (r < 0.5) angle = Location.LEFT; else angle = Location.RIGHT; setDirection(getDirection() + angle); }
6. How does a CrabCritter determine when it turns instead of moving?
- 答案: 通过检查makeMove中的loc参数跟当前位置是否一致,一致的话就选择turn
- 原因及源码支撑:
/* @file: projects/critters/CrabCritter.java */ // makeMove方法 /* @line: 79 */ if (loc.equals(getLocation()))
7. Why don’t the CrabCritter objects eat each other?
- 答案: 因为CrabCritter继承自Critter,而子类中并未override掉processActors方法,因此CrabCritter类使用的仍是父类Critter的。而在父类的定义中,该方法会"吃掉"所有非rock及非Critter的Actor,而CrabCritter继承自Critter,自然不会被"吃掉"。
- 原因及源码支撑:
/* @file: framework/info.gridworld.actor/Critter.java */ /* @line: 71~78 */ public void processActors(ArrayList<Actor> actors) { for (Actor a : actors) { if (!(a instanceof Rock) && !(a instanceof Critter)) a.removeSelfFromGrid(); } } /* @file: projects/critters/CrabCritter.java */ /* @line: 32 */ public class CrabCritter extends Critter