juan_gandhi: (Default)
Juan-Carlos Gandhi ([personal profile] juan_gandhi) wrote2007-05-25 05:26 pm

new trick

I wonder how obvious or how stupid or how ubiquitous is the following trick:

Say, I have an enum somewhere outside of my realm:
enum DataType { 
  PERSONAL,  COMMUNITY, PRISON, ARMY;
};

....





and my method takes an instance of that enum, and I really do not like switching based on the enum; of course I can have an
EnumMap
, but the funny trick is that my strategies are based on this enum type, so I can do just this:
enum Strategy { 
  PERSONAL {
    public void process(Entity entity) {...};
  }, 
  COMMUNITY {
    public void process(Entity entity) {...};
  }, 
  PRISON {
    public void process(Entity entity) {...};
  }, 
  ARMY {
    public void process(Entity entity) {...};
  }

  abstract public void process(Entity entity);
  DEFAULT{
    public void process(Entity entity) {...};
  };

  abstract public void process(Entity entity);

(and so on, add functionality here )

  Strategy forDataType(DataType type) {
    Strategy candidate = valueOf(type.name());
    return candidate == null ? DEFAULT : candidate;
  }
};
....


Strategy.forDataType(myType).process(myEntity);




(PURPLE STUFF ADDED LATER)


Is not it a poetry? :)

[identity profile] ygam.livejournal.com 2007-05-26 12:35 am (UTC)(link)
I didn't know Java has enums.

[identity profile] ivan-gandhi.livejournal.com 2007-05-26 12:47 am (UTC)(link)
Wow. Not only it has them, but they are a very efficient tool. Check out Josh Bloch's article (http://java.sun.com/features/2003/05/bloch_qa.html). There was a better one, but I could not find it... and it may be an internal publication anyway.

[identity profile] ygam.livejournal.com 2007-05-26 01:01 am (UTC)(link)
I only used Java 1.5 once in the last 4 1/2 years, and generics was the only new feature I took advantage of.

[identity profile] spamsink.livejournal.com 2007-05-26 01:11 am (UTC)(link)
What's the main difference between a switch and copying the list of enum constituents into another enum?

[identity profile] ivan-gandhi.livejournal.com 2007-05-26 04:52 am (UTC)(link)
You "copy" once; then you dispatch; switch is forever, and you are bound to have a long list of statements, just because.

[identity profile] spamsink.livejournal.com 2007-05-26 05:42 am (UTC)(link)
Suppose someone added a new element into the original enum and has not touched your code. The compile will succeed without any warning in your case, but with a switch there will be one.

[identity profile] ivan-gandhi.livejournal.com 2007-05-26 02:12 pm (UTC)(link)
With the switch, there will be a default case in most cases. With enum, watch it, I'll add a check.

[identity profile] spamsink.livejournal.com 2007-05-26 02:56 pm (UTC)(link)
Я правильно понял, что проверка будет производиться лишь однажды?

[identity profile] spamsink.livejournal.com 2007-05-28 05:06 am (UTC)(link)
Теперь понятно. Да, более "функционально", чем свич, но все равно, на мой взгляд, на уровне синтаксического сахара.

Проверка на идентичность enum-ов, а она существенна, при очередном редактировании потерялась.

[identity profile] spamsink.livejournal.com 2007-05-26 03:10 pm (UTC)(link)
If you have a separate and different action to take for each enum value, you don't need a default. While we're at it, how would you implement a default action here?

[identity profile] ivan-gandhi.livejournal.com 2007-05-28 05:02 am (UTC)(link)
I don't know. It depends. In the case of my app, it is just reporting success (I know it's stupid; I'll fix it later, I just started).

[identity profile] selfmade.livejournal.com 2007-05-26 02:46 am (UTC)(link)
В C# такое было всегда, правда с явным приведением типа:
return (Strategy) Enum.Parse(typeof(Strategy), type.ToString());

[identity profile] ivan-gandhi.livejournal.com 2007-05-26 04:53 am (UTC)(link)
Wait, looks weird. And c# people do it, when they have to map an enum to an enum? How come Java people did not do it?

