1、数据与表现分离--细胞自动机:
- 死亡:如果活着的邻居的数量<2或>3,则死亡;
- 新生:如果正好有3个邻居活着,则新生;
- 其他情况则保持原状;
package cellmachine;
import java.lang.reflect.Field;
import javax.swing.JFrame;
public class Cellmachine {
public static void main(String[] args) {
Field field = new Field(30,30);//30x30的网格
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) {//0-0.1之间的数
cell.reborn();
}
}
}
View view = new View(field);
JFrame frame = new JFrame;
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//默认关闭操作:x结束操作
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()) {
numOfLice++;
}
}
System.out.print("["+row+"]["+col+"]:");
System.out.print(cell.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();*
}
}
}
}
1.1 数据与表现分离:
- 程序的业务逻辑与表现无关;
- 表现可以是图形的也可以是文本的;
- 表现可以是当地的也可以是远程的;
1.2 View和Field的关系:
- 表现和数据的关系;
- View只管根据Field画出图形;
- Field只管数据的存放;
- 一旦数据更新以后,通知View重新画出整个画面;
- 不去精心设计哪个局部需要更新;
- 这样简化了程序逻辑;
- 是在计算机运算速度提高的基础上实现的;
package field;
import java.awt.Dimension;
import java.lang.reflect.Field;
public class View extends JPanel {//图形库画面
private static final long serialVersionUID = -52589956212330595L;
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);
}
}
1.3 责任驱动的设计:
- 将程序要实现的功能分配到合适的类/对象中去是设计中非常重要的一环;
1.4 网格化:
- 图形界面本身有更高的解析度;
- 但是将画面网格化以后,数据就更容易处理了;
1.5 问题0:
- 为什么不是在Cell提供setAlive(boolean)函数,而是采用复杂的die()、reborn()函数?
package cells;
import java.awt.Graphics;
public class Cell {
private boolean alive = false;
public void die() {alive = false;}
public void reborn() {alive = false;}
public boolean isAlive() {return alive;}
public void draw(Graphics g,int x,int y,int size) {
if(alive) {
g.fillRect(x,y,size,size);
}
}
}
1.6 问题1:
- 为什么Field.getNeighbor()不直接看Cell.isAlive()来返回一个数字,而是要返回一个数组让外面来数数?
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()]);
}
1.7 问题2:
- 为什么不是由Cell自己判断自己的邻居的情况来决定自己是否应该被die或reborn?
package cells;
import java.awt.Graphics;
public class Cell {
private boolean alive = false;
public void die() {alive = false;}
public void reborn() {alive = false;}
public boolean isAlive() {return alive;}
public void draw(Graphics g,int x,int y,int size) {
if(alive) {
g.fillRect(x,y,size,size);
}
}
}
2、接口--狐狸与兔子:
2.1 狐狸与兔子:
- 狐狸与兔子都有年龄;
- 当年龄到了一定的上限就会自然死亡;
- 狐狸可以随机决定在周围的兔子中吃一个;
- 狐狸和兔子可以随机决定生一个小的,放在旁边的空的格子里;
- 如果不吃也不生,狐狸和兔子可以随机决定向旁边空的格子移一步;
package foxnrabbit;
import java.util.ArrayList;
import javax.swing.JFrame;
public class FoxnrAndabbit {
private Field theField;
private View theView;
public FoxnrAndrabbit(int 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);
}
}
2.2 Cell类的地位很尴尬:
- 在Cells程序中它表达了细胞;
- 但是同时它也表达了放在网格中的一个格子‘
-
Fox和Rabbit是否应该从Cell继承?
2.3 接口(表达概念、规范):
- 接口是纯抽象类;
- 所有的成员函数都是抽象函数;
- 所有的成员变量都是public static final;
-
接口规定了长什么样,但是不管里面有什么;
package field;
import java.util.ArrayList;
public class Field {
private static final Location[] adjacent = {
new Locaton(-1-1),new Location(-1,0),new Location(-1,-1),
new Locaton(0,-1),new Location(0,0),new Location(0,1),
new Locaton(1-1),new Location(1,0),new Location(1,1),
};
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 gerHeight() {return height;}
public Cell place(int row,int col,Cell o) {//o表示任何实现了Cell接口的对象
return field[row][col];
}
2.4 主程序:
package foxnrabbit;
import java.util.ArrayList;
import javax.swing.JFrame;
import field.Field;
public class FoxnrAndabbit {
private Field theField;
private View theView;
public FoxnrAndrabbit(int 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("Cells");
frame.add(theView);
frame.pack();
frame.setVisible(true);
}//窗口
public void start(int steps) {
for(int i=0;i<steps;i++) {
step();
theView.repaint();
try {
Thread.sleep(200);
}catch(InterruptedException e) {
e.printStackTrace();
}
}
}
publci 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
Cell[]neighbour = theField.getNeighbour(row,col);
ArrayList(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(bay!=null) {
theField.placeRandomAdj(row, col, (Cell)baby);
}
}else {
theField.remove(row,col);
}
}
}
}
}
}
2.5 接口设计模式:
package cell;
import java.awt.Graphics;
public interface Cell {
void draw(Graphics g,int x,int y,int size);
}
实现接口:
- 类用extends,接口用implements(一个类实现接口);
- 类可以实现很多接口;
- 接口可以继承接口,但不能继承类;
- 接口不能实现接口;
面向接口的编程方式:
- 设计程序时先定义接口,再实现类;
- 任何需要在函数间传入传出的一定是接口而不是具体的类;
- 是java成功的关键之一,因为极适合多人同时写一个大程序;
- 也是java被批评的要点之一,因为代码量膨胀起来很快;
2.6 Cell和Field的关系:
- Cell在Field中,但是Cell的很多操作需要Field的数据;
- 方法一:
- 让每个Cell有一个Field的管理者(Cell知道Field)
animal.eat(theField);
Cell[]neighbour = theField.getNeighbour(row,col);
ArrayList(Cell an:neighbour){
if(an instanceof Rabbit) {
listRabbit.add(Rabbit)an);
}
}
- 方法二:
- 由外部第三方来建立两者之间的联系(Cell不知道Field)
2.7 讨论:
讨论0:
- Cell要不要知道Field ?
- 在城堡游戏中,Handler是知道Game的;
- 在细胞自动机中,Cell是不知道Field的;
讨论1:
- 如果另外用一个ArrayList<Animal>来表示所有的动物,每一步遍历这个列表而非整个Field,这样做是否更好?
- 这样就需要每个Animal知道自己在Field里的位置;