使用装饰者模式扩展坦克代码-设计模式之坦克大战
目录
一、复习总结装饰者模式
先来复习一下装饰者模式,
https://blog.csdn.net/phs999/article/details/107726853
装饰者模式的核心就是将待修饰类对象传参到装饰类,然后得到一个新的、更加强大、可以支持更多功能、修饰后的类对象。
而待修饰类对象与修饰后的对象 类型相同。
1、实现原理:
(1)装饰类与被装饰类都实现同一个接口或继承同一个抽象类。
(2)装饰类的构造方法必须是有参构造方法,而且参数是要被修饰的对象。
(3)修饰类中实现了对被修饰对象的方法重写和扩展。
2、与继承的区别:
(1)修饰类的构造方法是带参的,而且可以实现多个修饰类同时使用。
二、坦克大战中应用装饰者模式
在坦克大战中应用该设计模式,目的是练习,但应用之后会出现各种问题,下面主要讨论一下这些问题,以便实际应用中提前避免。
1、结构图
就像上面总结的装饰者模式的实现原理,坦克大战中一样。此处将游戏对象GameObject作为被修饰的抽象类,所有的游戏对象实体比如Bullet、Wall、Tank等都是他的子类。相应的,装饰类也要继承GameObject,并将GameObject作为构造方法参数。
2、 实现存在的问题
(1)碰撞检测逻辑出问题
碰撞检测的逻辑可以参考之前的文章,使用责任链模式实现:
https://blog.csdn.net/phs999/article/details/107453246
比如子弹与坦克之间的碰撞检测:
package phs999.tank.cor;
import phs999.tank.Bullet;
import phs999.tank.GameObject;
import phs999.tank.Tank;
public class BulletTankCollider implements Collider {
@Override
public boolean collide(GameObject o1, GameObject o2) {
if (o1 instanceof Bullet && o2 instanceof Tank) {
Bullet b = (Bullet)o1;
Tank t = (Tank)o2;
if (b.getGroup().equals(t.getGroup())) {
return false;
}
if (b.getRect().intersects(t.getRect())) {
t.die();
b.die();
return true;
}
}else if (o1 instanceof Tank && o2 instanceof Bullet) {
collide(o2,o1);
}else {
return false;
}
return false;
}
}
也就是必须明确是什么类型的类,是子弹还是坦克还是墙,然后才能进行不同逻辑分支的碰撞检测。
而我们如果对GameObject进行装饰,装饰后也只能得到GameObject,但是无法进行具体类的判断,进而不能满足碰撞检测的逻辑。
首先,是装饰者抽象类:
package phs999.tank.decorator;
import java.awt.Graphics;
import phs999.tank.GameObject;
public abstract class GameObjectDecorator extends GameObject{
GameObject go;
public GameObjectDecorator(GameObject go) {
this.go=go;
}
@Override
public void paint(Graphics g) {
go.paint(g);
}
@Override
public int getX() {
return go.getX();
}
@Override
public int getY() {
return go.getY();
}
}
然后是具体的抽象类:
package phs999.tank.decorator;
import java.awt.Color;
import java.awt.Graphics;
import phs999.tank.GameObject;
public class RectDecorator extends GameObjectDecorator{
public RectDecorator(GameObject go) {
super(go);
}
@Override
public void paint(Graphics g) {
super.paint(g);
Color color=g.getColor();
g.setColor(Color.YELLOW);
g.drawRect(getX(), getY(), getWidth()+1, getHeight()+1);
}
@Override
public int getWidth() {
return go.getWidth();
}
@Override
public int getHeight() {
return go.getHeight();
}
}
然后,我们在统一管理添加游戏对象的类GameModelFacade中,在初始化墙并添加到对象列表的时候,调用了装饰者类。具体代码在第30行。
package phs999.tank;
import java.awt.Graphics;
import java.util.ArrayList;
import java.util.List;
import phs999.tank.cor.ColliderChain;
import phs999.tank.decorator.RectDecorator;
public class GameModelFacade {
private static GameModelFacade gm=new GameModelFacade();
Tank myTank = new Tank(200, 400, Dir.UP,Group.GOOD);
/*
* List<Bullet> bullets =new ArrayList<>(); List<Tank> enemyTanks=new
* ArrayList<>(); List<Explode> explodes=new ArrayList<>();
*/
private List<GameObject> objects=new ArrayList<GameObject>();
// Collider collider1=new BulletTankCollider();
// Collider collider2=new TankTankCollider();
ColliderChain chain=new ColliderChain();
private GameModelFacade() {
int initTankCount=Integer.parseInt((String)PropertyMgr.get("initTankCount"));
//初始化敌方坦克
for (int i = 0; i < initTankCount; i++) {
add(new Tank(40+80*i, 200, Dir.DOWN,Group.BAD));
}
add(myTank);
add(new RectDecorator(new Wall(150, 150, 200, 50)));
add(new Wall(550, 150, 200, 50));
add(new Wall(300, 300, 50, 200));
add(new Wall(550, 300, 50, 200));
}
public static GameModelFacade getInstance() {
return gm;
}
public void paint(Graphics g) {
myTank.paint(g);
for (int i = 0; i < objects.size(); i++) {
objects.get(i).paint(g);
}
//碰撞检测
for (int i = 0; i <objects.size(); i++) {
//collider2.collide(objects.get(i), myTank);
for (int j = i+1; j < objects.size();j++) {
chain.collide(objects.get(i), objects.get(j));
//collider1.collide(objects.get(i), objects.get(j));//子弹坦克
//collider2.collide(objects.get(i), objects.get(j));//坦克坦克
}
}
}
public void add(GameObject go) {
objects.add(go);
}
public void remove(GameObject go) {
objects.remove(go);
}
public Tank getMainTank() {
return myTank;
}
}
详细代码在这里:
(2)解决方案
可以考虑对具体的游戏对象比如Bullet、Tank、Wall进行分别抽象,相应的为AbstractBullet、AbstractTank、AbstractWall,然后针对这些抽象类(AbstractBullet、AbstractTank、AbstractWall)进行装饰者模式的实现和应用,而不是GameObject。
所以说,装饰者模式的在使用前,有必要明确要被装饰的类是否合适,层次与等级。不是抽象等级越高越好,不是越抽象越好。
甚至,并不是应用设计模式就是高级的,就是好的。设计模式只是针对某些特定场景、解决特定问题的。如果使得代码逻辑更加复杂不可控,并造成新的问题,那就有必要衡量是不是此处场景不用更好。
本文地址:https://blog.csdn.net/phs999/article/details/107827532
上一篇: 建造者模式【图解,清晰思路】代码演示实现
下一篇: 设计模式-访问者模式