[identity profile] selfmade.livejournal.com 2007-05-26 02:37 pm (UTC)(link)
Убрать явное приведение из синтаксиса и будет то же самое, что и в твоём примере. Так что и Java люди делают это. Или пример из Java как раз о том, что приведение типов автоматически делается?

[identity profile] pvax.livejournal.com 2007-05-26 07:09 am (UTC)(link)
Err... And you've got to use reflection in Java instead of cast? Well, you can call it "poetry", very slow though.

[identity profile] ivan-gandhi.livejournal.com 2007-05-26 02:17 pm (UTC)(link)
How would you use cast here? (And, again, what's wrong with this kind of reflection?)

[identity profile] http://users.livejournal.com/d_m_/ 2007-05-26 03:15 pm (UTC)(link)
Там не совсем reflection, но поиск элемента по имени, причём путём полного перебора элементов enum #1. В настоящем mapping всё-таки есть хеширование. Но если автору так не нравится Map, почему бы не делать так, как положено, т.е. в каждый элемент enum #2 поместить элемент из enum #1, задавая эту связь в конструкторе? И работает шустрее некуда, и assert не просит :)

[identity profile] ivan-gandhi.livejournal.com 2007-05-26 06:38 pm (UTC)(link)
В каком конструкторе?

Проблема-то в следующем. Нам в метод поступает DataType; надо получить соответствующую стратегию. Ну типа factory вызвать. Ну так эта фабрика-то как работает? А вот я и предложил, как фабрика работает.

[identity profile] http://users.livejournal.com/d_m_/ 2007-05-26 08:04 pm (UTC)(link)
Я имел в виду вот что:
enum Strategy { 
  PERSONAL,  COMMUNITY, PRISON, ARMY;
};

enum DateType {
  
  PERSONAL(Strategy.PERSONAL),  
  COMMUNITY(Strategy.COMMUNITY),  
  PRISON(Strategy.PRISON),  
  ARMY(Strategy.ARMY);  
 
  private Strategy strategy;
  private DataType( Strategy strategy){this.strategy=strategy};

  public static strategyForType(DataType type){return type.strategy};
  public getStrategy(){return strategy};
}


Никаких хаков, никаков поисков перебором, никаких согласований по именам-идентификаторам.

[identity profile] ivan-gandhi.livejournal.com 2007-05-26 10:03 pm (UTC)(link)
Да это очевидное решение если мы хотим добавить зависимость DataType от Strategy. Что очень неестественно.

[identity profile] http://users.livejournal.com/d_m_/ 2007-05-26 10:06 pm (UTC)(link)
Ок, теперь я уже вижу первую фразу поста :)

[identity profile] pvax.livejournal.com 2007-05-26 05:31 pm (UTC)(link)
Not in Java, of course. In C#:

enum DataType { PERSONAL, COMMUNITY, PRISON, ARMY };

enum Strategy { PERSONAL, COMMUNITY, PRISON, ARMY };

Strategy ForDataType(DataType type)
{
return (Strategy)type;
}
I know it's a dirty hack but it works.

[identity profile] http://users.livejournal.com/d_m_/ 2007-05-26 05:48 pm (UTC)(link)
Ничего себе!
И как же в С# работает вот это место:
(Strategy)type
По порядковому номеру элемента? По имени? Этот кастинг описан в спецификациях языка?

[identity profile] pvax.livejournal.com 2007-05-26 06:12 pm (UTC)(link)
Я же говорю - хак. Но документированный.
Запись enum EnumName {...} на самом деле enum EnumName: int {..}. Соответственно, оба enum производные от Int32. Значения нумеруются с 0 и... В общем, дальнейшее понятно.
Хотя я тут подумал... Не совсем я правильно каст написал. Надо бы (Strategy)(int)type писать... Не, проверил - работает.

[identity profile] http://users.livejournal.com/d_m_/ 2007-05-26 08:09 pm (UTC)(link)
Тогда понятно. И хак только в неявном преобразовании к int.

Насколько я помню Delphi, там такой кастинг только через приведение к integer работал (во всяком случае, в ранних версиях), и я злился, почему нельзя напрямую, там-то ведь точно число. :)

Но в Java элемент enum - это полноценный объект, а порядковый номер - лишь одно из его полей.

