我刚刚开始学习Java几年的HTML / CSS编码,所以我希望我不会在这里问一个陈旧或愚蠢的问题,但是非常感谢任何解释这个问题的帮助。
我目前正在使用斯坦福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&amp; centre_y产生零值。我通过将程序更改为ConsoleProgram并具有getWidth&amp;来发现这一点。 run()方法中的getHeight行(并在屏幕上打印它们的值),然后生成所需的值,但没有将它们传递给GRect方法(因此仍然无效)。但是,如果我在run()中列出了getWidth / getHeight行,则它们不会为相对定位生成任何值。
我的问题是每个方法应该处理一个任务,并且(尽可能)方法应该在run()方法中定义,然后我如何获得placeGRect()方法的getWidth / getHeight值没有run()方法中的一大块代码。我理解的是不好的做法。
我没有经过任何代码来解决这个问题,我真的需要了解这个原则,以便将来可以编写有效的代码。我更喜欢理解鹦鹉时尚代码复制。
提前感谢您的帮助。
答案 0 :(得分:1)
在您的具体示例中:
您已将centre_x
和centre_y
声明为实例变量。当您的程序首次创建GraphicsHierarchy
的实例时,对象创建的顺序如下:
ClassLoader加载类...静态变量(BOX_WIDTH
,BOX_HEIGHT
)被赋予指定的值;
在堆上为GraphicsHierarchy
的实例分配空间(足以容纳实例变量的空间 - double
centre_x
和double
centre_y
- 包括基类实例变量的空间)
实例变量设置为默认值:centre_x
= 0,centre_y
= 0
调用GraphicsHierarchy
默认构造函数(除了调用基类构造函数之外什么都不做 - GraphicsProgram
)。
基类将经历步骤1-4,当它完成执行时返回GraphicsHiearchy
,它现在在执行任何剩余的构造函数语句之前评估显式实例变量初始值设定项(在默认构造函数的情况下) ,没有)。
(关于此过程的其他参考http://java.dzone.com/articles/java-object-initialization)
说完所有这些后,看来当您的班级GraphicsHierarchy
进入第5步并尝试为centre_x
和centre_y
分配值时,{{1}并且getWidth
依赖尚未就绪(即尚未创建窗口或画布,因此方法返回0)。但是当您在运行中移动分配并且getHeight
/ getWidth
返回值时,这意味着调用run的任何方法都首先经历了必要的窗口创建步骤。
Etienne de Martel的建议很好。它会延迟分配您的中心值,直到需要它们为止。如果您愿意,可以创建一个init方法并在init方法中移动赋值,然后调用init作为运行的第一步
getHeight
这与Martel的建议几乎相同,但如果您稍后发现其他初始化代码需要发生,您可以将它放在同一个地方。
至于制作灵活的代码,您可以考虑将private void init() {
centre_x = getWidth / 2;
centre_y = getHeight * 0.5;
}
public void run() {
init();
placeGRect();
}
重命名为placeGRect
并传入点数组(或者如果您愿意,可以使用Collection)placeGRects
(您可以使用Java.awt.Point或定义自己的Point类)
通过这种方式,您的placeGRects方法得以简化。它不再决定要渲染多少个框(传入的数组)。它也不确定这些新盒子的位置(Point对象的数组也是如此)。它只是循环遍历数组的大小,创建一个新框,添加它,并设置位置。
placeGRects(Point[] points)
您可以将Point数组初始化放在新的init()方法中。
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);
}
}
它使您的代码在需要时更易于理解和修改。
答案 1 :(得分:0)
也许我不明白你的问题,但为什么不将它们作为参数传递?
protected void placeGRect(double centre_x, double centre_y) {
// ...
}
然后你可以这样打电话给placeGRect
:
public void run() {
placeGRect(getWidth() / 2, getHeight() * 0.5);
}
答案 2 :(得分: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);
VS
Loan l1 = Loan.newSubsidizedLoan(15000, 36, f7.2, 2.5);
Loan l2 = Loan.newNormalLoan(15000, 36, f7.2);
请注意,在此示例中,如何将构造函数包装在两个不同的方法中,可以显着改进代码的编写(甚至不需要编写注释);
如果您对编码风格的一般主题感兴趣,请阅读this book。
干杯
L,
答案 3 :(得分:-1)