1

经过几年的 HTML/CSS 编码后,我才刚刚开始学习 Java,所以希望我不会在这里问一个老旧或愚蠢的问题,但是任何解释这个问题的帮助将不胜感激。

我目前正在通过斯坦福 CS106A 在线材料工作,我已经达到第 6 周,作业 2,问题 3 (http://see.stanford.edu/materials/icspmcs106a/13-assignment-2-simple-java.pdf )。

如您所见,它需要在屏幕上放置各种对象来创建图形层次结构,如所述。我的计划是使用中心坐标将所有对象相对放置在屏幕上。但是我遇到了一个我似乎无法找到答案的问题。该课程描述了方法分解应该如何允许每个方法处理一个问题(我相信单一责任原则),所以我编写了我的代码的第一部分:

//Import any libraries
import acm.program.*;
import acm.graphics.*;

    public class GraphicsHierarchy extends GraphicsProgram {

//Define constants
static final int BOX_WIDTH = 200;
static final int BOX_HEIGHT = 75;



public void run() {
    placeGRect();
}   

//Find centre x & y
double centre_x = getWidth() / 2; //check this
double centre_y = getHeight() * 0.5;//and this

//placeGRect method
public void placeGRect() {
    for (int count = 0; count < 4; count++) {
        GRect box = new GRect (BOX_WIDTH, BOX_HEIGHT);
        add(box);
        switch (count) {
        case 0:
            box.setLocation(centre_x, 75);
            break;
        case 1:
            box.setLocation((centre_x * 0.5), 250);
            break;
        case 2:
            box.setLocation(centre_x, 250);
            break;
        case 3:
            box.setLocation((centre_x * 1.5), 250);
            break;
        }
    }
}
}

但是,由于 center_x 和 center_y 产生零值,这不起作用。我通过将程序更改为 ConsoleProgram 并在 run() 方法中使用 getWidth 和 getHeight 行(并在屏幕上打印它们的值)发现了这一点,然后生成了所需的值但没有将它们传递给 GRect 方法(所以仍然没有工作)。但是,如果我在 run() 之外列出了 getWidth/getHeight 行,那么它们不会产生任何相对定位值。

我的问题是每个方法都应该处理一个任务,并且(尽可能)方法应该在 run() 方法之外定义,那么我怎样才能将 getWidth/getHeight 值传递给 placeGRect() 方法而没有一个run() 方法中的大块代码。我理解这是不好的做法。

我不追求任何代码来解决这个问题,我真的需要了解它的原理,这样我才能在将来编写有效的代码。我更喜欢理解鹦鹉时尚代码复制。

提前感谢您的帮助。

4

4 回答 4

1

在您的具体示例中:

您已将centre_x和声明centre_y为实例变量。当你的程序第一次创建实例时GraphicsHierarchy,对象创建的顺序是这样的:

  1. ClassLoader 加载类...静态变量 ( BOX_WIDTH, BOX_HEIGHT) 被赋予指定的值;

  2. 在堆上为一个实例分配GraphicsHierarchy空间(足够的空间来保存实例变量 - 一个doubleforcentre_x和一个doublefor centre_y- 包括基类实例变量的空间)

  3. 实例变量设置为默认值:centre_x= 0,centre_y= 0

  4. 调用默认构造函数(GraphicsHierarchy除了调用基类构造函数 - 之外什么都不做GraphicsProgram)。

  5. 基类将执行步骤 1-4,当它完成执行返回到GraphicsHiearchy执行任何剩余的构造函数语句(在默认构造函数的情况下,没有)之前评估显式实例变量初始化器。

(有关此过程的其他参考http://java.dzone.com/articles/java-object-initialization

说了这么多,看起来当您的类GraphicsHierarchy进入第 5 步并尝试将值分配给centre_xand时, and所依赖centre_y的子系统尚未准备好(即尚未创建窗口或画布,因此方法返回0)。但是,当您在运行和/返回值中移动分配时,这意味着调用运行的任何方法都首先经过了必要的窗口创建步骤。getWidthgetHeightgetWidthgetHeight

Etienne de Martel 的建议很好。它将您的中心值的分配延迟到需要它们之前。如果您愿意,您可以创建一个 init 方法并在 init 方法中移动分配,然后调用 init 作为运行的第一步

private void init() {
    centre_x = getWidth / 2;
    centre_y = getHeight * 0.5;
}

public void run() {
    init();
    placeGRect();
}

这与 Martel 的建议几乎相同,尽管如果您稍后发现您还有其他需要执行的初始化代码,您可以将它放在同一个地方。

至于制作灵活的代码,您可能会考虑重命名placeGRectplaceGRects传入点数组(或 Collection,如果您愿意)placeGRects(Point[] points)

(您可以使用 Java.awt.Point 或定义您自己的 Point 类)

这样,您的 placeGRects 方法就被简化了。它不再决定要渲染多少个框(传入的数组会)。它也不能确定这些新盒子的位置(同样是 Point 对象的数组)。它只是循环遍历数组的大小,创建一个新框,添加它并设置位置。

private Point[] boxPoints;

public void run() {
    init();
    placeGRects(boxPoints);
}

public void placeGRects(Point[] points) {
    for(int i=0;i<points.length;i++) {
        GRect b = new GRect(BOX_WIDTH,BOX_HEIGHT); 
        add(b);
        b.setLocation(points[i].x,points[i].y);
    }
}

您可以将 Point 数组初始化放在新的 init() 方法中。

private void init() {
    centre_x = getWidth / 2;
    centre_y = getHeight * 0.5;
    boxPoints = {new Point(centre_x, 75),new Point(centre_x * 0.5, 250)}; 
}

It makes your code easier to understand and modify when needed.

于 2011-05-05T16:54:23.687 回答
0

也许我不明白你的问题,但为什么不将它们作为参数传递呢?

protected void placeGRect(double centre_x, double centre_y) {
    // ...
}

然后你可以这样调用placeGRect

public void run() {
    placeGRect(getWidth() / 2, getHeight() * 0.5);
}
于 2011-05-05T14:25:46.807 回答
0

非常好的问题!如何组合你的方法是一个直觉问题,而不是严格的指导。

当然,方法应该专注于做一件事,而且只做一件事。首先,使用简短的方法(甚至是单行代码!)提高了代码的可理解性。作为一个非常粗略的例子,想想这个:

if (DateUtils.before(ticket.getExpirationDate(), new Date())) {
   accept(ticket);
}

然后这个

if (isNotExpired(ticket)) {
   accept(ticket);
}

...

private boolean isNotExpired(Ticket t) {
   return DateUtils.before(t.getExpirationDate(), now());
}

private Date now() {
  return (new Date());
}

注意单行方法isNotExpired()now()的引入如何显着改善了您对代码功能的理解。

这是另一个示例,这一次与构造对象有关:

Loan l1 = new Loan(15000, 36, f7.2, 2.5);
Loan l2 = new Loan(15000, 36, f7.2);

对比

Loan l1 = Loan.newSubsidizedLoan(15000, 36, f7.2, 2.5);
Loan l2 = Loan.newNormalLoan(15000, 36, f7.2);

请注意,在此示例中,将构造函数包装在两种不同的方法中如何显着改进了代码文档(甚至不需要编写注释);

如果您对编码风格的一般主题感兴趣,您应该阅读本书。

干杯

L.

于 2011-05-05T15:19:01.030 回答
-1

您的代码似乎不包含 getWidth() 和 getHeight() 方法。此外,以下代码作为放置是完全错误的,应该放在构造函数中:

double centre_x = getWidth() / 2; //check this
double centre_y = getHeight() * 0.5;//and this

应该成为

private double centre_x;
private double centre_y; 
GraphicsHierarchy(){
    centre_x = GraphicsHierarchy.BOX_WIDTH / 2;
    centre_y = GraphicsHierarchy.BOX_HEIGHT * 0.5;
}

这段代码至少可以编译,但考虑下面描述的解决方案,它会更好。

考虑到您已将 BOX_WIDTH 和 BOX_HEIGHT 定义为静态变量,您总是可以找到 center_x 和 center_y。因此,您甚至不需要定义 BOX_WIDTH 和 BOX_HEIGHT

你可以像这样定义你的类:

//Import any libraries
import acm.program.*;
import acm.graphics.*;

public class GraphicsHierarchy extends GraphicsProgram {
public void run() {
    placeGRect();
}   
//Define constants
public static final double CENTRE_X= 100.00; 
public static final double CENTRE_Y = 37.50;
//placeGRect method
public void placeGRect() {
    for (int count = 0; count < 4; count++) {
        GRect box = new GRect (200, 75);
        add(box);
        switch (count) {
        case 0:
            box.setLocation(GraphicsHierarchy.CENTRE_X, 75);
            break;
        case 1:
            box.setLocation((GraphicsHierarchy.CENTRE_X * 0.5), 250);
            break;
        case 2:
            box.setLocation(GraphicsHierarchy.CENTRE_X, 250);
            break;
        case 3:
            box.setLocation((GraphicsHierarchy.CENTRE_X * 1.5), 250);
            break;
        }
    }
}
}

在我看来,您可以通过消除所有计算并替换这些东西来走得更远

GraphicsHierarchy.CENTRE_X * 1.5 

150

来吧,在你的虚拟机上轻松一点!你的类使用了大量的静态信息,所以不需要这么多的计算。但是拥有 BOX_WIDTH 和 BOX_HEIGHT 作为常量是完全没有用的,因为它们只在内部使用并且只在一个地方使用。从 BOX_WIDTH 和 BOX_HEIGHT 计算 center_x 和 center_y 也是没有用的,因为它们是最终的,您可以轻松地自己进行计算并减少不必要的变量创建。

此外,您不会在任何地方使用 center_y 值,因此您应该放弃它。

为了进一步添加一些有用的建议,像 NetBeans、Eclipse 或 IntellIJIDEA 这样的体面的 IDE 应该具有代码完成和语法突出显示功能,并且将极大地帮助您成为一个更好(或更知识渊博,甚至更好)的程序员。

于 2011-05-05T15:48:01.103 回答