欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

使用装饰者模式扩展坦克代码-设计模式之坦克大战

程序员文章站 2022-06-19 15:11:06
复习总结装饰者模式的实现原理以及与继承的区别,同时介绍在坦克大战代码中应用中出现的问题,相应设计模式要在特定场景下发挥作用,不能为了设计模式而设计模式。装饰者模式的核心就是将待修饰类对象传参到装饰类,然后得到一个新的、更加强大、可以支持更多功能的修饰后的类对象。......

目录

一、复习总结装饰者模式

1、实现原理:

2、与继承的区别:

二、坦克大战中应用装饰者模式

1、结构图

2、 实现存在的问题

(1)碰撞检测逻辑出问题

(2)解决方案


一、复习总结装饰者模式

先来复习一下装饰者模式,

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

详细代码在这里:

https://github.com/phs999/DesignPatterns/tree/b8e29f38855ff4bcc75d2e3da41c4d9d0259b5d8/tank/src/phs999/tank/decorator

(2)解决方案

可以考虑对具体的游戏对象比如Bullet、Tank、Wall进行分别抽象,相应的为AbstractBullet、AbstractTank、AbstractWall,然后针对这些抽象类(AbstractBullet、AbstractTank、AbstractWall)进行装饰者模式的实现和应用,而不是GameObject。

所以说,装饰者模式的在使用前,有必要明确要被装饰的类是否合适,层次与等级。不是抽象等级越高越好,不是越抽象越好。

甚至,并不是应用设计模式就是高级的,就是好的。设计模式只是针对某些特定场景、解决特定问题的。如果使得代码逻辑更加复杂不可控,并造成新的问题,那就有必要衡量是不是此处场景不用更好。

本文地址:https://blog.csdn.net/phs999/article/details/107827532