【问题标题】:Java OOP: how to create objects properlyJava OOP:如何正确创建对象
【发布时间】:2012-05-28 13:34:45
【问题描述】:

我正在使用 OOP 创建一个简单的保龄球游戏,我想为每个保龄球创建一个类,一个由两个碗组成的 Frame 类和一个由十个帧组成的 Game 类。

目前我有这样的事情

碗.java

public class Bowl {

int bowlScore = 0;

public Bowl(int pinsKnocked){
    bowlScore = pinsKnocked;
}
}

Frame.java

public class Frame{

int firstScore;
int secondScore;
public Bowl firstBowl;
public Bowl secondBowl;

public Frame (){
    firstBowl = new Bowl(0);
    secondBowl = new Bowl(0);
}

public Frame (int firstScore, int secondScore){
    firstBowl = new Bowl(firstScore);
    secondBowl = new Bowl(secondScore);
} 

Game.java

public class Game {

int totalScore;
public Frame firstFrame;
public Frame secondFrame;
...
    public Frame tenthFrame;

public Game(){
    firstFrame = new Frame();   
}

public Game(Frame f){
    firstFrame = f;
}

这是使用 OOP 功能的正确方法吗?或者我该如何改进?

【问题讨论】:

  • 当我掷出 2 次打击时,第 10 帧会发生什么?
  • 我建议使用封装,将所有字段标记为私有/受保护(根据需要)访问类型,并提供getter/setter(根据需要)访问字段。
  • @Nix - 我还没有到第 10 帧,我只是想了解所有类如何协同工作以及它们如何相互访问,然后再进入游戏的细节
  • 值得一读:Test Driven Development: By Example Kent Beck。这个例子是一个保龄球游戏:)
  • 为什么不创建一个框架和碗的array?这样你就没有一堆命名字段。

标签: java oop object


【解决方案1】:

没有一种 100% 正确的方式来设计保龄球游戏;有许多可行的解决方案,甚至更多是行不通的。

您需要的是一个适合您和您的目标的解决方案。

如果你想显示分数,那么我建议你从getScore() 方法开始。如果要显示获胜者,请从 displayWinners() 方法开始。

最终你会发现这些方法自然地绑定到各种名词。例如,您可能一开始将getScore() 附加到Game 对象,但随后意识到这不自然地意味着您的游戏只能有一个分数。如果发生这种情况,您可以将 getScore() 移动到 Player 对象,并让游戏保留一个或多个玩家。

如果您正在使用一种自然属于其他地方的方法,那么您的代码中有许多提示可以指导您。最明显的暗示是一个方法似乎对另一个对象的数据特别感兴趣,甚至对它自己的数据也是如此。在上面的示例中,游戏对象中的getScore() 对玩家的帧、球等过度感兴趣。

要获得如何安全地将代码从一个地方移动到另一个地方的技能和指导,我建议阅读 Martin Fowler 的重构书。

An excellent example, using exactly your problem is demonstrated here.

祝你好运,一段时间后,由于获得的技能,您将可以跳过此过程;但是,第一次学习时,最好不要跳过这些探索性步骤(从头开始)。

PS。请记住,您的代码只会做您测试它要做的事情,如果您不熟悉测试驱动开发,那么研究它可能是一个好主意(提示,这是一个严重的轻描淡写)。

【讨论】:

    【解决方案2】:

    正如 Edwin 所提到的,它们有很多方法可以为保龄球比赛建模。但是,在这里我将列出对您的代码进行可能的更正以改进它。
    这里有很多需要改进的地方
    1. score 属性仅适用于 Bowl 类。所以从 Frame 和 Game 中删除 score 属性。
    2.Bowl中的bowlScore应该是私有的,并为其提供getter方法。你会
    3. 现在 Frame 类应该是这样的:

    public class Frame{
        private Bowl firstBowl;
        private Bowl secondBowl;
    
