设计模式之装饰者decorator模式

概念

字面意思,用来做装饰的。

原理:通过类的聚合

例子(github游戏代码地址

(游戏设计bug非常多,完全是为了学设计模式而写,勿喷。除了用的装饰模式,还混杂着其他设计模式,有些实在不适合混杂在一起,我又重新归零,另建了分支。

操作:上下左右控制移动,ctrl键发射一颗子弹,A键发射4颗子弹)

现有一个坦克的小游戏,我想要对坦克发射的子弹进行装饰。

原先的游戏界面:

 

我想要给子弹加个方框,然后再加个尾巴。效果图:

 

 

 

 

 

游戏中类的结构图:

 

稍作分析:

所有游戏物体(Bullet,Tank,Wall)都抽象出共同父类GameObject。抽象的GODecorator装饰器又是从GameObject继承,并且聚合了GameObject。

这样做的好处就是,我可以用修饰器对任意GameObject子类进行修饰,并且修饰之间可以扩展。

 

GameObject抽象类

 

package com.zl.pojo;

import com.zl.enums.Group;

import java.awt.*;

/**
 * @Description
 * @Author zl
 * @Date 2020/9/14 16:04
 * @Version 1.0
 */
public abstract class GameObject {
    public int x, y;

    public abstract void paint(Graphics g);
    public abstract int getWidth();
    public abstract int getHeight();
    public abstract Rectangle getRect();
    public abstract Group getGroup();

}
GameObject类

 

 

Bullet类

 

package com.zl.pojo;

import com.zl.Audio;
import com.zl.GameModel;
import com.zl.ResourceImage;
import com.zl.TankFrame;
import com.zl.enums.Dir;
import com.zl.enums.Group;

import java.awt.*;
import java.awt.image.BufferedImage;

/**
 * @Description 子弹实体
 * @Author zl
 * @Date 2020/8/15 11:13
 * @Version 1.0
 */
public class Bullet extends GameObject{

    //方向
    private Dir dir;

    //子弹宽高
    public static int width = ResourceImage.bulletD.getWidth();
    public static int heigth = ResourceImage.bulletD.getHeight();

    public Rectangle rect = new Rectangle();

    private final static int speed = 6;

    //子弹是否活着
    private boolean living = true;

    public Group group = null;

    TankFrame tf = null;


    public Bullet(int x, int y, Dir dir, Group group) {
        this.x = x;
        this.y = y;
        this.dir = dir;
        this.group = group;
        rect.x = this.x;
        rect.y = this.y;
        rect.width = width;
        rect.height = heigth;
        //GameModel.getInstance().add(this);
        if (group == Group.GOOD)
        new Thread(()->new Audio("com/zl/audio/tank_fire.wav").play()).start();
    }

    public Dir getDir() {
        return dir;
    }

    public void setDir(Dir dir) {
        this.dir = dir;
    }


    public void paint(Graphics g) {

        if (!living) {
            GameModel.getInstance().remove(this);
        }

//        Color color = g.getColor();
//        g.setColor(Color.RED);
//        g.fillOval(x,y,10,10);
//        g.setColor(color);
        move();
        BufferedImage image = null;
        switch (dir) {
            case UP:
                image = ResourceImage.bulletU;
                break;
            case DOWN:
                image = ResourceImage.bulletD;
                break;
            case LEFT:
                image = ResourceImage.bulletL;
                break;
            case RIGHT:
                image = ResourceImage.bulletR;
                break;
        }
        g.drawImage(image,x,y,null);

    }

    @Override
    public int getWidth() {
        return width;
    }

    @Override
    public int getHeight() {
        return heigth;
    }

    @Override
    public Rectangle getRect() {
        return rect;
    }

    @Override
    public Group getGroup() {
        return group;
    }

    private void move() {
        switch (dir){
            case UP:
                y-=speed;
                break;
            case DOWN:
                y+=speed;
                break;
            case LEFT:
                x-=speed;
                break;
            case RIGHT:
                x+=speed;
                break;
        }

        rect.x = this.x;
        rect.y = this.y;

        if (x<0 || y<0 || x> TankFrame.GAME_WIDTH || y>TankFrame.GAME_HEIGHT) living = false;
        GameModel.getInstance().collideWith();
    }

    /*public void collideWith(Tank tank) {
        if (this.group == tank.getGroup()) return;
        if (rect.intersects(tank.rect)) {
            tank.die();
            this.die();
            gm.gameObjects.add(new Explode(tank.getX()+ Tank.width/2- Explode.width/2, tank.getY()+ Tank.heigth/2- Explode.heigth/2, gm));
        }
    }*/

    public void die() {
        this.living = false;
    }
}
Bullet类

 

 

GODecorator抽象类

package com.zl.decorator;

import com.zl.pojo.GameObject;

import java.awt.*;

public abstract class GODecorator extends GameObject {

    GameObject go;

    public GODecorator(GameObject go) {
        this.go = go;
    }
    @Override
    public void paint(Graphics g) {
        go.paint(g);
    }
}
GODecorator类

 

RectDecorator类(为物体添加边框

package com.zl.decorator;

import com.zl.enums.Group;
import com.zl.pojo.GameObject;

import java.awt.*;

public class RectDecorator extends GODecorator {
    public RectDecorator(GameObject go) {
        super(go);
    }

    @Override
    public void paint(Graphics g) {
        super.paint(g);
        Color color  = g.getColor();
        g.setColor(Color.BLACK);
        g.drawRect(super.go.x, super.go.y, getWidth()+2, getHeight()+2);
        g.setColor(color);
    }

    @Override
    public int getWidth() {
        return super.go.getWidth();
    }

    @Override
    public int getHeight() {
        return super.go.getHeight();
    }

    @Override
    public Rectangle getRect() {
        return super.go.getRect();
    }

    @Override
    public Group getGroup() {
        return super.go.getGroup();
    }
}
RectDecorator类

 

TailDecorator类(为物体加上尾巴)

package com.zl.decorator;

import com.zl.enums.Group;
import com.zl.pojo.GameObject;

import java.awt.*;

public class TailDecorator extends GODecorator {
    public TailDecorator(GameObject go) {
        super(go);
    }

    @Override
    public void paint(Graphics g) {
        this.x = super.go.x;
        this.y = super.go.y;
        super.paint(g);
        Color color  = g.getColor();
        g.setColor(Color.BLACK);
        g.drawLine(super.go.x, super.go.y, super.go.x-10, super.go.y-20);
        g.setColor(color);
    }

    @Override
    public int getWidth() {
        return super.go.getWidth();
    }

    @Override
    public int getHeight() {
        return super.go.getHeight();
    }

    @Override
    public Rectangle getRect() {
        return super.go.getRect();
    }

    @Override
    public Group getGroup() {
        return super.go.getGroup();
    }
}
TailDecorator类

 

在没有修饰的情况下,当坦克开火打出一颗子弹时,直接new

GameModel.getInstance().add(new Bullet(bx, by, t.getDir(), t.getGroup()));

在需要为子弹加边框时,new一个bullet,把它传到rectDecorator里面即可

GameModel.getInstance().add(
                new RectDecorator(
                        new Bullet(bx, by, t.getDir(), t.getGroup())
                )
        );

如果需要在加边框的基础上加尾巴,接着把rectDecorator传到TailDecorator里面即可

GameModel.getInstance().add(
            new RectDecorator(
                new TailDecorator(
                    new Bullet(bx, by, t.getDir(), t.getGroup())
                )
            )
        );

 

如果需要修饰其他物体,以此类推

 

posted @ 2020-09-20 15:18  风子磊  阅读(170)  评论(0编辑  收藏  举报