以下为个人学习笔记和习题整理
课程:面向对象程序设计 ——Java 语言
- 浙江大学 - 翁恺 @ 中国大学 MOOC
https://www.icourse163.org/course/ZJU-1001542001
# 课堂笔记
# 抽象 abstract
抽象函数:表达概念而无法实现具体代码的函数。
抽象类:表达概念而无法构造出实体的类。
用 abstract
关键字来定义抽象类。
抽象类的作用仅仅是表达接口,而不是具体的实现细节。
抽象类中可以存在抽象方法。
抽象方法也是使用 abstract
关键字来修饰。
抽象的方法是不完全的,它只是一个方法签名而完全没有方法体,不能带 {}
。
如果一个类有了一个抽象的方法,这个类就必须声明为抽象类。
有抽象函数的类,一定是抽象类。
一个抽象类可以没有任何抽象方法,所有的方法都有方法体,但是整个类是抽象的。
设计这样的抽象类主要是为了防止制造它的对象出来。
任何继承类抽象类的非抽象类的对象,可以赋给这个变量。
抽象类不能制造对象,但是可以定义变量。
如果父类是抽象类,那么子类必须覆盖所有在父类中的抽象方法,否则子类也成为一个抽象类。
# 两种抽象
- 与具体相对:表示一种概念而非实体
- 与细节相对:表示在一定程度上忽略细节而着眼大局
# 接口 interface
接口是纯抽象类
- 所有的成员函数都是抽象函数
- 所有的成员变量都是
public static final
接口规定了长什么样,但是不管里面有什么。
interface
是一种特殊的class
,使用时取代class
的位置。
# 实现接口 implements
类用 extends
,接口用 implements
类可以实现很多接口
接口可以继承接口,但不能继承类
接口不能实现接口
# 面向接口的编程方式
设计程序时先定义接口,再实现类。
任何需要在函数间传入传出的一定是接口,而不是具体的类。
是 Java 成功的关键之一,因为极适合多人同时写一个大程序。
也是 Java 被批评的要点之一,因为代码量膨胀起来很快。
# 课内代码
# 细胞自动机
死亡:如果活着的邻居的数量<2 或>3,则死亡
新生:如果正好有 3 个邻居活着,则新生
其他情况则保持原状
package cellmachine; | |
import javax.swing.JFrame; | |
import cell.Cell; | |
import field.Field; | |
import field.View; | |
public class CellMachine { | |
public static void main(String[] args) { | |
Field field = new Field(30,30); | |
for ( int row = 0; row<field.getHeight(); row++ ) { | |
for ( int col = 0; col<field.getWidth(); col++ ) { | |
field.place(row, col, new Cell()); | |
} | |
} | |
for ( int row = 0; row<field.getHeight(); row++ ) { | |
for ( int col = 0; col<field.getWidth(); col++ ) { | |
Cell cell = field.get(row, col); | |
if ( Math.random() < 0.2 ) { | |
cell.reborn(); | |
} | |
} | |
} | |
View view = new View(field); | |
JFrame frame = new JFrame();// 图形窗口 | |
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // 默认关闭的操作 | |
frame.setResizable(false); | |
frame.setTitle("Cells"); | |
frame.add(view); | |
frame.pack(); | |
frame.setVisible(true); | |
for ( int i=0; i<1000; i++ ) { | |
for ( int row = 0; row<field.getHeight(); row++ ) { | |
for ( int col = 0; col<field.getWidth(); col++ ) { | |
Cell cell = field.get(row, col); | |
Cell[] neighbour = field.getNeighbour(row, col); | |
int numOfLive = 0; | |
for ( Cell c : neighbour ) { | |
if ( c.isAlive() ) { | |
numOfLive++; | |
} | |
} | |
System.out.print("["+row+"]["+col+"]:"); | |
System.out.print(cell.isAlive()?"live":"dead"); | |
System.out.print(":"+numOfLive+"-->"); | |
if ( cell.isAlive() ) { | |
if ( numOfLive <2 || numOfLive >3 ) { | |
cell.die(); | |
System.out.print("die"); | |
} | |
} else if ( numOfLive == 3 ) { | |
cell.reborn(); | |
System.out.print("reborn"); | |
} | |
System.out.println(); | |
} | |
} | |
System.out.println("UPDATE"); | |
frame.repaint(); | |
try { | |
Thread.sleep(200); | |
} catch (InterruptedException e) { | |
e.printStackTrace(); | |
} | |
} | |
} | |
} |
package cell; | |
import java.awt.Graphics; | |
public class Cell { | |
private boolean alive = false; | |
public void die() { alive = false; } | |
public void reborn() { alive = true; } | |
public boolean isAlive() { return alive; } | |
public void draw(Graphics g, int x, int y, int size) { | |
g.drawRect(x, y, size, size); | |
if ( alive ) { | |
g.fillRect(x, y, size, size); | |
} | |
} | |
} |
package field; | |
import java.util.ArrayList; | |
import cell.Cell; | |
public class Field { | |
private int width; | |
private int height; | |
private Cell[][] field; | |
public Field(int width, int height) { | |
this.width = width; | |
this.height = height; | |
field = new Cell[height][width]; | |
} | |
public int getWidth() { return width; } | |
public int getHeight() { return height; } | |
public Cell place(int row, int col, Cell o) { | |
Cell ret = field[row][col]; | |
field[row][col] = o; | |
return ret; | |
} | |
public Cell get(int row, int col) { | |
return field[row][col]; | |
} | |
public Cell[] getNeighbour(int row, int col) { | |
ArrayList<Cell> list = new ArrayList<Cell>(); | |
for ( int i=-1; i<2; i++ ) { | |
for ( int j=-1; j<2; j++ ) { | |
int r = row+i; | |
int c = col+j; | |
if ( r >-1 && r<height && c>-1 && c<width && !(r== row && c == col) ) { | |
list.add(field[r][c]); | |
} | |
} | |
} | |
return list.toArray(new Cell[list.size()]); | |
} | |
public void clear() { | |
for ( int i=0; i<height; i++ ) { | |
for ( int j=0; j<width; j++ ) { | |
field[i][j] = null; | |
} | |
} | |
} | |
} |
package field; | |
import java.awt.Dimension; | |
import java.awt.Graphics; | |
import javax.swing.JFrame; | |
import javax.swing.JPanel; | |
import cell.Cell; | |
public class View extends JPanel { | |
private static final long serialVersionUID = -5258995676212660595L; | |
private static final int GRID_SIZE = 16; | |
private Field theField; | |
public View(Field field) { | |
theField = field; | |
} | |
@Override | |
public void paint(Graphics g) { | |
super.paint(g); | |
for ( int row = 0; row<theField.getHeight(); row++ ) { | |
for ( int col = 0; col<theField.getWidth(); col++ ) { | |
Cell cell = theField.get(row, col); | |
if ( cell != null ) { | |
cell.draw(g, col*GRID_SIZE, row*GRID_SIZE, GRID_SIZE); | |
} | |
} | |
} | |
} | |
@Override | |
public Dimension getPreferredSize() { | |
return new Dimension(theField.getWidth()*GRID_SIZE+1, theField.getHeight()*GRID_SIZE+1); | |
} | |
public static void main(String[] args) { | |
Field field = new Field(10,10); | |
for ( int row = 0; row<field.getHeight(); row++ ) { | |
for ( int col = 0; col<field.getWidth(); col++ ) { | |
field.place(row, col, new Cell()); | |
} | |
} | |
View view = new View(field); | |
JFrame frame = new JFrame(); | |
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); | |
frame.setResizable(false); | |
frame.setTitle("Cells"); | |
frame.add(view); | |
frame.pack(); | |
frame.setVisible(true); | |
} | |
} |
# 设计理念
- 数据与表现分离
程序的业务逻辑与表现无关
表现可以是图形的也可以是文本的
表现可以是当地的也可以是远程的
表现与数据的关系
View
只管根据Field
画出图形,Field
只管数据的存放。
一旦数据更新以后,通知View
重新画出整个画面,不去精心设计哪个局部需要更新,这样简化了程序逻辑,是在计算机运算速度提高的基础上实现的。
责任驱动的设计
将程序要实现的功能分配到合适的类 / 对象中去是设计中非常中要的一环网格化
图形界面本身有更高的解析度
但是将画面网格化以后,数据就更容易处理了
# 狐狸与兔子
狐狸、兔子都有年龄;
到达一定年龄上限会自然死亡;
狐狸随机吃掉周围一只兔子;
狐狸、兔子可以随机生一只小的放在旁边格子;
如果不吃不生,狐狸、兔子可以随机向旁边格子移一步会随机吃掉。
# FoxAndRabbit
程序执行入口
主要思想:
两层 for
循环,挨个 cell
执行移动、进食、生育;
每次循环结束后 repaint view
。
package FoxAndRabbit; | |
import field.Field; | |
import field.View; | |
import field.Location; | |
import java.util.ArrayList; | |
import javax.swing.JFrame; | |
import animal.Fox; | |
import animal.Rabbit; | |
import animal.Animal; | |
import cell.Cell; | |
public class FoxAndRabbit{ | |
private Field thefield; | |
private View theview; | |
public FoxAndRabbit( int size ){ | |
thefield = new Field(size, size); | |
for( int row = 0; row <thefield.getHeight(); row++ ){ | |
for( int col = 0; col < thefield.getWidth(); col++ ){ | |
double probability = Math.random(); | |
if( probability <0.05 ){ | |
thefield.place( row, col, new Fox()); | |
}else if( probability < 0.15 ){ | |
thefield.place( row, col, new Rabbit()); | |
} | |
} | |
} | |
theview = new View(thefield); | |
JFrame frame = new JFrame(); | |
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); | |
frame.setResizable(false); | |
frame.setTitle("FoxAndRabbit"); | |
frame.add(theview); | |
frame.pack(); | |
frame.setVisible(true); | |
} | |
public void step(){ | |
for( int row = 0; row < thefield.getHeight(); row++ ){ | |
for( int col = 0; col < thefield.getWidth(); col++ ){ | |
Cell cell = thefield.get(row, col); | |
if( cell != null ){ | |
Animal animal = (Animal)cell; | |
animal.grow(); | |
if( animal.isAlive()){ | |
//move | |
Location loc = animal.move(thefield.getFreeNeighbour(row, col)); | |
if( loc != null ){ | |
thefield.move(row, col, loc); | |
} | |
//eat animal.eat(thefield); | |
if( animal instanceof Fox){ | |
Cell[] neighbour = thefield.getNeighbour(row, col); | |
ArrayList<Animal> listRabbit = new ArrayList<Animal>(); | |
for( Cell an : neighbour ){ | |
if( an instanceof Rabbit ){ | |
listRabbit.add( (Rabbit)an ); | |
} | |
} | |
if( !listRabbit.isEmpty() ){ | |
Animal fed = animal.feed(listRabbit); | |
if( fed != null ){ | |
thefield.remove((Cell)fed); | |
} | |
} | |
} | |
//breed | |
Animal baby = animal.breed(); | |
if( baby != null ){ | |
thefield.placeRandomAdj(row, col, (Cell)baby); | |
} | |
}else{ | |
thefield.remove(row, col); | |
} | |
} | |
} | |
} | |
} | |
public void start( int steps ){ | |
for( int i = 0; i < steps; i++){ | |
step(); | |
theview.repaint(); | |
try{ | |
Thread.sleep(200); | |
}catch (InterruptedException e){ | |
e.printStackTrace(); | |
} | |
} | |
} | |
public static void main(String[] args) { | |
FoxAndRabbit fnr = new FoxAndRabbit(30); | |
fnr.start(10); | |
} | |
} |
# cell
包
# Cell
接口类
package cell; | |
import java.awt.Graphics; | |
public interface Cell { | |
void draw(Graphics g, int x, int y, int size); | |
} |
# field
包
# Location
类
用于定位,返回所要 cell
位置。
package field; | |
public class Location{ | |
private int row; | |
private int col; | |
public Location( int row, int col ){ | |
this.col = col; | |
this.row = row; | |
} | |
public int getRow(){ | |
return row; | |
} | |
public int getCol() { | |
return col; | |
} | |
} |
# Field
类
package field; | |
import java.util.ArrayList; | |
import cell.Cell; | |
public class Field { | |
// 这段代码没用到过 | |
// private static final Location[] adjacent = { | |
// new Location(-1,-1),new Location(-1,0),new Location(-1,1), | |
// new Location(0,-1) ,new Location(0,0) ,new Location(0,-1), | |
// new Location(1,-1) ,new Location(1,-1),new Location(1,-1), | |
// }; | |
private int width; // 长 | |
private int height; // 宽 | |
private Cell[][] field; //cell 二维数组 | |
/** | |
* 构造方法:生成指定大小 cell 二维数组 | |
* @param width 长 | |
* @param height 宽 | |
*/ | |
public Field(int width, int height){ | |
this.height = height; | |
this.width = width; | |
field = new Cell[height][width]; | |
} | |
/** | |
* 简单方法:获取长、宽、指定 cell | |
* @return [description] | |
*/ | |
public int getWidth(){ return width; } | |
public int getHeight(){ return height; } | |
public Cell get(int row, int col){ | |
return field[row][col]; | |
} | |
/** | |
* 获取周围 cell | |
* @param row 当前 cell 的坐标:行 | |
* @param col 当前 cell 的坐标:列 | |
* @return 周围 cell 数组 | |
*/ | |
public Cell[] getNeighbour(int row, int col) { | |
ArrayList<Cell> list = new ArrayList<Cell>(); | |
for( int i = -1; i<2; i++ ) { | |
for( int j = -1; j<2; j++ ) { | |
int r = row+i; | |
int c = col+j; | |
if( r>-1 && r<height && c>-1 && c<width && !(r == row && c == col)) { | |
list.add(field[r][c]); | |
} | |
} | |
} | |
return list.toArray(new Cell[list.size()]); | |
} | |
/** | |
* 列出周围的空 cell | |
* @param row 当前 cell 的坐标:行 | |
* @param col 当前 cell 的坐标:列 | |
* @return 周围的空 cell 数组 | |
*/ | |
public Location[] getFreeNeighbour(int row, int col) { | |
ArrayList<Location> list = new ArrayList<Location>(); | |
for( int i = -1; i<2; i++ ) { | |
for( int j = -1; j<2; j++ ) { | |
int r = row+i; | |
int c = col+j; | |
if( r>-1 && r<height && c>-1 && c<width && field[r][c] == null ) { | |
list.add(new Location(r, c)); | |
} | |
} | |
} | |
return list.toArray(new Location[list.size()]); | |
} | |
/** | |
* 在周围空 cell 里放置一个 cell | |
* @param row 当前 cell 的坐标:行 | |
* @param col 当前 cell 的坐标:列 | |
* @param cell 待放入的 cell | |
* @return 是否成功放入 cell 进 field | |
*/ | |
public boolean placeRandomAdj( int row, int col, Cell cell ){ | |
boolean ret = false; | |
Location[] FreeAdj = getFreeNeighbour(row, col); | |
if( FreeAdj.length > 0 ){ | |
int idx = (int)(Math.random()*FreeAdj.length); | |
field[FreeAdj[idx].getRow()][FreeAdj[idx].getCol()] = cell; | |
ret = true; | |
} | |
return ret; | |
} | |
/** | |
* 根据坐标删除 cell | |
* @param row 待删除 cell 的坐标:行 | |
* @param col 待删除 cell 的坐标:列 | |
* @return | |
*/ | |
public Cell remove( int row, int col ){ | |
Cell ret = field[row][col]; | |
field[row][col] = null; | |
return ret; | |
} | |
/** | |
* 删除 cell | |
* @param cell 待删除的 cell | |
*/ | |
public void remove( Cell cell ){ | |
for( int row = 0; row < height; row++ ){ | |
for( int col = 0; col <width; col++ ){ | |
if( field[row][col] == cell ){ | |
field[row][col] = null; | |
break; | |
} | |
} | |
} | |
} | |
/** | |
* 清空所有 cell | |
*/ | |
public void clear(){ | |
for( int i = 0; i < height; i++){ | |
for( int j = 0; j < width; j++){ | |
field[i][j] = null; | |
} | |
} | |
} | |
/** | |
* 移动 cell | |
* @param row 当前 cell 的坐标:行 | |
* @param col 当前 cell 的坐标:列 | |
* @param loc 目标坐标的 Location | |
*/ | |
public void move(int row, int col, Location loc){ | |
field[loc.getRow()][loc.getCol()] = field[row][col]; | |
remove(row,col); | |
} | |
public Cell place(int row, int col, Cell c) { | |
Cell ret = field[row][col]; | |
field[row][col] = c; | |
return ret; | |
} | |
} |
# View
类
继承自 JPanel
面板
重写的方法: paint
,设置刚好的尺寸。
package field; | |
import java.awt.Color; | |
import java.awt.Dimension; | |
import java.awt.Graphics; | |
import javax.swing.JPanel; | |
import cell.Cell; | |
public class View extends JPanel{ | |
private static final long serialVersionUID = -2417015700213488315L; | |
private static final int GRID_SIZE = 16; | |
private Field thefield; | |
public View (Field field){ | |
thefield = field; | |
} | |
@Override | |
public void paint( Graphics g ){ | |
super.paint(g); | |
g.setColor(Color.gray); | |
for( int row = 0; row < thefield.getHeight(); row++ ){ | |
g.drawLine(0, row*GRID_SIZE, thefield.getWidth()*GRID_SIZE, row*GRID_SIZE); | |
} | |
for( int col = 0; col < thefield.getHeight(); col++ ){ | |
g.drawLine(col*GRID_SIZE, 0, col*GRID_SIZE, thefield.getHeight()*GRID_SIZE); | |
} | |
for( int row = 0; row < thefield.getHeight(); row++){ | |
for( int col = 0; col < thefield.getWidth(); col++){ | |
Cell cell = thefield.get(row, col); | |
if( cell != null ){ | |
cell.draw( g, col*GRID_SIZE, row*GRID_SIZE, GRID_SIZE); | |
} | |
} | |
} | |
} | |
@Override | |
public Dimension getPreferredSize(){ | |
return new Dimension(thefield.getWidth()*GRID_SIZE+1, thefield.getHeight()*GRID_SIZE+1); | |
} | |
} |
# animal
包
# Animal
抽象类
是 fox
, rabbit
的父类。
package animal; | |
import java.util.ArrayList; | |
import field.Location; | |
public abstract class Animal | |
{ | |
private int ageLimit; // 生存时间 | |
private int breedableAge; // 生育年龄 | |
private int age; // 现年龄 | |
private boolean isAlive = true; // 是否存活 | |
/** | |
* 构造方法 | |
* @param ageLimit 生存时间 | |
* @param breedableAge 生育年龄 | |
*/ | |
public Animal(int ageLimit, int breedableAge){ | |
this.ageLimit = ageLimit; | |
this.breedableAge = breedableAge; | |
} | |
/** | |
* 简单方法:获取年龄 | |
* @return 现年龄 | |
*/ | |
protected int getAge(){ | |
return age; | |
} | |
/** | |
* 简单方法:获取年龄百分比 | |
* @return 年龄百分比 | |
*/ | |
protected double getAgePercent(){ | |
return (double)age/ageLimit; | |
} | |
/** | |
* 简单方法:存活状态 | |
* @return 是否存活 | |
*/ | |
public boolean isAlive(){ | |
return isAlive; | |
} | |
/** | |
* 简单方法:生育状态 | |
* @return 是否能够生育 | |
*/ | |
public boolean isBreedable(){ | |
return age > breedableAge; | |
} | |
/** | |
* 简单方法:寿命增长 | |
* @param addage 增长数值 | |
*/ | |
protected void longerlife( int addage ){ | |
ageLimit += addage; | |
} | |
/** | |
* 简单方法:成长(到年龄死亡) | |
*/ | |
public void grow(){ | |
age++; | |
if(age > ageLimit){ | |
die(); | |
} | |
} | |
/** | |
* 简单方法:死亡 | |
*/ | |
private void die() { | |
isAlive = false; | |
} | |
/** | |
* 移动方法 | |
* @param freeAdj 周围空 cell 坐标 | |
* @return 其中一个空 cell 坐标 | |
*/ | |
public Location move(Location[] freeAdj){ | |
Location ret = null; | |
if( freeAdj.length > 0 && Math.random() < 0.02 ){ | |
ret = freeAdj[(int)(Math.random()*freeAdj.length)]; | |
} | |
return ret; | |
} | |
/** | |
* 抽象方法:生育 | |
*/ | |
public abstract Animal breed(); | |
/** | |
* 返回值方法:进食 | |
* @param neighbour 附近动物的数组 | |
* @return 被吃的动物 | |
*/ | |
public Animal feed(ArrayList<Animal> neighbour){ | |
return null; | |
} | |
@Override | |
public String toString(){ | |
return " "+age+":"+(isAlive?"live":"dead"); | |
} | |
} |
# Fox
类
继承 Animal
, Cell
package animal; | |
import java.awt.Color; | |
import java.awt.Graphics; | |
import java.util.ArrayList; | |
import cell.Cell; | |
public class Fox extends Animal implements Cell | |
{ | |
/** | |
* 构造方法 | |
* 生存时间 20,生育年龄 4 | |
*/ | |
public Fox(){ | |
super(20,4); // 最大年龄,生育年龄 | |
} | |
/** | |
* 重写接口 cell 方法:draw | |
* @param g cell 图 | |
* @param x x 坐标 | |
* @param y y 坐标 | |
* @param size 大小 | |
*/ | |
@Override | |
public void draw( Graphics g, int x, int y, int size ){ | |
int alpha = (int)((1-getAgePercent())*255); | |
g.setColor(new Color(0,0,0,alpha)); | |
g.fillRect(x, y, size, size); | |
} | |
/** | |
* 重写抽象方法:Animal 的生育 | |
* @return Animal | |
*/ | |
@Override | |
public Animal breed() { | |
Animal ret = null; | |
// 生育的概率是 5% | |
if( isBreedable() && Math.random() < 0.05 ){ | |
ret = new Fox(); | |
} | |
return ret; | |
} | |
/** | |
* 重写方法:Animal 的进食 | |
* @param neighbour 附近兔子的数组 | |
* @return 被吃的兔子 | |
*/ | |
@Override | |
public Animal feed(ArrayList<Animal> neighbour){ | |
Animal ret = null; | |
// 被吃的概率为 20% | |
if( Math.random() < 0.2 ){ | |
ret = neighbour.get((int)(Math.random()*neighbour.size())); | |
longerlife(2); | |
} | |
return ret; | |
} | |
@Override | |
public String toString(){ | |
return "Fox:"+super.toString(); | |
} | |
} |
# Rabbit
类
继承 Animal
, Cell
package animal; | |
import java.awt.Color; | |
import java.awt.Graphics; | |
import cell.Cell; | |
public class Rabbit extends Animal implements Cell | |
{ | |
/** | |
* 构造方法 | |
* 生存时间 10,生育年龄 2 | |
*/ | |
public Rabbit() { | |
super(10, 2); | |
} | |
/** | |
* 重写接口 cell 方法:draw | |
* @param g cell 图 | |
* @param x x 坐标 | |
* @param y y 坐标 | |
* @param size 大小 | |
*/ | |
@Override | |
public void draw(Graphics g, int x, int y, int size) { | |
int alpha = (int)((1-getAgePercent())*255); | |
g.setColor(new Color(255,0,0,alpha)); | |
g.fillRect(x, y, size, size); | |
} | |
/** | |
* 重写抽象方法:Animal 的生育 | |
* @return Animal | |
*/ | |
@Override | |
public Animal breed() { | |
Animal ret = null; | |
// 生育的概率是 12% | |
if( isBreedable() && Math.random() < 0.12 ){ | |
ret = new Rabbit(); | |
} | |
return ret; | |
} | |
@Override | |
public String toString(){ | |
return "Rabbit:"+super.toString(); | |
} | |
} |
# 课堂讨论
# 细胞自动机
- 为什么没有
Cell.setAlive()
?为什么不是在Cell
提供setAlive(boolean)
函数?而是采用复杂的die()
、reborn()
两个函数?
提高可阅读性,且
Cellmachine
类就是用来实现人机交互,业务逻辑交给Cell
类。
- 为什么
Field.getNeighbour()
不直接看Cell.isAlive()
来返回一个数字,而是要返回一个数组让外面来数数?
Field
这个类的职责是为对象提供一个可定位的安放空间,它不需要知道对象的任何额外信息。这种低耦合设计使得Cell
变动时Field
无须作出调整。
- 为什么不是由
Cell
自己判断自己的邻居的情况来决定自己是否应该被die
或reborn
?
Cell
要知道自己被生还是被死,就要获取一个数组,Cell
类,Cellmachine
类,Field
类就会相互使用,面向设计原则有一点是单一原则,各类各司其职,判断细胞是生是死让Cellmachine
做就行了,Cell
只要负责提供细胞就行了。
好处就是简化了Cellmachine
的代码,增加可读性,降低复杂性。
# 狐狸与兔子
Fox.breed()
和Rabbit.breed()
几乎一样,有什么好办法修改?注意Fox
和Rabbit
的breed()
返回的具体类型不同。
Animal
中定义
protected double breedPercent; | |
public abstract Animal newAnimal(); | |
public Animal breed() { | |
Animal ret = null; | |
if ( isBreedable() && Math.random()< breedPercent) { | |
return newAnimal(); | |
} | |
return ret; | |
} |
Fox
中定义
protected double breedPercent = 0.05; | |
@Override | |
public Animal newAnimal() { | |
return new Fox(); | |
} |
Rabbit
中定义
protected double breedPercent = 0.12; | |
@Override | |
public Animal newAnimal() { | |
return new Rabbit(); | |
} |
- 在城堡游戏中,
Handler
是知道Game
的;在细胞自动机中,Cell
是不知道Field
的。在现在版本的狐狸与兔子中,Cell
也是不知道Field
的。那么,如果让Cell
知道Field
会怎样呢?两种做法各有什么优缺点呢?
Cell
不知道Field
的话,Cell
比较独立,对Field
的修改不会影响到Cell
,Cell
也可以单独拿到其他地方用。
如果Cell
和Field
之间有重复的复杂的交互的话,可以把交互过程放到一个方法里;如果这种交互的主体是Cell
的话,那么把交互方法放入Cell
就有一定合理性,这类交互越多,Cell
知道Field
的好处就越多。其中就是独立和便捷之间的权衡。
- 如果另外用一个
ArrayList<Animal>
来表示所有的动物,每一步遍历这个列表而非整个Field
,这样做是否更好?
提示:这样就需要每个Animal
知道自己在Field
里的位置。
Animal
知道位置的话对Field
就有了依赖。
死亡时需要查找和删除动物,使用ArrayList
效率不高,用HashMap
好些。
在动物出生和移动时,需要同时设置Field
和Animal
中的数据。
- 现在
Cell
接口其实是承担了两个责任的。在Field
看过来,它只认识Cell
,它认为Cell
是存放在其中的数据。而对于View
来说,它看到的是Cell
所具有的draw
方法,从这个角度来说Cell
表示了表现。
有没有必要两种分开。比如有一个Cell
抽象类表示可以放进Field
的东西,另一个Drawable
接口表示可以被View
画出来的东西?