Основы грамотности Java на конкретных примерах. Часть I
от DominaN
Всем привет, сегодня я разберу исходник из темы "Исправление исходников" и, кроме непосредственно исправления, постараюсь дать исчерпывающие комментарии почему я делаю эти исправления именно так. Кроме того, я приведу в качестве решения свою версию кода. Итак, изначальный исходник (настоятельно рекомендую ознакомиться, чтобы быть в теме):
Начинаем исправлять, двигаясь сверху вниз.
1. Бессмысленно использовать в данном конкретном случае полновесный объект Boolean, можно обойтись примитивным типом boolean, к тому же это позволит немного сэкономить память. Поэтому исправляем:
Также, переменная str не несет никакой полезной нагрузки, поэтому от нее можно избавиться.
2. Конструктор. При наследовании от Canvas не требуется специально вызывать конструктор суперкласса, поэтому смело можно удалять super() из конструктора. Также, как вы помните, мы избавились от переменной str, нужно вычистить ее из конструктора. Удалим строку str = s; , а строку p = parsing.splitString(str,"\n"); заменим на p = parsing.splitString(s,"\n");
3. Строка Thread thread = new Thread(this); избыточна, хотя и возможна. Однако красивее объединить ее с thread.start(); в конструкцию
чтобы не создавать лишних явных локальных объектов в конструкторе.
4. Метод run(). Строки
являются полной тавтологией, которую нужно удалить. Вообще, сравнение в стиле if (ololo == true) всегда лучше сократить до if (ololo). Кроме того, задержка в 5 мс вместо 20 вполне достаточна, а в catch следует указать точный вид возможного исключения - InterruptedException. В итоге метод стал выглядеть так:
5. Переходим в метод paint(Graphics g). Первое, что бросается в глаза - строка g.setFont(Font.getFont(0, 0, Font.SIZE_SMALL)); ведь у нас уже есть заранее заготовленный в конструкторе объект font. Поэтому заменяем параметр на него:
Далее следует цикл
займемся его оптимизацией.
Первое, что приходит в голову - вынести объявление переменной in за цикл. Однако, от него можно и вообще избавиться, об этом чуть ниже.
Второе, так как в процессе вывода строк их цвет не меняется динамически, то g.setColor(0,0,0); также можно вынести за пределы цикла.
третье, так как строки выводятся в цикле, то лучше заменить банальное сложение на конкатенацию, кроме того, последний параметр (якорь) не нужен, так как все координаты задаются явно, а так же избавимся от ненужной локальной переменной, то есть приводим строку
к виду
Итоговый вид метода:
6. Метод keyPressed(int key). Константы вида Canvas.KEY_NUM2 избыточны, так как наш класс уже является наследником Canvas, поэтому имя суперкласса можно опустить:
к тому же в таком варианте возможна ситуация, когда будут зажаты одновременно и 2 и 8, и текст просто застынет. Чтобы это исключить, следует использовать конструкцию switch вместо условий:
операторы break автоматически не дают выполнится неверным условиям.
7. keyRealised такого метода в Canvas нет вообще, есть keyReleased. Имеет смысл сделать его по аналогии с предыдущим методом, то есть:
8. Метод down() также можно немного оптимизировать. В первую очередь мы видим, что довольно часто используется конструкция fh * p.length. Можно рассчитать это значение вначале метода, также для красоты кода можно заменить строчку id = id - fh; на id -= fh; :
9. Метод up() в целом нормальный, но также можно заменить строку id = id + fh; на id += fh;
10. Комментарий /* end */ банален, его можно удалить.
Итого, получаем следующий код:
Теперь поговорим об оформлении кода. Понятно, что у каждого свои предпочтения и возможности для редактирования исходника, но лично я в свое время писал и на телефоне, и в блокноте, и, в итоге в NetBeans. При написании исходников на телефоне я придерживался определенных правил форматирования - никогда не ставил больше одного переноса подряд, отделял вложенные операторы двумя пробелами вместо табуляции, перед фигурной скобкой ставил пробел. Уже этого достаточно для оформления достаточно красивого, но читабельного на телефоне кода. Смотрим:
Итак, основную идею я показал, в продолжении мы поговорим о логике программы и некоторых трюках, обеспечивающих с одной стороны эффективность, а с другой - красоту кода.
Открыть спойлер
Начинаем исправлять, двигаясь сверху вниз.
1. Бессмысленно использовать в данном конкретном случае полновесный объект Boolean, можно обойтись примитивным типом boolean, к тому же это позволит немного сэкономить память. Поэтому исправляем:
- boolean up;
- boolean down;
2. Конструктор. При наследовании от Canvas не требуется специально вызывать конструктор суперкласса, поэтому смело можно удалять super() из конструктора. Также, как вы помните, мы избавились от переменной str, нужно вычистить ее из конструктора. Удалим строку str = s; , а строку p = parsing.splitString(str,"\n"); заменим на p = parsing.splitString(s,"\n");
3. Строка Thread thread = new Thread(this); избыточна, хотя и возможна. Однако красивее объединить ее с thread.start(); в конструкцию
- new Thread(this).start();
4. Метод run(). Строки
- if (down == false) down = false;
- if (up == false) up = false;
- public void run(){
- while(true){
- if (up) up();
- if (down) down();
- try{
- Thread.sleep(5);
- } catch (InterruptedException e){}
- }
- }
5. Переходим в метод paint(Graphics g). Первое, что бросается в глаза - строка g.setFont(Font.getFont(0, 0, Font.SIZE_SMALL)); ведь у нас уже есть заранее заготовленный в конструкторе объект font. Поэтому заменяем параметр на него:
- g.setFont(font);
- for (int i=0; i< p.length; i++){
- String in = Integer.toString(i);
- g.setColor(0,0,0);
- g.drawString(in + " "+p[i], 0, id + i* fh,20);
- }
Первое, что приходит в голову - вынести объявление переменной in за цикл. Однако, от него можно и вообще избавиться, об этом чуть ниже.
Второе, так как в процессе вывода строк их цвет не меняется динамически, то g.setColor(0,0,0); также можно вынести за пределы цикла.
третье, так как строки выводятся в цикле, то лучше заменить банальное сложение на конкатенацию, кроме того, последний параметр (якорь) не нужен, так как все координаты задаются явно, а так же избавимся от ненужной локальной переменной, то есть приводим строку
- g.drawString(in + " "+p[i], 0, id + i* fh,20);
- g.drawString(Integer.toString(i).concat(" ").concat(p[i]), 0, id + i * fh, 0);
Итоговый вид метода:
- public void paint(Graphics g){
- g.setFont(font);
- g.setColor(255,255,255);
- g.fillRect(0,0,width,height);
- g.setColor(0,0,0);
- for (int i=0; i< p.length; i++) g.drawString(Integer.toString(i).concat(" ").concat(p[i]), 0, id + i * fh, 0);
- repaint();
- }
6. Метод keyPressed(int key). Константы вида Canvas.KEY_NUM2 избыточны, так как наш класс уже является наследником Canvas, поэтому имя суперкласса можно опустить:
- public void keyPressed(int key){
- if (key == KEY_NUM2) up= true;
- if (key == KEY_NUM8) down = true;
- }
- public void keyPressed(int key){
- switch (key) {
- case KEY_NUM2:
- up = true;
- break;
- case KEY_NUM8:
- down = true;
- break;
- }
- }
7. keyRealised такого метода в Canvas нет вообще, есть keyReleased. Имеет смысл сделать его по аналогии с предыдущим методом, то есть:
- public void keyReleased(int key){
- switch (key) {
- case KEY_NUM2:
- up = false;
- break;
- case KEY_NUM8:
- down = false;
- break;
- }
- }
8. Метод down() также можно немного оптимизировать. В первую очередь мы видим, что довольно часто используется конструкция fh * p.length. Можно рассчитать это значение вначале метода, также для красоты кода можно заменить строчку id = id - fh; на id -= fh; :
- public void down(){
- int tmp_height = fh * p.length;
- if (tmp_height > height){
- id -= fh;
- if (height - id> tmp_height) id = height - tmp_height;
- }
- }
9. Метод up() в целом нормальный, но также можно заменить строку id = id + fh; на id += fh;
10. Комментарий /* end */ банален, его можно удалить.
Итого, получаем следующий код:
Открыть спойлер
Теперь поговорим об оформлении кода. Понятно, что у каждого свои предпочтения и возможности для редактирования исходника, но лично я в свое время писал и на телефоне, и в блокноте, и, в итоге в NetBeans. При написании исходников на телефоне я придерживался определенных правил форматирования - никогда не ставил больше одного переноса подряд, отделял вложенные операторы двумя пробелами вместо табуляции, перед фигурной скобкой ставил пробел. Уже этого достаточно для оформления достаточно красивого, но читабельного на телефоне кода. Смотрим:
Открыть спойлер
Итак, основную идею я показал, в продолжении мы поговорим о логике программы и некоторых трюках, обеспечивающих с одной стороны эффективность, а с другой - красоту кода.