• 设计模式之装饰者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())
                    )
                )
            );

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

    心有所想,必有回响
  • 相关阅读:
    android dp深度解析(转)
    MySQL主从同步开源组件
    JQuery学习笔记
    JavaScript学习笔记
    css学习笔记一
    与spring的相关代码,开发中的经验总结
    传统的服务端有状态Session至JWT的无状态至OAuth2至OAuth2+JWT
    Post请求的两种编码格式:application/x-www-form-urlencoded和multipart/form-data(转)
    mybatis xml文件对象中的集合 resultMap该如何映射
    Java 请求的@RequestBody
  • 原文地址:https://www.cnblogs.com/zhulei2/p/13697733.html
Copyright © 2020-2023  润新知