6.抽象与接口

6.1.抽象

       在第一章就有一个Shape类的例子。这个类有很多的子类,每个子类也都实现了父类的方法。实际上父类Shape只是一个抽象的概念而并没有实际的意义。如果请你画一个圆,你知道该怎么画;如果请你画一个矩形,你也知道该怎么画。但是如果我说:“请画一个形状,句号”。你该怎么画?同样,我们可以定义Circle类和Rectangle类的draw(),但是Shape类的draw()呢?

         Shape类表达的是一种概念,一种共同属性的抽象集合,我们并不希望任何Shape类的对象会被创建出来。那么,我们就应该把这个Shape类定义为抽象的。我们用abstract关键字来定义抽象类。抽象类的作用仅仅是表达接口,而不是具体的实现细节。抽象类中可以存在抽象方法。抽象方法也是使用abstract关键字来修饰。抽象的方法是不完全的,它只是一个方法签名而完全没有方法体。
         如果一个类有了一个抽象的方法,这个类就必须声明为抽象类。如果父类是抽象类,那么子类必须覆盖所有在父类中的抽象方法,否则子类也成为一个抽象类。一个抽象类可以没有任何抽象方法,所有的方法都有方法体,但是整个类是抽象的。设计这样的抽象类主要是为了防止制造它的对象出来。

在shape例子里有abstract的类及方法如下:

public abstract class Shape {
	
	public abstract void draw(Graphics g);
	
}

shape没有存在的意义只是表达一种概念,draw方法也没有存在意义。

实现抽象方法:

两种抽象:

与具体相对:表示一种概念而非实体

与细节相对:表示在一定程度上忽略细节而着眼大局

 

6.2.数据与表现分离

细胞自动机:

‘’

cellmachine:

package cellmachine;
import javax.swing.JFrame;

import cell.Cell;
import field.Field;
import field.View;

public class CellMachine {
	public static void main(String[] args) {
		//30*30的网格
		Field field = new Field(30,30);
		//在每个网格中放置cell对象
		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();
			}
		}
	}
}

view:

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

Data:Field/Cell

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

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

网格化:

问题:

1、为什么不是在cell提供setAlive(Boolean)函数?而是采用复杂的die()、reborn()两个函数?

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

3、为什么不是cell自己判断自己的邻居的情况来决定自己是否应该die或reborn?

 

6.3.接口:狐狸和兔子

狐狸和兔子都有年龄

当年龄到了一定的上线就会自然死亡

狐狸可以随机决定在周围的兔子中吃一个

狐狸哥兔子可以随机决定吃一个小的,放在旁边的空的格子里

如果不吃也不生,狐狸和兔子可以随机决定向旁边空的格子移一步

cell类的地位很尴尬:

      从图中可得rabbit继承animal类和cell类,这是一种多继承,大多数的oop语言都不支持多继承(除c++外),因为多继承在实现上不好实现。

若animal类与cell类相关联从语义上是不可行的,动物类和格子并没有确定的关系。但filed还需一个他认识的东西进行管理。

接口:

所以将cell定义为接口,fox和rabbit实现cell的接口。使用关键词implements

因为cell是接口所以他不可能有对象,o的意思是任何实现了cell接口的对象(fox/rabbit)

实现接口:

面对接口的编程方式:

           Cell和Field的关系:cell在field中,但是cell的很多操作需要field的数据。方法一:让每个cell有一个filed的管理者(cell知道field)。方法二:有外部第三方来建立两者之间的联系(cell不知道filed)。

讨论

  1. cell要不要知道field? 在城堡游戏中,handler是知道game的;在细胞自动机中cell是不知道field的;
  2. 如果另外用一个ArrayList<animal>来表示所有的动物,每一步遍历这个列表而非整个ffield,这样做是否更好?(这样就需要每个animal知道自己在field里的位置)

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值