Методы и разложение - PullRequest
1 голос
/ 05 мая 2011

Я только начинаю изучать Java после нескольких лет HTML / CSS-кодирования, так что, надеюсь, я не задаю здесь старый или глупый вопрос, но любая помощь, объясняющая эту проблему, будет очень признательна.

В настоящее время я работаю с онлайн-материалом Stanford 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 и добавив строки getWidth & getHeight внутри метода run () (и напечатав их значения на экране), который затем выдал необходимые значения, но не прошелих метод GRect (так что до сих пор не работает). Однако, если я чave строки getWidth / getHeight перечислены в списке из run (), тогда они не дают никаких значений для относительного позиционирования.

У меня вопрос, что каждый метод должен обрабатывать одну задачу и (насколько это возможно) методыдолжен быть определен из метода run (), тогда как я могу получить значения getWidth / getHeight в метод placeGRect (), не имея одного большого блока кода в методе run ().Я понимаю, что это плохая практика.

Мне не нужен какой-либо код для решения этой проблемы, мне действительно нужно понимать принципы этого, чтобы я мог написать эффективный код в будущем.Я предпочитаю понимание, чтобы копировать код мода попугай.

Заранее благодарим за любую помощь.

Ответы [ 4 ]

1 голос
/ 05 мая 2011

В вашем конкретном примере:

Вы объявили centre_x и centre_y в качестве переменных экземпляра. Когда ваша программа впервые создает экземпляр GraphicsHierarchy, порядок создания объекта таков:

  1. ClassLoader загружает класс ... статическим переменным (BOX_WIDTH, BOX_HEIGHT) назначаются заданные значения;

  2. Место выделено в куче для экземпляра GraphicsHierarchy (достаточно места для хранения переменных экземпляра - double для centre_x и double для 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_x и centre_y, подсистема, на которую полагаются getWidth и getHeight, будет не готов (т.е. окно или холст еще не были созданы, поэтому методы возвращают 0). Но когда вы переместили свои назначения в run и getWidth / getHeight вернули значения, это означало бы, что метод, вызывающий run, сначала прошел необходимые этапы создания окна.

Предложение Этьена де Мартеля в порядке. Это задерживает назначение значений вашего центра до того момента, когда они понадобятся Если вы хотите, вы можете создать метод init и переместить назначения внутри метода init, а затем вызвать init в качестве первого шага выполнения

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

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

Это почти то же самое, что и предложение Мартела, хотя, если вы обнаружите, что позже у вас есть другой код инициализации, который должен произойти, вы можете выбросить его в том же месте.

Что касается создания гибкого кода, вы можете подумать о переименовании placeGRect в placeGRects и передаче в массив точек (или 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)}; 
}

Это облегчает понимание и изменение вашего кода при необходимости.

0 голосов
/ 05 мая 2011

Очень хороший вопрос! Как составить ваши методы - это вопрос интуиции, а не строгих правил.

Конечно, методы должны быть направлены на то, чтобы делать одно и только одно. Во-первых, наличие коротких методов (даже однострочных!) Улучшает понятность кода. В качестве очень грубого примера подумайте об этом:

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

Обратите внимание, что в этом примере обтекание конструкторов двумя различными методами значительно улучшает документацию кода (даже без необходимости писать комментарии);

Если вас интересует общая тема стиля кодирования, вам следует прочитать эту книгу.

Приветствия

* 1 028 * Л.
0 голосов
/ 05 мая 2011

Ваш код не включает методы 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

Давай, успокойся на своей виртуальной машине! Ваш класс использует всю нагрузку статической информации, поэтому нет необходимости в таком большом количестве вычислений. Но наличие BOX_WIDTH и BOX_HEIGHT совершенно бесполезно в качестве констант, так как они используются только внутри и только в одном месте. Вычисление centre_x и centre_y из BOX_WIDTH и BOX_HEIGHT также бесполезно, поскольку, поскольку они являются окончательными, вы можете легко выполнить вычисления самостоятельно и сократить ненужное создание переменных.

Кроме того, вы нигде не используете значение centre_y, поэтому вам следует отказаться от него.

Чтобы добавить несколько полезных советов, приличная IDE, такая как NetBeans, Eclipse или IntellIJIDEA, должна иметь завершение кода и подсветку синтаксиса и поможет вам стать лучшим (или более знающим, что еще лучше) программистом.

0 голосов
/ 05 мая 2011

Возможно, я не понимаю ваш вопрос, но почему бы не передать их в качестве параметров?

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

Затем вы можете вызвать placeGRect примерно так:

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