以下为个人学习笔记和习题整理
课程:面向对象程序设计 ——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);
	}
}

# 设计理念

  1. 数据与表现分离
    程序的业务逻辑与表现无关
    表现可以是图形的也可以是文本的
    表现可以是当地的也可以是远程的

表现与数据的关系
View 只管根据 Field 画出图形, Field 只管数据的存放。
一旦数据更新以后,通知 View 重新画出整个画面,不去精心设计哪个局部需要更新,这样简化了程序逻辑,是在计算机运算速度提高的基础上实现的。

  1. 责任驱动的设计
    将程序要实现的功能分配到合适的类 / 对象中去是设计中非常中要的一环

  2. 网格化
    图形界面本身有更高的解析度
    但是将画面网格化以后,数据就更容易处理了

# 狐狸与兔子

狐狸、兔子都有年龄;
到达一定年龄上限会自然死亡;
狐狸随机吃掉周围一只兔子;
狐狸、兔子可以随机生一只小的放在旁边格子;
如果不吃不生,狐狸、兔子可以随机向旁边格子移一步会随机吃掉。

# 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 抽象类

foxrabbit 的父类。

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

继承 AnimalCell

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

继承 AnimalCell

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();
	}
}

# 课堂讨论

# 细胞自动机

  1. 为什么没有 Cell.setAlive() ?为什么不是在 Cell 提供 setAlive(boolean) 函数?而是采用复杂的 die()reborn() 两个函数?

提高可阅读性,且 Cellmachine 类就是用来实现人机交互,业务逻辑交给 Cell 类。

  1. 为什么 Field.getNeighbour() 不直接看 Cell.isAlive() 来返回一个数字,而是要返回一个数组让外面来数数?

Field 这个类的职责是为对象提供一个可定位的安放空间,它不需要知道对象的任何额外信息。这种低耦合设计使得 Cell 变动时 Field 无须作出调整。

  1. 为什么不是由 Cell 自己判断自己的邻居的情况来决定自己是否应该被 diereborn

Cell 要知道自己被生还是被死,就要获取一个数组, Cell 类, Cellmachine 类, Field 类就会相互使用,面向设计原则有一点是单一原则,各类各司其职,判断细胞是生是死让 Cellmachine 做就行了, Cell 只要负责提供细胞就行了。
好处就是简化了 Cellmachine 的代码,增加可读性,降低复杂性。

# 狐狸与兔子

  1. Fox.breed()Rabbit.breed() 几乎一样,有什么好办法修改?注意 FoxRabbitbreed() 返回的具体类型不同。

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();
}
  1. 在城堡游戏中, Handler 是知道 Game 的;在细胞自动机中, Cell 是不知道 Field 的。在现在版本的狐狸与兔子中, Cell 也是不知道 Field 的。那么,如果让 Cell 知道 Field 会怎样呢?两种做法各有什么优缺点呢?

Cell 不知道 Field 的话, Cell 比较独立,对 Field 的修改不会影响到 CellCell 也可以单独拿到其他地方用。
如果 CellField 之间有重复的复杂的交互的话,可以把交互过程放到一个方法里;如果这种交互的主体是 Cell 的话,那么把交互方法放入 Cell 就有一定合理性,这类交互越多, Cell 知道 Field 的好处就越多。其中就是独立和便捷之间的权衡。

  1. 如果另外用一个 ArrayList<Animal> 来表示所有的动物,每一步遍历这个列表而非整个 Field ,这样做是否更好?
    提示:这样就需要每个 Animal 知道自己在 Field 里的位置。

Animal 知道位置的话对 Field 就有了依赖。
死亡时需要查找和删除动物,使用 ArrayList 效率不高,用 HashMap 好些。
在动物出生和移动时,需要同时设置 FieldAnimal 中的数据。

  1. 现在 Cell 接口其实是承担了两个责任的。在 Field 看过来,它只认识 Cell ,它认为 Cell 是存放在其中的数据。而对于 View 来说,它看到的是 Cell 所具有的 draw 方法,从这个角度来说 Cell 表示了表现。
    有没有必要两种分开。比如有一个 Cell 抽象类表示可以放进 Field 的东西,另一个 Drawable 接口表示可以被 View 画出来的东西?
阅读次数

请我喝[茶]~( ̄▽ ̄)~*

Ruri Shimotsuki 微信支付

微信支付

Ruri Shimotsuki 支付宝

支付宝

Ruri Shimotsuki 贝宝

贝宝