#java #enums
#java #перечисления
Вопрос:
Я работаю над клиентом, который использует стандартизированный веб-сервис, однако один из поставщиков ведет себя несколько иначе, так что мы должны учитывать это поведение. Для этого мы использовали перечисление:
public enum ServiceProviderType {
FOO, BAR;
public ServiceProviderType checkService(String url) {
if (url.equals("http://www.example.com")) {
return ServiceProviderType.FOO;
} else {
return ServiceProviderType.BAR;
}
}
}
Разница в этом поведении также зависит от того, что мы запрашиваем у службы, например, мы можем запросить слой и хотим, чтобы этот слой был окрашен в красный цвет, но знаем, что службы BAR и FOO представляют значения RGB по-разному. Для этого мы создали другое перечисление, в котором хранятся свойства, которые мы хотим для каждого уровня в сервисе.
public enum LayerServiceProviderType {
FOO("#ff0000"),
BAR("#ff5555");
private String colour;
public ServiceProviderType(String colour) {
this.colour = colour;
}
public String getColour() {
return colour;
}
public ServiceProviderType checkService(String url) {
if (url.equals("http://www.example.com")) {
return ServiceProviderType.FOO
} else {
return ServiceProviderType.BAR;
}
}
}
Это работает нормально, за исключением случаев, когда мы хотим обрабатывать несколько слоев и рассматривать их как производные от одного и того же базового перечисления. По сути, мы хотим обрабатывать Layer1ServiceProviderType .BAR как эквивалент Layer2ServiceProviderType.BAR . Но мы не можем подклассировать перечисления, и даже попытка сделать это, похоже, нарушает все виды принципов звукового дизайна.
Моей первой мыслью было иметь интерфейс, содержащий перечисление:
interface ServiceProvider {
ServiceProviderType {FOO, BAR};
ServiceProviderType getServiceProviderType();
ServiceProvider checkService(String url);
}
public enum LayerServiceProvider implements ServiceProvider {
FOO (ServiceProviderType.FOO, "#ff0000"),
BAR (ServiceProviderType.BAR, "#ff0000");
public LayerServiceProvider(ServiceProviderType serviceProviderType, String colour) {
this.serviceProviderType = serviceProviderType;
this.colour = colour;
}
@Override
public ServiceProviderType getServiceProviderType() {
return this.serviceProviderType;
}
@Override
public ServiceProvider checkService(String url) {
if (url.equals("http://www.example.com")) {
return LayerServiceProviderType.FOO
} else {
return LayerServiceProviderType.BAR;
}
}
}
Но мне кажется чрезмерным иметь перечисление с перечислением, каждое из которых содержит один и тот же диапазон значений. Есть ли лучший способ сделать это?
Комментарии:
1. Есть ли причина, по которой вы не могли просто добавить
colour
ServiceProviderType
?2.
colour
и другие переменные не будут общими для всехServiceProviderType
— переменные будут меняться в соответствии с тем, что мы запрашиваем у поставщика услуг. Общность — это перечисление.3. Мне действительно непонятно, в чем проблема, с которой вы столкнулись. На самом деле это вопрос не столько о перечислениях, сколько о спецификации предметной области и дизайне модели. «Плохо спроектированный API может навсегда разрушить класс» — Дж. Блох. Возможно, перечисление — это элегантный способ (стратегические перечисления — это вещь), но, возможно, нет. Я недостаточно знаком с вашим проектом, чтобы знать, как помочь.
4. @scottb — В некоторых случаях логика разграничения между поставщиками услуг специфична для реализации этих поставщиков услуг — например, в зависимости от уровня, который мы запрашиваем у службы. Однако в некоторых случаях поведение является общим. Шаблон перечисления стратегии решает мою проблему, но делает это путем дублирования значений FOO, BAR внутри вложенного перечисления.
Ответ №1:
Возможно, шаблон посетителя — это то, что вы ищете.
Используемый с перечислением, он в основном позволяет добавлять логику, зависящую от перечисления, без использования switch
операторов.
Пример:
public enum ServiceProviderType {
FOO {
@Override public <T> T apply(Action<T> action) { return action.doFoo(); }
},
BAR {
@Override public <T> T apply(Action<T> action) { return action.doBar(); }
};
public interface Action<T> {
T doFoo();
T doBar();
}
public abstract <T> T apply(Action<T> action);
public static ServiceProviderType checkService(String url) {
if (url.equals("http://www.example.com"))
return FOO;
return BAR;
}
}
public class LayerServiceProviderType implements ServiceProviderType.Action<String> {
@Override
public String doFoo() {
return "#ff0000";
}
@Override
public String doBar() {
return "#ff0000";
}
}
public class Main {
public static void main(String[] args) {
ServiceProviderType type = ServiceProviderType.checkService("");
String colour = type.apply(new LayerServiceProviderType());
}
}
Комментарии:
1. Мне нравится этот шаблон, но мне интересно, как он работает в случае, когда LayerServiceProviderType должен возвращать несколько переменных. Например, случай, когда мне может понадобиться иметь
colour
переменную иname
переменную (и многое другое).2. В этом случае вы можете рассматривать
Layer...
приведенный выше класс как фабричный класс , возвращающий объект с набором методов получения, о которых идет речь. Это не ограничивается только методами получения; это могут быть поведенческие методы, что делает это более похожим на шаблон стратегии , в основном используя «фабрику» для получения «стратегии», зависящей от перечисления.