        public Frame (int firstScore, int secondScore){
            firstBowl = new Bowl(firstScore);
            secondBowl = new Bowl(secondScore);
        }
        public int getFrameScore(){
           return (firstBowl.getScore()+secondBowl.getScore());
        }
    } 
    

    4.在游戏类中,你有构造函数,你只传递一帧?一名玩家的一场比赛由 10 帧组成。对帧使用 10 个变量也不是一个好主意。为此,我们有 java 集合。您可以使用列表。

    public class Game {
        private java.uti.List<Frame> frames;
        public Game(List<Frame> frames){
          this.frames = frames;
        }
        public getGameScore(){
          // loop goes here to sum up scores from all frames
          //sum = sum+frames.get(i);
        }
    }
    

    }

    5.如果你假设这个游戏只有一个玩家玩,这个模型也是有效的。对于多个玩家,上面的 Game 类实际上变成了 Player 类,您必须创建新的 Game 类。

    【讨论】:

    • 如果您是 OOP 的初学者,我建议您现在不要参考 Martin Fowler 的重构书。您必须先掌握 OOP。
    【解决方案3】:

    我倾向于删除Bowl 的多个实例。如果您发现自己处于这种情况,请问自己 - 我将如何处理 100 个 Bowl 实例?考虑在游戏生命周期内必须创建和维护的十个 Frame 实例。保留多个实例不是一个好主意,除非您需要多个实例用于某些业务逻辑。

    public class Bowl {
    
        private int bowlScore;
    
        // Use a no-argument constructor
        public Bowl() {
            this.bowlScore = 0;
        }
    
        public void setBowlScore( int score ) {
            this.bowlScore = score;
        }
    
        public int getBowlScore() {
            return this.bowlScore;
        }
    }
    

    对于Frame 类,

    public class Frame {
    
        private int frameScore;
        private Bowl bowlArray[];
    
        public Frame() {
            this.frameScore = 0;
            this.bowlArray = new Bowl[2];
        }
    
        public void setScoreForFirstBowl( int score ) {
            this.bowlArray[0] = score;
            this.frameScore += score;
        }
    
        public void setScoreForSecondBowl( int score ) {
            this.bowlArray[1] = score;
            this.frameScore += score;
        }
    
        public void setFrameScore( int score ) {
            this.frameScore = score;
        }
    
        public int getFrameScore() {
            return this.frameScore;
        }
    
        // this should not be used, left in for completeness
        public Bowl[] getBowlArray() {
            return this.bowlArray;
        }
    }
    

    对于Game

    public class Game {
    
         private int gameScore;
         private ArrayList<Frame> gameFrames;
    
         public Game() {
             this.gameScore = 0;
             this.gameFrames = new ArrayList<Frame>();
         }
    
         /* There are many ways of implementing the game logic. Here is an example.
            You will have to complete the rest :) */
    
         // @frame frame object with bowl data that is appended to list
         public void frameCompleted(Frame frame) {
             this.gameScore += frame.getFrameScore; // I assume this is what you want to do. Change if not
             this.gameFrames.add(frame);
         }
    
         /* The method written above can also be implemented by passing integer values
            for the score, bowl number and frame number. However, this would not be very
            OOP friendly. Essentially, this is 'Encapsulation' of the Frame data into the 
            Frame object. */
    
         // Add getters and setters for gameScore and gameFrames
    }
    

    有关进一步使用的示例,请参阅ArrayList 文档。如果您不想使用它,可以将其替换为数组。我加入它只是为了展示列表功能。

    阅读更多关于Encapsulationherehere的信息。

    您会注意到我在BowlFrame 课程中没有考虑“罢工”。这是因为它是一个需要满足的特殊情况,所以我把它留给你来实现。我的理解是,如果你在第一个碗上投出好球,你就不会得到第二个碗。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-10-01
      • 2012-06-08
      • 2019-09-09
      • 2021-07-18
      • 2021-10-21
      • 2017-11-29
      • 1970-01-01
      相关资源
      最近更新 更多