Это неприятно для меня ... Я парень PHP, работающий на Java над проектом JSP. Я знаю, как делать то, что пытаюсь сделать, используя слишком много кода и полное отсутствие тонкости.
Я бы предпочел сделать это правильно. Вот такая ситуация:
Я пишу небольшой дисплей, чтобы показать клиентам, в какие дни они могут поливать газоны в зависимости от группы полива (ABCDE) и в какое время года. Наши сезоны выглядят так: Лето (с 5-1 по 8-31) Spring (от 3-1 до 4-30) Падение (с 9-1 до 10-31) Зима (с 11-1 по 2-28)
Примером может быть:
Если я нахожусь в группе А, вот мое разрешенное время: Зима: только по понедельникам Spring: вт, чт, сб Лето: в любой день Осень: вт, чт, сб
Если бы я писал это на PHP, я бы использовал такие массивы:
//M=Monday,t=Tuesday,T=Thursday.... etc
$schedule["A"]["Winter"]='M';
$schedule["A"]["Spring"]='tTS';
$schedule["A"]["Summer"]='Any';
$schedule["A"]["Fall"]='tTS';
$schedule["B"]["Winter"]='t';
Я МОГУ сделать массивы дней (массив («вторник», «четверг», «суббота») и т.д., но это не обязательно для того, что я действительно пытаюсь выполнить.
Мне также нужно будет настроить массивы, чтобы определить, в каком я сезоне:
$seasons["Summer"]["start"]=0501;
$seasons["Summer"]["end"]=0801;
Кто-нибудь может предложить действительно крутой способ сделать это? У меня будет сегодняшняя дата и групповое письмо. Мне нужно будет выйти из своей функции в день (M) или несколько дней (tTS), (Any).




Вы можете сделать, по сути, тот же код с Hashtables (или какой-либо другой картой):
Hashtable<String, Hashtable<String, String>> schedule
= new Hashtable<String, Hashtable<String, String>>();
schedule.put("A", new Hashtable<String, String>());
schedule.put("B", new Hashtable<String, String>());
schedule.put("C", new Hashtable<String, String>());
schedule.put("D", new Hashtable<String, String>());
schedule.put("E", new Hashtable<String, String>());
schedule.get("A").put("Winter", "M");
schedule.get("A").put("Spring", "tTS");
// Etc...
Не так элегантно, но опять же, Java не является динамическим языком, и у него нет хэшей на уровне языка.
Примечание: вы могли бы предложить лучшее решение, это просто пришло мне в голову, когда я прочитал ваш вопрос.
Нет красивого решения. Java просто не очень хорошо справляется с подобными вещами. Решение Майка в значительной степени подходит для этого, если вам нужны строки в качестве индексов (ключей). Другой вариант, если настройка хеш-хэшей слишком уродливая, - это сложить строки вместе (без зазрения совести украденные у Майка и модифицированные):
Hashtable<String, String> schedule = new Hashtable<String, String>();
schedule.put("A-Winter", "M");
schedule.put("A-Spring", "tTS");
а затем поиск:
String val = schedule.get(group + "-" + season);
Если вы недовольны общим уродством (и я вас не виню), поместите все это за вызов метода:
String whenCanIWater(String group, Date date) { /* ugliness here */ }
Я не программист на Java, но ухожу от Java и просто думаю в терминах, которые больше не зависят от языка - более чистый способ сделать это может заключаться в использовании констант или перечислимых типов. Это должно работать на любом языке, поддерживающем многомерные массивы.
При использовании именованных констант, где, например:
int A = 0;
int B = 1;
int C = 2;
int D = 3;
int Spring = 0;
int Summer = 1;
int Winter = 2;
int Fall = 3;
...
Тогда константы служат более читаемыми индексами массива:
schedule[A][Winter] = "M";
schedule[A][Spring] = "tTS";
schedule[A][Summer] = "Any";
schedule[A][Fall] = "tTS";
schedule[B][Winter] = "t";
Использование перечислимых типов:
enum groups
{
A = 0,
B = 1,
C = 2,
D = 3
}
enum seasons
{
Spring = 0,
Summer = 1,
Fall = 2,
Winter = 3
}
...
schedule[groups.A][seasons.Winter] = "M";
schedule[groups.A][seasons.Spring] = "tTS";
schedule[groups.A][seasons.Summer] = "Any";
schedule[groups.A][seasons.Fall] = "tTS";
schedule[groups.B][seasons.Winter] = "t";
Не пытайтесь быть таким же динамичным, как PHP. Вы можете попробовать сначала определять, что вам нужно.
interface Season
{
public string getDays();
}
interface User
{
public Season getWinter();
public Season getSpring();
public Season getSummer();
public Season getFall();
}
interface UserMap
{
public User getUser(string name);
}
И, пожалуйста, прочтите документацию к Хеш-таблица перед его использованием. Этот класс синхронизирован, что означает, что каждый вызов защищен от многопоточности, что действительно замедляет доступ, когда вам не нужна дополнительная защита. Пожалуйста, используйте любую реализацию карта вместо HashMap или TreeMap.
+1 Думаю, название интерфейса Пользователь должен переименовать в UserGroup для наглядности.
Кажется, что все пытаются найти способ сделать это в Java, как будто вы делаете это в PHP, а не так, как это должно быть сделано в Java. Просто рассматривайте каждую часть вашего массива как объект или, по крайней мере, первый уровень массива как объект, а каждый подуровень как переменные внутри объекта. Построение структуры данных, которую вы заполняете указанными объектами, и доступ к объектам через предоставленные средства доступа структуры данных.
Что-то вроде:
class Schedule
{
private String group;
private String season;
private String rundays;
public Schedule() { this.group = null; this.season = null; this.rundays= null; }
public void setGroup(String g) { this.group = g; }
public String getGroup() { return this.group; }
...
}
public ArrayList<Schedule> schedules = new ArrayList<Schedule>();
Schedule s = new Schedule();
s.setGroup(...);
...
schedules.add(s);
...
Конечно, это, вероятно, тоже неправильно. Я бы сделал каждый сезон объектом, и, возможно, каждый будний день тоже стал объектом. В любом случае, его легче повторно использовать, понимать и расширять, чем скомканный Hashtable, который пытается имитировать ваш PHP-код. Конечно, в PHP тоже есть объекты, и вы должны использовать их аналогичным образом вместо ваших uber-массивов, где это возможно. Но я понимаю соблазн обмануть. PHP делает это так просто и весело!
Я согласен с тем, что вам определенно стоит заложить эту логику в чистый интерфейс:
public String lookupDays(String group, String date);
но, возможно, вам следует вставить данные в файл свойств. Я не против жесткого кодирования этих данных в исходных файлах, но, как вы заметили, Java может быть довольно многословной, когда дело касается вложенных коллекций. Ваш файл может выглядеть так:
A.Summer=M
A.Spring=tTS
B.Summer=T
Обычно мне не нравится переносить подобные статические данные во внешний файл, потому что это увеличивает «расстояние» между данными и кодом, который их использует. Однако всякий раз, когда вы имеете дело с вложенными коллекциями, особенно с картами, все может стать очень уродливым, очень быстро.
Если вам не нравится эта идея, возможно, вы можете сделать что-то вроде этого:
public class WaterScheduler
{
private static final Map<String, String> GROUP2SEASON = new HashMap<String, String>();
static
{
addEntry("A", "Summer", "M");
addEntry("A", "Spring", "tTS");
addEntry("B", "Summer", "T");
}
private static void addEntry(String group, String season, String value)
{
GROUP2SEASON.put(group + "." + season, value);
}
}
Вы теряете некоторую читабельность, но, по крайней мере, данные ближе к тому месту, где они будут использоваться.
Я совершенно не понимаю, почему некоторые из вас, кажется, думают, что бросание кусков объектов в код - это лучший способ. Например, есть ровно четыре сезона, и в них ничего не делать или хранить. Как можно что-то упростить, чтобы превратить их в объекты? Wing совершенно прав, что это, вероятно, должны быть константы (или, может быть, перечисления).
По сути, Брюсу нужна просто таблица поиска. Ему не нужна иерархия объектов и интерфейсов; ему нужен способ найти расписание на основе сезона и идентификатора группы. Превращение вещей в объекты имеет смысл только в том случае, если у них есть обязанности или состояние. Если у них нет ни того, ни другого, то они просто идентификаторы, и создание для них специальных объектов просто увеличивает кодовую базу.
Вы мог строите, например, объекты Group, каждый из которых содержит набор строк расписания (по одной для каждого сезона), но если все, что делает объект Group, обеспечивает функциональность поиска, то вы заново изобрели таблицу поиска гораздо менее интуитивно. Если ему нужно найти группу, а затем просмотреть расписание, все, что у него есть, - это двухэтапная таблица поиска, код которой требует больше времени, с большей вероятностью будет содержать ошибки и будет труднее поддерживать.
Возможно, лучшим решением было бы поместить все эти данные в базу данных, вместо того, чтобы жестко кодировать их в ваших источниках или использовать файлы свойств.
Использование базы данных будет намного проще поддерживать, и есть механизмы разнообразиеизсвободныйбаза данных на выбор.
Два из этих движков баз данных полностью реализованы на Java и могут быть встроены в приложение, просто включив файл jar. Конечно, это немного тяжеловесно, но оно намного более масштабируемое и простое в обслуживании. Тот факт, что сегодня есть 20 записей, не означает, что их не будет позже из-за изменения требований или расползания функций.
Если через несколько недель или месяцев вы решите добавить, скажем, ограничения на полив по времени суток, будет намного проще добавить эту функцию, если вы уже используете базу данных. Даже если этого никогда не произойдет, значит, вы потратили несколько часов на изучение того, как встраивать базу данных в приложение.
Должна ли «дата» быть параметром? Если вы просто показываете текущий график полива, класс WateringSchedule сам может определить, какой сегодня день и, следовательно, какое сейчас время года. Тогда просто имейте метод, который возвращает карту, где ключ - это групповая буква. Что-то вроде:
public Map<String,List<String>> getGroupToScheduledDaysMap() {
// instantiate a date or whatever to decide what Map to return
}
Затем на странице JSP
<c:forEach var = "day" items = "${scheduler.groupToScheduledDaysMap["A"]}">
${day}
</c:forEach>
Если вам нужно показать расписания для более чем одного сезона, у вас должен быть метод в классе WateringSchedule, который возвращает карту, где Seasons являются ключами, а затем Maps of groupToScheduledDays являются значениями.
Вот как это выглядит мог, остальное вы можете выяснить:
A = new Group();
A.getSeason(Seasons.WINTER).addDay(Days.MONDAY);
A.getSeason(Seasons.SPRING).addDay(Days.TUESDAY).addDay(Days.THURSDAY);
A.getSeason(Seasons.SPRING).addDays(Days.MONDAY, Days.TUESDAY, ...);
schedule = new Schedule();
schedule.addWateringGroup( A );
Я с теми, кто предлагает инкапсулировать функции в объекты.
import java.util.Date;
import java.util.Map;
import java.util.Set;
public class Group {
private String groupName;
private Map<Season, Set<Day>> schedule;
public String getGroupName() {
return groupName;
}
public void setGroupName(String groupName) {
this.groupName = groupName;
}
public Map<Season, Set<Day>> getSchedule() {
return schedule;
}
public void setSchedule(Map<Season, Set<Day>> schedule) {
this.schedule = schedule;
}
public String getScheduleFor(Date date) {
Season now = Season.getSeason(date);
Set<Day> days = schedule.get(now);
return Day.getDaysForDisplay(days);
}
}
Обновлено: Кроме того, ваши диапазоны дат не учитывают високосные годы:
Our seasons look like this: Summer (5-1 to 8-31) Spring (3-1 to 4-30) Fall (9-1 to 10-31) Winter (11-1 to 2-28)
Для повышения производительности используйте HashMap вместо Hashtable. Hashtable, как и Vector, ДЕЙСТВИТЕЛЬНО старый и не должен использоваться по соображениям производительности. В основном потому, что все синхронизировано, хотя этого быть не должно.