Я делаю учебник на Java, и я всегда читаю, что я должен стараться не повторяться, и я заметил, что это очень повторяемо; Поэтому, если кто-нибудь может дать мне несколько советов, чтобы сделать его менее повторяющимся или каким-то образом лучше, он будет очень полезен. Спасибо;) (Это не часть учебника, я сделал это просто для удовольствия из-за того, что я изучаю в науке в школе)
Файл Run.java:
package scientificFormula;
public class Run {
public static void main(String[] args) {
Formula formula = new Formula();
formula.compound1 = args[0];
formula.compound2 = args[1];
String theFormula = formula.createFormula();
System.out.println("Molecule: " + args[0] + " " + args[1] + " = "
+ theFormula);
}
}
Файл Formula.java:
package scientificFormula;
import java.util.HashMap;
import java.util.Map;
public class Formula {
String compound1;
String compound2;
static private Map<String, String> map = new HashMap<String, String>();
static private void initiateIons() {
// 1+
map.put("Hydrogen", "H^1+");
map.put("Lithium", "Li^1+");
map.put("Sodium", "Na^1+");
map.put("Potassium", "K^1+");
map.put("Rubidium", "Rb^1+");
// 2+
map.put("Magnesium", "Mg^2+");
map.put("Calcium", "Ca^2+");
map.put("Strontium", "Sr^2+");
// 3+
map.put("Aluminium", "Al^3+");
// 3-
map.put("Nitrogem", "N^-3");
map.put("Phosphorus", "P^-3");
// 2-
map.put("Oxygen", "O^-2");
map.put("Sulfur", "S^-2");
map.put("Selenium", "Se^-2");
// 1-
map.put("Fluorine", "F^-1");
map.put("Chlorine", "Cl^-1");
map.put("Bromine", "Br^-1");
map.put("Iodine", "I^-1");
}
String createFormula() {
initiateIons();
// Example1: Input = Calcium Iodine:
// 2x + -1y = 0
// x = 1 and y = 2
// Output = CaI2
//
// Example2: Input = Sulfur Iodine
// Output = Molecule: Sulfur Iodine = SI2
String symbol1 = map.get(compound1);
String symbol2 = map.get(compound2);
int charge1 = Integer.parseInt(symbol1.replace("+", "").substring(
symbol1.length() - 2));
int charge2 = Integer.parseInt(symbol2.replace("+", "").substring(
symbol2.length() - 2));
String letter1 = null;
String letter2 = null;
if (symbol1.length() == 5) {
letter1 = symbol1.substring(0, 2);
} else if (symbol1.length() == 4) {
letter1 = symbol1.substring(0, 1);
}
if (symbol2.length() == 5) {
letter2 = symbol2.substring(0, 2);
} else if (symbol2.length() == 4) {
letter2 = symbol2.substring(0, 1);
}
int possitive1 = (int) Math.sqrt(charge1 * charge1);
int possitive2 = (int) Math.sqrt(charge2 * charge2);
if ((possitive1 == 1) & (possitive2 == 1)) {
return letter1 + letter2;
} else if (possitive1 == 1) {
return letter1 + possitive2 + letter2;
} else if (possitive2 == 1) {
return letter1 + letter2 + possitive1;
}
if (possitive1 == 0) {
possitive1 = -(charge1);
}
if (possitive2 == 0) {
possitive2 = -(charge2);
}
return letter1 + possitive2 + letter2 + possitive1;
}
}
Я настоятельно рекомендую прочитать чистый код книги, который в основном касается рефакторинга.
Дублирование кода является лишь одной (важной) проблемой при запуске рефакторинга (также называемом DRY - Do not Repeat Yourself). Есть много других принципов, и я попытаюсь описать некоторые из них, которые я считаю наиболее важными:
Одним из важных "эмпирических правил" является принцип SRP: Single Responsibility, в котором говорится, что каждый класс должен иметь только одну ответственность, и если мы применяем ту же идею к методам - каждый метод должен делать только одно! Это может показаться очень строгим, но когда вы начнете его применять, ваш код станет более понятным для чтения и упрощения его использования.
Другой использует значащие имена (классы/методы/переменные):
return letter1 + possitive2 + letter2; // you probably meant positive with one 's' (typo!)
может означать что-то для вас - но это не будет означать много другого читателя - теперь, конечно, вы можете решить это, добавив комментарии к коду, но это исправление проблемы вместо ее решения. Кроме того, комментарии к коду становятся устаревшими - либо становятся неуместными, либо даже хуже - могут ввести читателя в заблуждение, когда код изменится, а комментарий - нет.
И последнее (на данный момент), сохраняйте четкий порядок выполнения, позволяйте взять фрагмент кода, который вы разместили, и улучшить его:
String symbol1 = map.get(compound1);
String symbol2 = map.get(compound2);
int charge1 = Integer.parseInt(symbol1.replace("+", "").substring(
symbol1.length() - 2));
int charge2 = Integer.parseInt(symbol2.replace("+", "").substring(
symbol2.length() - 2));
String letter1 = null;
String letter2 = null;
if (symbol1.length() == 5) {
letter1 = symbol1.substring(0, 2);
} else if (symbol1.length() == 4) {
letter1 = symbol1.substring(0, 1);
}
if (symbol2.length() == 5) {
letter2 = symbol2.substring(0, 2);
} else if (symbol2.length() == 4) {
letter2 = symbol2.substring(0, 1);
}
int possitive1 = (int) Math.sqrt(charge1 * charge1);
int possitive2 = (int) Math.sqrt(charge2 * charge2);
Как видим, положительный зависит от заряда, который зависит от символа, который зависит от соединения. И тотально не связано с ним: буква зависит от символа, который зависит от состава.
пусть разделить его на отдельные методы:
int getPositive(String compound) { // I have no idea what "positive", "symbol" and compound represent, consider better names please
String symbol = map.get(compound);
int charge = Integer.parseInt(symbol.replace("+", "").substring(
symbol.length() - 2));
return (int) Math.sqrt(charge2 * charge2);
}
И теперь мы можем применить то же самое к getLetter(String compound) {...}
и т.д.
Я бы выбрал часть этого разбора в свой класс, возможно, вы даже сможете разгрузить его там, когда вы создадите больше функциональности.
public class Symbol {
final int charge;
final String letter;
public Symbol(String str) {
int sepIndex = str.indexOf('^');
if(sepIndex != -1) {
letter = str.substring(0, sepIndex);
charge = Integer.parseInt(str.substring(sepIndex+1).replace("+", ""));
} else {
throw new IllegalArgumentException(str + " isnt a valid Symbol, no ^ found");
}
}
}
public class Formula {
String compound1;
String compound2;
static private Map<String, String> map = new HashMap<String, String>();
// make this a static block so its only called once.
static {
// 1+
map.put("Hydrogen", "H^1+");
map.put("Lithium", "Li^1+");
map.put("Sodium", "Na^1+");
map.put("Potassium", "K^1+");
map.put("Rubidium", "Rb^1+");
// 2+
map.put("Magnesium", "Mg^2+");
map.put("Calcium", "Ca^2+");
map.put("Strontium", "Sr^2+");
// 3+
map.put("Aluminium", "Al^3+");
// 3-
map.put("Nitrogem", "N^-3");
map.put("Phosphorus", "P^-3");
// 2-
map.put("Oxygen", "O^-2");
map.put("Sulfur", "S^-2");
map.put("Selenium", "Se^-2");
// 1-
map.put("Fluorine", "F^-1");
map.put("Chlorine", "Cl^-1");
map.put("Bromine", "Br^-1");
map.put("Iodine", "I^-1");
}
String createFormula() {
// Example1: Input = Calcium Iodine:
// 2x + -1y = 0
// x = 1 and y = 2
// Output = CaI2
//
// Example2: Input = Sulfur Iodine
// Output = Molecule: Sulfur Iodine = SI2
Symbol symbol1 = new Symbol(map.get(compound1));
Symbol symbol2 = new Symbol(map.get(compound2));
int possitive1 = Math.abs(symbol1.charge); // sqrt(a*a) == abs(a)
int possitive2 = Math.abs(symbol1.charge);
if ((possitive1 == 1) & (possitive2 == 1)) {
return symbol1.letter + symbol1.letter;
} else if (possitive1 == 1) {
return symbol1.letter + possitive2 + symbol2.letter;
} else if (possitive2 == 1) {
return symbol1.letter + symbol2.letter + possitive1;
}
// dead code, if positive1 is 0 then setting it to -0 does nothing
/*if (possitive1 == 0) {
possitive1 = -(symbol1.charge);
}
if (possitive2 == 0) {
possitive2 = -(symbol2.charge);
}*/
return symbol1.letter + possitive2 + symbol2.letter + possitive1;
}
}
Ну, сначала позвольте изменить свой код так, чтобы все... 1 переменные были вместе. Судя по вашему соглашению об именах, вы используете letter1 для вычисления символа1, символа1 для вычисления charge1 и т.д. Я просто собираюсь сосредоточиться на createFormula(), так как часть, которую вы хотите уменьшить по размеру.
String createFormula() {
initiateIons();
//Calculate symbol1, charge1, letter1, possitive1
String symbol1 = map.get(compound1);
int charge1 = Integer.parseInt(symbol1.replace("+", "").substring(
symbol1.length() - 2));
String letter1 = null;
if (symbol1.length() == 5) {
letter1 = symbol1.substring(0, 2);
} else if (symbol1.length() == 4) {
letter1 = symbol1.substring(0, 1);
}
int possitive1 = (int) Math.sqrt(charge1 * charge1);
//calculate symbol2, charge2, letter2, possitive2
String symbol2 = map.get(compound2);
int charge2 = Integer.parseInt(symbol2.replace("+", "").substring(
symbol2.length() - 2));
String letter2 = null;
if (symbol2.length() == 5) {
letter2 = symbol2.substring(0, 2);
} else if (symbol2.length() == 4) {
letter2 = symbol2.substring(0, 1);
}
int possitive2 = (int) Math.sqrt(charge2 * charge2);
//Returns
if ((possitive1 == 1) & (possitive2 == 1)) {
return letter1 + letter2;
} else if (possitive1 == 1) {
return letter1 + possitive2 + letter2;
} else if (possitive2 == 1) {
return letter1 + letter2 + possitive1;
}
if (possitive1 == 0) {
possitive1 = -(charge1);
}
if (possitive2 == 0) {
possitive2 = -(charge2);
}
return letter1 + possitive2 + letter2 + possitive1;
}
Я согласен, шаг вычисления кажется излишним. Было бы замечательно иметь функцию, которая вычисляет те и возвращает кортеж из них, но (насколько я знаю) Java еще не имеет кортежей. Мы можем сделать строковый массив, хотя и проанализировать ints обратно из строк. Здесь менее избыточный пересмотр кода.
String[] calculatePieces(String compound){
String symbol = map.get(compound);
int charge = Integer.parseInt(symbol.replace("+", "").substring(
symbol.length() - 2));
String letter = null;
if (symbol.length() == 5) {
letter = symbol1.substring(0, 2);
} else if (symbol1.length() == 4) {
letter = symbol1.substring(0, 1);
}
int possitive = (int) Math.sqrt(charge * charge);
pieces = new String[4];
pieces[0] = symbol;
pieces[1] = charge + "";
pieces[2] = letter;
pieces[3] = possitive + "";
return pieces;
}
String createFormula() {
initiateIons();
String[] pieces1 = calculatePieces(compound1);
int charge1 = Integer.parseInt(pieces1[1]);
int possitive1 = Integer.parseInt(pieces1[3]);
String[] pieces2 = calculatePieces(compound2);
int charge2 = Integer.parseInt(pieces2[1]);
int possitive2 = Integer.parseInt(pieces2[3]);
//Returns
if ((possitive1 == 1) & (possitive2 == 1)) {
return pieces1[2] + pieces2[2];
} else if (possitive1 == 1) {
return pieces1[2] + possitive2 + pieces2[2];
} else if (possitive2 == 1) {
return pieces1[2] + pieces2[2] + possitive1;
}
if (possitive1 == 0) {
possitive1 = -(charge1);
}
if (possitive2 == 0) {
possitive2 = -(charge2);
}
return pieces1[2] + possitive2 + pieces2[2] + possitive1;
}
Это немного лучше, но ограничение Java при возврате одного объекта ограничивает то, насколько это можно получить. Способ сделать его еще более чистым - сделать объект-оболочку, который в основном действует как кортеж (string, int, string, int), но если вам нужно, чтобы это было быстрым, а не тем, как идти.
Я считаю, что это эквивалентно вашему опубликованному коду -
private static String checkCompound(String symbol) {
if (symbol.length() == 5) {
return symbol.substring(0, 2);
} else if (symbol.length() == 4) {
return symbol.substring(0, 1);
}
return "";
}
затем
String letter1 = checkCompound(symbol1);
String letter2 = checkCompound(symbol2);
if (charge1 > 0) {
if (charge2 > 0) {
return letter1 + letter2;
}
return letter1 + charge2 + letter2;
} else if (charge2 > 0) {
return letter1 + letter2 + charge1;
}
return letter1 + charge2 + letter2 + charge1;
Наконец, это
if (possitive1 == 0) {
possitive1 = -(charge1);
}
был удален, потому что он -0
равен 0
.
int possitive1 = (int) Math.sqrt(charge1 * charge1)
сообщает нам, что possitive1 == обвинение1. Почему? Что такое квадратный корень из 2 * 2
, 3 * 3
, n * n
? Вы ожидали, что это будет абсолютное значение?
Math.sqrt(charge1 * charge1)
?
Первый метод:
getCharge(String symbol){
return Integer.parseInt(symbol.replace("+", "").substring(symbol.length() - 2));
}
Второй метод:
getLetter(String symbol){
if (symbol.length() == 5) {
return symbol.substring(0, 2);
} else if (symbol.length() == 4) {
return symbol.substring(0, 1);
}
}
createFormula()
(немного скрыто - требуется прокрутка)