书名:代码本色:用编程模拟自然系统
作者:Daniel Shiffman
译者:周晗彬
ISBN:978-7-115-36947-5
目录
4.6 继承和多态的简介
1、继承和多态
- 面向对象编程有3个最基本的特性,
- 继承和多态就是其中两个(另一个是封装)。
2、贺卡粒子系统
- 贺卡就是一个粒子系统——由一系列彩条(粒子)组成的集合。
- 我们可以在粒子类中放入各种效果所需的全部变量,比如颜色、形状、行为,等等,然后随机地初始化这些变量,这样做就可以实现想要的效果。
- 但如果这些粒子之间的差异很大,整个程序将会变得非常混乱,因为不同粒子对应的所有代码都被塞到一个类中。
- 对此,你可能会用以下几个类改进这个程序:
class HappyConfetti {
}
class FunConfetti {
}
class WackyConfetti {
}
- 这是个不错的解决方案:我们用3个类分别描述3种不同的彩条,这3种彩条都有可能出现在粒子系统中。在粒子系统的构造函数中,我们随机地创建这3种实例变量,并放入ArrayList中。“随机选择”这部分的代码和“引言”中的随机游走示例的代码是一样的。
class ParticleSystem {
ParticleSystem(int num) {
particles = new ArrayList();
for (int i = 0; i < num; i++) {
float r = random(1);
随机选择一种粒子
if (r < 0.33) { particles.add(new HappyConfetti()); }
else if (r < 0.67) { particles.add(new FunConfetti()); }
else { particles.add(new WackyConfetti()); }
}
}
3、两个问题
问题1: 难道我们要在不同的“彩条”类之间复制或粘贴很多重复的代码?
是的,尽管粒子之间的差异很大,以至于我们要用不同类的分别实现,但它们之间仍然存在很多可以共用的代码。比如:它们都有位置、速度和加速度向量;它们都有一个update()函数用于实现运动的算法,等等。
而继承在这里正好派上用场。面向对象的继承特性让它可以从另一个类中继承变量和函数,如此一来,这个类只需要实现自己特有的特性。问题2:ArrayList怎么知道它里面的对象是什么类型?
这是一个很重要的问题。请记住,我们在ArrayList中存放的是泛型类型。那么是否需要创建3个不同的ArrayList,分别存放不同类型的粒子?
ArrayList<HappyConfetti> a1 = new ArrayList<HappyConfetti>();
ArrayList<FunConfetti> a2 = new ArrayList<FunConfetti>();
ArrayList<WackyConfetti> a3 = new ArrayList<WackyConfetti>();
- 这个实现看起来非常烦琐,最好能用一个列表存放粒子系统中的所有对象。
有了面向对象的多态特性,这就能得以实现。通过多态,我们可以把不同类型的对象当成同种类型,并将它们存放在单个ArrayList中。
4、碎裂
- 模拟一个物体碎裂成很多块的效果。如何将一个大图形分解成许多小粒子?
- 如果屏幕中有很多大图形,它们一被鼠标点中,就会碎裂,如何模拟这一现象?
ParticleSystem ps;
void setup() {
size(640,360);
ps = new ParticleSystem(200,100,8);
}
void draw() {
background(255);
ps.display();
ps.update();
}
void mousePressed() {
ps.shatter();
}
Particle.pde
class Particle {
PVector position;
PVector velocity;
PVector acceleration;
float lifespan;
float r;
color c;
Particle(float x, float y, float r_) {
acceleration = new PVector(0,0.01);
velocity = PVector.random2D();
velocity.mult(0.5);
position = new PVector(x,y);
lifespan = 255.0;
r = r_;
}
void run() {
update();
display();
}
// Method to update position
void update() {
velocity.add(acceleration);
position.add(velocity);
lifespan -= 2.0;
c = color(random(255),random(255),random(255));
}
// Method to display
void display() {
stroke(0);
fill(c);
rectMode(CENTER);
rect(position.x,position.y,r,r);
}
// Is the particle still useful?
boolean isDead() {
if (lifespan < 0.0) {
return true;
} else {
return false;
}
}
}
ParticleSystem.pde
class ParticleSystem {
ArrayList<Particle> particles;
int rows = 20;
int cols = 20;
boolean intact = true;
ParticleSystem(float x, float y, float r) {
particles = new ArrayList<Particle>();
for (int i = 0; i < rows*cols; i++) {
addParticle(x + (i%cols)*r, y + (i/rows)*r, r);
}
}
void addParticle(float x, float y, float r) {
particles.add(new Particle(x, y, r));
}
void display() {
for (Particle p : particles) {
p.display();
}
}
void shatter() {
intact = false;
}
void update() {
if (!intact) {
for (Particle p : particles) {
p.update();
}
}
}
}