[identity profile] pvax.livejournal.com 2007-05-27 06:58 am (UTC)(link)
1. (Не)явного преобразования к System.Int32 нет. В IL выполняется инструкция castclass, один раз, а не 2.
2. .Net-овский System.Enum - также полноценный объект (корректнее - value type, т. е. объекты создаются на стеке и от enum нельзя создавать производные типы), с кучей методов, реализующий кучу интерфейсов и т.п.

[identity profile] http://users.livejournal.com/d_m_/ 2007-05-27 10:53 am (UTC)(link)
Вот за такие трюки, документированные хаки, я и не люблю C#.
Зачем потребовалось вводить такой кастинг непосредственно в язык?
Ведь это значит, что, поменяв последовательность элементов в декларации enum, программист рискует получить проблему при приведении типов, в то время как обычно эта операция либо завершается успешно и делает именно то, что ожидали, либо возбуждает runtime exception.

[identity profile] pvax.livejournal.com 2007-05-28 04:13 pm (UTC)(link)
Во-первых, с C# такого приведения типов нет, это все свойства платформы. Во-вторых, в описанном случае при перемене порядка элементов их значения действительно меняются. Если хочется жестко привязать, то можно сделать что-то вроде: enum Blah { None=0, Undefined=1, Unknown=2, Empty=3, Dummy=4, WTF=5 } а потом тасовать, как пожелается. Ну, плюс еще есть фишка удобная, добавить к enum атрибут [Flags] и над ним можно будет логические операции проделывать :)
Вообще говоря, перечисления - настолько низкоуровневая штука, что в прикладной программе их быть не должно. Полиморфизм-то не зря придумывали.

[identity profile] ivan-gandhi.livejournal.com 2007-05-26 06:39 pm (UTC)(link)
Такое ощущение, что главного-то джавного цимеса никто не знает просто. Добавим сейчас.

[identity profile] selfmade.livejournal.com 2007-05-26 11:33 pm (UTC)(link)
В смысле, что в Java в enum можно напихать методов? Да, такого в C# вроде нету.

[identity profile] pvax.livejournal.com 2007-05-27 07:06 am (UTC)(link)
Гм, в Жаве перечисления ссылочные или по значению?
Вся прелесть .Net-овских перечислений в том, что они ссылочные, т.е. очень дешевые с точки зрения памяти и GC. Методов не накидаешь, правда, зато вышеописанный трюк, что в .Net, что в старой Жаве, что без enum-ов, легко реализуется (и именно так, как вы написали), описан у Бека, и называется pluggable selector.

[identity profile] ivan-gandhi.livejournal.com 2007-05-28 05:07 am (UTC)(link)
Я не уверен, что понял вопрос, но, думаю, правильный ответ будет - ссылочные. :) Но имеют и значения, и могут быть перечислены.

[identity profile] pvax.livejournal.com 2007-05-28 04:09 pm (UTC)(link)
Ссылочные типы и типы (передаваемые) по значению - терминология .Net. Объекты ссылочного типа создаются в куче и подлежат сборке мусора. Объекты по значению создаются на стеке и сборке мусора не подлежат. И те, и те - объекты.

[identity profile] caseq.livejournal.com 2007-05-26 09:17 am (UTC)(link)
Надо рассказывать как это красиво делается на темплейтах в C++, или ты в курсе? :)

[identity profile] ivan-gandhi.livejournal.com 2007-05-26 02:14 pm (UTC)(link)
Да, пожалуйста. Я уже давно не сишник.

[identity profile] http://users.livejournal.com/d_m_/ 2007-05-26 09:56 am (UTC)(link)
А можно буквально в двух словах, чему именно нужно восхищаться в этом коде?

[identity profile] ivan-gandhi.livejournal.com 2007-05-26 02:18 pm (UTC)(link)
Краткостью. Есть другие решения?

[identity profile] http://users.livejournal.com/d_m_/ 2007-05-26 02:30 pm (UTC)(link)
А какая задача решалась? :))

[identity profile] http://users.livejournal.com/d_m_/ 2007-05-26 03:04 pm (UTC)(link)
Кстати, а фиолетовый код вчера был?

