单一责任原则对特定代码的实施

时间:2016-11-14 01:24:20

标签: java solid-principles

如何细分此代码以遵循单一责任原则的原则?即使我理解SOLID原则并阅读了很多材料,特别是Uncle Bob关于SOLID原则的文章,但遗憾的是我无法将以下代码拆分为两个不同的类来遵循单一责任原则。我非常感谢StackOverflow提供的帮助

/ **完全利用的唯一子类  实体超类(没有其他类需要  基于图块的地图中的移动)。  包含与之相关的所有游戏玩法  播放器。** /

package com.neet.DiamondHunter.Entity;

import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import com.neet.DiamondHunter.Manager.Content;
import com.neet.DiamondHunter.Manager.JukeBox;
import com.neet.DiamondHunter.TileMap.TileMap;
public class Player extends Entity {
    // sprites
    private BufferedImage[] downSprites;
    private BufferedImage[] leftSprites;
    private BufferedImage[] rightSprites;
    private BufferedImage[] upSprites;
    private BufferedImage[] downBoatSprites;
    private BufferedImage[] leftBoatSprites;
    private BufferedImage[] rightBoatSprites;
    private BufferedImage[] upBoatSprites;

    // animation
    private final int DOWN = 0;
    private final int LEFT = 1;
    private final int RIGHT = 2;
    private final int UP = 3;
    private final int DOWNBOAT = 4;
    private final int LEFTBOAT = 5;
    private final int RIGHTBOAT = 6;
    private final int UPBOAT = 7;

    // gameplay
    private int numDiamonds;
    private int totalDiamonds;
    private boolean hasBoat;
    private boolean hasAxe;
    private boolean onWater;
    private long ticks;

    // player status
    private int healthPoints;
    private boolean invincible;
    private boolean powerUp;
    private boolean speedUp;


    public Player(TileMap tm) {

        super(tm);

        width = 16;
        height = 16;
        cwidth = 12;
        cheight = 12;

        moveSpeed = 2;

        numDiamonds = 0;

        downSprites = Content.PLAYER[0];
        leftSprites = Content.PLAYER[1];
        rightSprites = Content.PLAYER[2];
        upSprites = Content.PLAYER[3];
        downBoatSprites = Content.PLAYER[4];
        leftBoatSprites = Content.PLAYER[5];
        rightBoatSprites = Content.PLAYER[6];
        upBoatSprites = Content.PLAYER[7];

        animation.setFrames(downSprites);
        animation.setDelay(10);

    }

    private void setAnimation(int i, BufferedImage[] bi, int d) {
        setAnimation(i, bi, d, false);
    }

    private void setAnimation(int i, BufferedImage[] bi, int d, boolean slowMotion) {
        currentAnimation = i;
        animation.setFrames(bi);
        animation.setDelay(d);
        slowMotion = true;
    }

    public void collectedDiamond() { numDiamonds++; }
    public int numDiamonds() { return numDiamonds; }
    public int getTotalDiamonds() { return totalDiamonds; }
    public void setTotalDiamonds(int i) { totalDiamonds = i; }

    public int getx() { return x; }
    public int gety() { return y; }
    public int getRow() { return rowTile; }
    public int getCol() { return colTile; }

    public void gotBoat() { hasBoat = true; tileMap.replace(22, 4); }
    public void gotAxe() { hasAxe = true; }
    public boolean hasBoat() { return hasBoat; }
    public boolean hasAxe() { return hasAxe; }

    public int getHealthPoints() { return healthPoints; }

    // Used to update time.
    public long getTicks() { return ticks; }

    // Keyboard input. Moves the player.
    public void setDown() {
        super.setDown();
    }
    public void setLeft() {
        super.setLeft();
    }
    public void setRight() {
        super.setRight();
    }
    public void setUp() {
        super.setUp();
    }

    // Keyboard input.
    // If Player has axe, dead trees in front
    // of the Player will be chopped down.
    public void setAction() {
        final boolean pressUPKEY = currentAnimation == UP && tileMap.getIndex(rowTile - 1, colTile) == 21;
        final boolean pressDOWNKEY = currentAnimation == DOWN && tileMap.getIndex(rowTile + 1, colTile) == 21;
        final boolean pressLEFTKEY = currentAnimation == LEFT && tileMap.getIndex(rowTile, colTile - 1) == 21;
        final boolean pressRIGHTKEY = currentAnimation == RIGHT && tileMap.getIndex(rowTile, colTile + 1) == 21;
        if(hasAxe) {
            if(pressUPKEY) {
                tileMap.setTile(rowTile - 1, colTile, 1);
            }
            if(pressDOWNKEY) {
                tileMap.setTile(rowTile + 1, colTile, 1);
            }
            if(pressLEFTKEY) {
                tileMap.setTile(rowTile, colTile - 1, 1);
            }
            if(pressRIGHTKEY) {
                tileMap.setTile(rowTile, colTile + 1, 1);
            }
            JukeBox.play("tilechange");
        }
    }

