2011-05-05 85 views
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; 
     } 
    } 
} 
} 

然而,这并不因工作产生零值的centre_x & centre_y。我通过将程序更改为ConsoleProgram,并在run()方法中使用getHeight行(并在屏幕上打印它们的值),发现了这一点,然后生成所需的值但未将它们传递给GRect方法(如此仍然没有工作)。但是,如果我将getWidth/getHeight行列出run(),那么它们不会为相对定位生成任何值。我的问题是,每个方法都应该处理一个任务,并且(尽可能)方法应该从run()方法中定义出来,然后我怎么才能将getWidth/getHeight值传递给placeGRect()方法在run()方法中没有一大块代码。我理解的是不好的做法。

我没有任何代码解决这个问题,我真的需要了解这个原理,所以我可以在将来编写有效的代码。我更喜欢理解鹦鹉式的代码复制。

在此先感谢您的帮助。

+1

您的课程名称和作业编号对大多数人来说都是无用的;为什么不给它一些有用的相关标题? – 2011-05-05 14:21:39

+0

如果'getWidth()'和'getHeight'与'getGRect()'在同一个对象上,那么为什么你需要*来传递它们呢? – Jeremy 2011-05-05 14:28:11

+1

道歉,在我将标题改为更有用的东西之前,我按了提交问题。 – 2011-05-05 14:52:15

回答

1

在你的具体的例子:

您已经声明centre_xcentre_y实例变量。当你的程序首先创建的GraphicsHierarchy一个实例对象创建的顺序是这样:

  1. 类加载器加载的类...静态变量(BOX_WIDTHBOX_HEIGHT)被分配指定的值;

  2. 空间在堆上分配用于GraphicsHierarchy一个实例(足够的空间容纳实例变量 - 一个doublecentre_xcentre_y一个double - 包括用于基类的实例变量空间)

  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_xcentre_y,该子系统getWidthgetHeight依赖没有准备好(即一个窗口或画布尚未创建,所以方法返回0)。但是,当您将内部任务移动到内部并返回值时,这意味着无论调用什么方法,run run首先会经历必要的窗口创建步骤。

Etienne de Martel的建议很好。它会延迟您的中心值的分配,直到需要之前。如果您愿意,您可以创建一个init方法和移动init方法里面的任务,然后调用初始化为运行的第一步

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

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

这是几乎同样的事情,马特尔的建议,但如果你后来发现你有其他的初始化代码需要发生,你可以把它放在同一个地方。

至于起草(如果你喜欢或集合),你可能想重命名placeGRectplaceGRects并通过以点为单位的阵列灵活的代码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)}; 
} 

它使您的代码在需要时更易于理解和修改。

+0

虽然在这个特定的例子中,你甚至不需要传入boxPoints作为参数,因为它是一个成员变量。 :)在尝试访问它的length属性之前,placeGRects应该可能检查点不是null。 – 2011-05-05 17:51:11

+1

非常感谢你。这只是我正在寻找的那种答案,现在让我更好地理解了我所需要的。我现在对它失败的原因有了更清晰的了解。谢谢 – 2011-05-05 20:37:57

+0

@Steve - 快乐我可以帮忙! :) – 2011-05-05 21:30:11

0

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

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

你可以调用placeGRect像这样:

public void run() { 
    placeGRect(getWidth()/2, getHeight() * 0.5); 
} 
+0

我无法将它们作为参数传递的原因是每个GRect对象相对于中心都有不同的位置。所以我需要定义中心,然后定位每个GRect(使用for循环)。 – 2011-05-05 14:53:55

+0

@Steve我还是不明白你的问题到底是什么。 – 2011-05-05 15:15:45

+0

我觉得我缺乏知识并没有帮助我在这里明确。好的,让我试着解释一下。如果行: double centre_x = getWidth()/ 2; double centre_y = getHeight()* 0.5; 在run()方法中,那么中心坐标是可用的,因为“屏幕”对象是活的并且在使用中。但是这些坐标不能传递到开关柜内的“centre_x”和“centre_y”。如何确定中心点,然后将这些值传递给“centre_x”和“centre_y”,一旦用placeGRect调用它们即可。 – 2011-05-05 15:25:28

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()现在(显著改善你不知道代码的作用。

下面是另一个例子,这次有做与构造对象:

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); 

注意,在这个例子中是如何在两种不同的方法包装的构造显著提高了文档的代码(甚至不需要写评论);

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

干杯

L.

+0

感谢您的书的建议和答案。然而,我仍然坚持如何进行整洁的方法构建/分解,并且能够在run()方法中没有大量代码的情况下解决问题。我只是真的肛门,我应该让它工作吗? – 2011-05-05 15:35:27

+0

从我所了解的问题中,您更有兴趣编写“干净的代码”。请记住,这个主题涉及的不仅仅是如何编写你的方法,这需要花费时间和经验来开发(不管怎样,你不会只知道表单看一些答案)。我真的建议开始阅读这本书。顺便说一句,不,我不认为你是肛门,而不是在正确的道路上。 – 2011-05-05 16:25:08

+0

@Steve如果您可以将所有init代码移出运行,并移入对象构造函数,那将是理想的,但不幸的是这并非总是可行。在你的特定情况下,在运行被调用之前,一些所需的信息不可用。尽管如此,仍然可以避免使用单一代码,尽可能将逻辑分为不同的单元。 – 2011-05-05 17:26:10

-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定义为静态变量,您始终可以找到centre_x和centre_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 

来吧,有它容易对你的V irtual机器!你的课程使用了整个静态信息,所以不需要太多的计算。但是有一个BOX_WIDTH和BOX_HEIGHT作为常量是完全没有用的,因为它们只在内部使用,只能在一个地方使用。从BOX_WIDTH和BOX_HEIGHT中计算centre_x和centre_y也是无用的,因为它们是最终的,您可以轻松地自行完成计算并减少不必要的变量创建。

另外,你不要在任何地方使用centre_y值,所以你应该抛弃它。

为了进一步添加一些有用的建议,象NetBeans,Eclipse或IntellIJIDEA这样体面的IDE应该有代码完成和语法高亮显示,并且将帮助你成为一个更好的(或更知名的,甚至更好的)程序员。

+0

getWidth(),getHeight()来自基类。它们是窗口/画布的宽度和高度。它们与OP试图呈现的Boxes的大小不同。它们不是常量,这会使您的所有建议无效。 – 2011-05-05 16:58:27

+0

@新萨尔谢谢你。我认为这是我昏厥。 – 2011-05-05 20:40:20