[identity profile] ivan-gandhi.livejournal.com 2007-05-26 05:31 pm (UTC)(link)
Шас добавил. Но я им недоволен. Поправлю опять.

[identity profile] http://users.livejournal.com/d_m_/ 2007-05-26 05:52 pm (UTC)(link)
Во-первых, я не понимаю, где возникает такая задача - взять два enum'а с элементами, совпадающими по именам, и отобразить один на другой.

Во-вторых, я не понимаю, что "поэтического" в решении этой задачи в лоб.

[identity profile] ivan-gandhi.livejournal.com 2007-05-26 10:07 pm (UTC)(link)
Задача вот какая: в прикладном коде ветвить действия в зависимости от :) перечислимого :) параметра. Варианты: Map, switch. Я добавил новый. Прикладной программе нужно ввести strategy. Ну вот я и предлагаю, раз уж это зависит от другого enum, то и именовать его точно так же.

Ведь это ж вечная проблема - mapping.

[identity profile] http://users.livejournal.com/d_m_/ 2007-05-26 10:38 pm (UTC)(link)
Ок.

Быстродействие, связанное с перебором внутри valueOf(), игнорируем. Понятно, что количество элементов будет измеряется десятками, да и время выполнения стратегии достточно велико, чтобы время выбора стратегии было несущественно.

Но синтаксически не очень красиво смотрится размещение кода внутри enum. Для компактности Strategy.process() может вызывать один из методов personal(), community() и т.п. Но в таком случае, наверное, проще искать нужный метод через рефлексию, обойдясь без лишнего enum и встроив этот поиск прямо в process().
nine_k: A stream of colors expanding from brain (Default)

[personal profile] nine_k 2007-05-26 10:24 am (UTC)(link)
I enjoy seeing mainstream static languages moving to more and more dynamic grounds.

Recently I enjoyed learning about java.lang.reflect.Proxy that allows one to construct classes on the fly.

[identity profile] ivan-gandhi.livejournal.com 2007-05-26 02:19 pm (UTC)(link)
You probably know about Guice (pronounced as juice)?
nine_k: A stream of colors expanding from brain (Default)

[personal profile] nine_k 2007-05-26 05:53 pm (UTC)(link)
Very very little. (Frankly, I did not do any serious Java development since about 2002, and only track things superficially.)

I can imagine a fully static class construction path, either via bytecode assembler or by full-blown java source generation and complilation, and then loading the class file. I could imagine Guice to work this way :)

But to quote project homepage, "Guice cures tight coupling," "Guice is the anti-static." This all sounds pretty to my dynamic-languages-inclined ear :)

[identity profile] mikkim08.livejournal.com 2007-05-26 10:44 am (UTC)(link)
Так вот иногда думаешь, что Джава не такой уж и плохой язык. Просто библиотеки у него плохие

[identity profile] furia-krucha.livejournal.com 2007-05-26 11:09 am (UTC)(link)
Is not it a poetry? :)
На дадаизм похоже, да. :-)

Проблема в том, что если кто-то outside of your realm добавит новое значение в enum DataType, то статическими методами этого обнаружить нельзя, и результатом будет исключение в процессе исполнения.

"Uncaught java.lang.InvalidCast exception in class mil.NORAD.ControlLoop, commencing emergency launch."


[identity profile] ivan-gandhi.livejournal.com 2007-05-26 02:26 pm (UTC)(link)
Так уж и нельзя обнаружить.

[identity profile] furia-krucha.livejournal.com 2007-05-26 02:39 pm (UTC)(link)
Я имел в виду, что этого не обнаруживают статические проверки, производимые компилятором (т.е. системой типизации). Конечно, "вручную" можно доказать корректность этого кода, но только как часто придётся проводить это доказательство (и как узнать, что его нужно повторить?)? Статическая типизация для того и существует, чтобы _гарантировать_ отсутствие некоторого класса ошибок. Отказываясь от неё, вы этот класс возвращаете и должны с ним бороться какими-то другими методами.

[identity profile] ivan-gandhi.livejournal.com 2007-05-26 05:33 pm (UTC)(link)
Это верно. Но как быть, как статически проверить такую вещь? Ведь нужно новые языковые штуки вводить.