    public void update() {

        ticks++;
        boolean current = onWater;
        onWater = CheckIfOnWater();

        //if going from land to water
        if(!current && onWater){
            JukeBox.play("splash");
        }

        // set animation
        setAnimationDown();
        setAnimationLeft();
        setAnimationRight();
        setAnimationUp();

        // update position
        super.update();

    }

    public void setAnimationUp() {
        if(up) {

            if(onWater && currentAnimation != UPBOAT) {
                setAnimation(UPBOAT, upBoatSprites, 10);
            }
            else if(!onWater && currentAnimation != UP) {
                setAnimation(UP, upSprites, 10);
            }
        }
    }

    public void setAnimationRight() {
        if(right) {

            if(onWater && currentAnimation != RIGHTBOAT) {
                setAnimation(RIGHTBOAT, rightBoatSprites, 10);
            }
            else if(!onWater && currentAnimation != RIGHT) {
                setAnimation(RIGHT, rightSprites, 10);
            }
        }
    }

    public void setAnimationLeft() {
        if(left) {


            if(onWater && currentAnimation != LEFTBOAT) {
                setAnimation(LEFTBOAT, leftBoatSprites, 10);
            }
            else if(!onWater && currentAnimation != LEFT) {
                setAnimation(LEFT, leftSprites, 10);
            }
        }
    }

    public void setAnimationDown() {
        if(down) {
            if(onWater && currentAnimation != DOWNBOAT) {
                setAnimation(DOWNBOAT, downBoatSprites, 10);
            }
            else if(!onWater && currentAnimation != DOWN) {
                setAnimation(DOWN, downSprites, 10);
            }
        }
    }

    public boolean CheckIfOnWater(){
        int index = tileMap.getIndex(ydest / tileSize, xdest / tileSize);
        if(index == 4) {
            return true;
        }
        else {
            return false;
        }

    }

    // Draw Player.
    public void draw(Graphics2D g)
    {
        super.draw(g);
    }

}

2 个答案:

答案 0 :(得分:1)

尝试以模型视图控制器样式实现组件,例如,将所有与更新视图的代码移动到yourapp.view包中,并将当前类Player的代码移动到新类中例如,GameBoard或GameView或whaterver,其中该类的单一责任是更新在游戏板屏幕中绘制动画/图像等的模型表示。另一个类,例如,PlayerMovement并移动所有与de键盘事件相关的代码,当前在你的Player类中,此类的责任是捕捉使玩家移动的键。

移动与游戏命令和目标相关的所有代码,并移动另一个包yourapp.controller或动作或其他任何包,并创建新的类,例如PlayerController,GameController或其他任何东西,其中此类的单一责任接收播放器请求更新游戏模型状态,寻址命令和对模型类说法更新,并说每次模型更改时查看获得新模型状态的类;例如,当玩家在游戏板上有一个新位置,或某些missil或某些角色的位置死亡等时。

将模型类放在其他包中,例如yourapp.character或actors或者其他包,并移动与模型状态相关的代码,创建表示游戏角色或演员的新类或定义行为或角色的实时元素你的游戏例如玩家或船只或加农炮等。这类他们的责任只是定义游戏角色及其角色和行为例如游戏牌中的位置,他们的武器,他们的力量,如果是活的还是死的等等,以及关于他们在游戏中的角色的其他信息。

尝试在第二阶段识别并应用GoF模式,这可以帮助您重构代码并使其更符合SOLID原则。

答案 1 :(得分:0)

关于SRP的另一个想法是每个单位都应该有一个单一的改变原因。

上课,你将来可能会改变它的原因是什么:

  • 精灵们改变了。
  • 动画改变。
  • 游戏玩法发生了变化。
  • 玩家状态逻辑发生变化。

您组织了各个字段,因此您自己发现了它们。现在再往前走一步,为每个人创建类。因此,您可以更改动画,而不会触及影响游戏玩法的代码。这样:

  • 使更改更安全(在没有良好测试套件的情况下副作用更少)
  • 让您更轻松地工作,您需要阅读更少的代码,只需要您感兴趣的部分
  • 让您的团队更轻松地查看更改(例如,他们会在更改动画时知道您没有影响游戏玩法)
  • 让团队更轻松地在系统的不同部分工作,减少合并冲突的可能性
  • 可以轻松地将一个组件替换为另一个实现,例如,您可以通过将sprite组件替换为另一个组件来重新设置游戏皮肤