[identity profile] furia-krucha.livejournal.com 2007-05-26 05:53 pm (UTC)(link)
Никак. Если используется такая сильная рефлексия, что тип или значение получается по его имени (строке), которая есть результат (в общем случае произвольного) вычисления, то типизация эквивалентна проблеме остановки. Т.е. в тривиальном случае, вроде данного, assert может и поможет, но вообще --- нет.

[identity profile] ivan-gandhi.livejournal.com 2007-05-26 06:40 pm (UTC)(link)
Да дело не в имени. Концептуально они оба зовутся по имени. Но как имплементировать это соответствие? Явно прописать map?

[identity profile] http://users.livejournal.com/d_m_/ 2007-05-26 08:25 pm (UTC)(link)
Насколько я понимаю, Вас очень восхищает то, что элемент enum содержит в качестве одного из полей своё имя, и Вам непременно хочется придумать на этой основе какой-нибудь трюк. Однако, назначение этого поля (IMHO) - реализация toString() и некая сериализация/десериализация, где почему-то не хотят использовать порядковые номера.

[identity profile] ivan-gandhi.livejournal.com 2007-05-26 10:01 pm (UTC)(link)
Блин. Ну умные же люди собрались. На хера бы мне это восхищение наличием.

Вот задача. Есть независимая сущность, данная нам в именах. В принципе, юзеру пофиг, строки это, числа, набор сингтонов. Просто есть набор, он ограничен и неизменен.

Теперь, юзеру нужно запрограммировать действия, зависящие от этих самых сущностей. Если бы функции были законными элементами языка, и если бы нужно было типа по паре функций на сущность, то можно было бы ограничиться двумя таблицами. Скажем, как на джаваскрипте:
var processMap = {
  PERSON:    function(Entity entity) { /* do this and that */ },
  COMMUNITY: function(Entity entity) { /* do that and then this */ },
etc
};



Но в условиях отсутствия нужно что-то придумывать. Окей, Map<DataType, Strategy> - ну и? Ведь по сути-то дела, нужно быть готовым переварить все входные варианты.

Какие идеи?

[identity profile] http://users.livejournal.com/d_m_/ 2007-05-26 10:24 pm (UTC)(link)
> Ведь по сути-то дела, нужно быть готовым переварить все входные варианты.

Обсуждаемый юзер точно знает, какой тип на какую стратегию отображается, ибо он сам эти стратегии и кодирует. Следовательно, речь идёт вовсе не обо "всех входных вариантах", которым несть числа, и где было бы полезно некое автоматизированное нахождение соответствия, как у Вас с совпадающими именами enum.

Что мы вообще обсуждаем - как изящно записать это отображение, используя только синтаксис Java (т.е. конфигурационные файлы не годятся)?


[identity profile] furia-krucha.livejournal.com 2007-05-26 09:16 pm (UTC)(link)
Ну как, добавить в DataType новый метод

Strategy getStrategy(void)

и реализовать его очевидным образом для каждого значения. Потом

data.getStrategy().process(entity);

Всё статически типизировано. Насколько я понимаю, вам нужно решение, которое не меняет DataType ("I have an enum somewhere outside of my realm"), но это по сути значит, что просто нет общего дизайна.

[identity profile] ivan-gandhi.livejournal.com 2007-05-26 09:49 pm (UTC)(link)
Если бы в DataType можно было что-то добавить, проблем было бы меньше. Но этот DataType, скажем, принадлежит java.lang.something.

Ну даже если бы. DataType не должен никакого понятия иметь о своих будущих юскейсах. Мало ли кто где использует; на всяк чих не наздоровкаешься.

[identity profile] furia-krucha.livejournal.com 2007-05-27 08:07 pm (UTC)(link)
Если связь между Data и Strategy слабая, т.е не хочется, чтобы Data от неё зависел, то логично использовать Visitor pattern.

[identity profile] ivan-gandhi.livejournal.com 2007-05-28 05:10 am (UTC)(link)
Вот это очень реальная, правильная, хорошая и здоровая альтератива. Не всегда она лучше, чем маппинг, но не всегда и хуже. Надо, наверное, на эту тему подробно что-то написать.