2010-05-10 2 views
27

В Java я хотел бы иметь возможность определять интерфейсы маркеров, которые принудительно реализуют статические методы. Например, для простого текста сериализации/десериализации Я хотел бы быть в состоянии определить интерфейс, который выглядел примерно так:Альтернативы статическим методам на интерфейсах для обеспечения согласованности

public interface TextTransformable<T>{ 

    public static T fromText(String text); 

    public String toText(); 

Поскольку интерфейсы в Java не может содержать статические методы, хотя (как отмечено в ряд других сообщений/потоков: here, here и here этот код не работает

То, что я ищу, однако, некоторые разумные парадигмы, чтобы выразить то же самое намерение, а именно симметричные методы, один из которых является статическим. , и применяется компилятором. В настоящее время лучшее, что мы можем придумать, - это какой-то статический заводский объект или универсальный завод, ни один из которых не является действительно удовлетворительным Тори.

Примечание: в нашем случае наш основной прецедент - у нас есть много, много типов «значение-объект» - перечисления или другие объекты, которые имеют ограниченное число значений, как правило, не имеют состояния за пределами их значения, а какие мы анализируем/удаляем тысячи раз в секунду, поэтому на самом деле заботимся о повторном использовании экземпляров (например, Float, Integer и т. д.) и его влиянии на потребление памяти/gc

Любые мысли?

EDIT1: Чтобы прояснить некоторую путаницу, - у нас есть много, разных объектов, посаженных эта модель -really мы пытаемся придумать нечто элегантное звонящие с 2 семантикой:

  • Интерфейсов как контракты - единство доступа (например, TextTransformable как возможность)
  • Требуя реализации подклассы/реализации (например, заставить их выполнять свои собственные преобразования

в тер мс наших мыслей о Flyweight, Factories - они оба варианта, которые мы рассмотрели, действительно мы пытаемся понять, можем ли мы найти что-то более элегантное, чем полагаться на JavaDoc, говорящий «реализовать вызовы Factory и делегировать на него или выставить его в положение XXX по соглашению «

ответ

6

Это действительно подходит для Flyweight. Это в основном то, что вы пытаетесь выполнить со статикой. Что касается того, как обслуживать объект Flyweight, чтобы вы не создавали тысячи из них, вот некоторые идеи.

Один из них - это завод, на котором вы заявляете, что считаете, что думаете и отклонили, хотя вы не указали, почему (так что любые другие идеи могут пострадать от одной и той же проблемы), поэтому я не буду вдаваться в это.

Другим является то, что тип значения имеет метод, который может служить его преобразователю. Что-то вроде этого:

public class ValueType { 
     public static final TextTransformable<ValueType> CONVERT = .... 
} 

И затем использовать его как это:

ValueType value = ValueType.CONVERT.fromText(text); 

String text = ValueType.CONVERT.toText(value); 

Теперь, не навязывает, что все ValueType Обеспечим свои конвертеры через тот же механизм, для этого я думаю, что вам нужен завод .

Edit: Я предполагаю, что я не знаю, что вы нашли безвкусный о заводе, но я думаю, что вы сосредоточены на звонящих, так как это чувство к вам:

ValueType value = getTransformer(ValueType.class).fromText(text); 

выше можно сделать со статическим импорта завода и метод, который имеет подпись, как так:

public static <T> TextTransformable<T> getTransformer(Class<T> type) { 
     ... 
    } 

код, чтобы найти правильный трансформатор не обязательно красивая, но с позвонившим перспективном все красиво представлены.

Редактировать 2: Размышляя об этом дальше, я вижу, что вы хотите контролировать строительство объекта. Вы не можете этого сделать.Другими словами, в Java вы не можете заставить исполнителя использовать или не использовать фабрику для создания своего объекта. Они всегда могут выставлять публичный конструктор. Я думаю, что ваша проблема в том, что вас не устраивают механизмы принудительного строительства. Если это понимание правильное, тогда может использоваться следующий шаблон.

Вы создаете объект только с частными конструкторами, который обертывает ваш тип значения. Объект может иметь параметр общего типа, чтобы узнать, какой тип значения он обертывает. Этот объект создается с помощью статического заводского метода, который использует заводский интерфейс для создания «реального» объекта значения. Весь код рамки, который использует объект, принимает этот объект только как параметр. Он не принимает тип значения напрямую, и этот объект не может быть создан без фабрики для типа значения.

Проблема с этим подходом заключается в том, что он довольно ограничивает. Существует только один способ создания объектов (поддерживаемых заводским интерфейсом), и существует ограниченная способность использовать объекты значений, так как обработка этих текстовых элементов имеет ограниченное взаимодействие только через этот объект.

Я думаю, они говорят, что нет проблем с программным обеспечением, которые не могут быть решены с помощью дополнительного слоя косвенности, но это может быть мостом слишком далеко. По крайней мере, его пища для размышлений.

+0

Да, я отредактирую выше с запиской о том, что мы говорили о мухи - действительно, хотя я думаю, что мы сосредоточены на единство доступа (например, интерфейс маркера и принудительный контракт по подклассу) - ни один из которых не имеет фабричного или статического трансформатора) Основная причина, по которой я думаю, что мы недовольны фабричной идеей чувствует себя неэлегантным, хотя, по общему признанию, иногда мы терпим неэлегантность, когда мы не можем придумать что-то элегантное ... – jayshao

+0

Да, ироническое ограничение - это то, что мы хотим;) Идеально больше вникания, но без использования соавтора объект - хотя я согласен, ваше резюме похоже на наше текущее мышление и, вероятно, так хорошо, как мы сможем сейчас придумать. – jayshao

1

Если вы работаете на Java 5 или выше, вы можете использовать тип перечисления - все они по определению являются одноточиями. Таким образом, вы могли бы что-то вроде:

public enum MyEnumType { 
    Type1, 
    Type2, 
    //.... 
    TypeN; 

    //you can do whatever you want down here 
    //including implementing interfaces that the enum conforms to. 

} 

Таким образом, проблема с памятью исчезает, и вы можете иметь отдельные экземпляры поведения.


Edit: Если у вас нет доступа к перечислениям (1.4 или выше) или по какой-то другой причине, они не работают для вас, я бы рекомендовал Flyweight pattern реализацию.

+0

Ниже вы можете узнать, почему мы пытаемся выработать соглашение, выходящее за рамки возможностей, встроенных в поддержку enum в Java 5+ – jayshao

+0

@jayshao. У нас была аналогичная проблема со значением и работала вокруг него. Но я думаю, что мухи-паттерн с реестром одного синтаксиса будет работать вокруг значительной части этой проблемы для вас, как поясняется @Yishai – aperkins

+0

@aperkins любой пример кода? – jayshao

4

Просто идея рассмотреть следующие вопросы: Вы можете отделить логику преобразования от самих объектов, а затем у вас есть фиксированный набор трансформаторов, реализует следующий интерфейс:

public interface Transformer<T>{ 

    public T fromText(String text); 

    public String toText(T obj); 
} 

Ваши фактические классы данных могут иметь метод getTransformer(), который возвращает для них правильный трансформатор.

+1

Мать сгуститель! Ты подтолкнул меня на это!:) – les2

+0

Мы надеемся сохранить код совместно - это в основном просто объекты ценности, поэтому идеально это большая часть того, что они делают – jayshao

0

Как сказал @aperkins, вы должны использовать перечисления.

Базовый класс enum, Enum, предоставляет метод valueOf, который преобразует строку в экземпляр.

enum MyEnum { A, B, C; } 
// Here's your toText 
String strVal = MyEnum.A.getName(); 
// and here's your fromText 
MyEnum value = MyEnum.valueOf(MyEnum.class, strVal); 

Обновление: Для тех, которые перечислений, это может делать то, что вам нужно. Он использует отражение, поэтому вам нужно только реализовать EnumHelper для перечислений, где вам нужно иметь дело с устаревшими значениями.

/** Enums can implement this to provide a method for resolving a string 
    * to a proper enum name. 
    */ 
public interface EnumHelp 
{ 
    // Resolve s to a proper enum name that can be processed by Enum.valueOf 
    String resolve(String s); 
} 

/** EnumParser provides methods for resolving symbolic names to enum instances. 
    * If the enum in question implements EnumHelp, then the resolve method is 
    * called to resolve the token to a proper enum name before calling valueOf. 
    */ 
public class EnumParser 
{ 
    public static <T extends Enum<T>> T fromText(Class<T> cl, String s) { 
     if (EnumHelp.class.isAssignableFrom(cl)) { 
      try { 
       Method resolve = cl.getMethod("resolve", String.class); 
       s = (String) resolve.invoke(null, s); 
      } 
      catch (NoSuchMethodException ex) {} 
      catch (SecurityException ex) {} 
      catch(IllegalAccessException ex) {} 
      catch(IllegalArgumentException ex) {} 
      catch(InvocationTargetException ex) {} 
     } 
     return T.valueOf(cl, s); 
    } 

    public <T extends Enum<T>> String toText(T value) 
    { 
     return value.name(); 
    } 
} 
+0

Многие из существующих объектов уже перечислены, хотя, к сожалению, поскольку Enum не позволяет переопределять valueOf() наша способность создавать хорошие текстовые представления (значения по умолчанию, нуль-сейф, поддержка прошлых версий, кодирование и т. д.) ограничена - следовательно, наше желание реализовать отдельные методы - но с подобной семантикой, просто больше возможностей в нашей настройке , – jayshao

+0

Ничего не мешает вам добавить пользовательский метод поиска к вашему перечислению, если вам не нравится поведение valueOf(). Вы часто это делаете, если вам нужен метод поиска, который возвращает значение null или значение по умолчанию, если ничего не найдено. С valueOf() вы можете сделать это только с помощью try/catch, который ставит обработку исключений, отвечающих за поток управления. –

0

Кажется, вам нужно отделить завод от созданного объекта.

public interface TextTransformer<T> { 
    public T fromText(String text); 
    public String toText(T source); 
} 

Вы можете зарегистрировать классы TextTransformer, как вам нравится:

public class FooTextTransformer implements TextTransformer<Foo> { 
    public Foo fromText(String text) { return ...; } 
    public String toText(Foo source) { return ...; } 
} 

Если вы хотите FooTextTransformer быть синглтон, вы можете использовать контейнер, как Spring для обеспечения этого. Google инициировал проект, чтобы удалить все вручную насильственные одиночка из их кодовых, но если вы хотите сделать старомодный синглтон, вы могли бы использовать служебный класс:

public class TextTransformers { 
    public static final FooTextTransformer FOO = new FooTextTransformer(); 
    ... 
    public static final BarTextTransformer BAR = new BarTextTransformer(); 
} 

В коде клиента:

Foo foo = TextTransformers.FOO.fromText(...); 
... 
foo.setSomething(...); 
... 
String text = TextTransformers.FOO.toText(foo); 

Это всего лишь один подход с моей головы.

2

Совершенно другой подход (и уродливый взлом, если на то пошло) - это позволить интерфейсу иметь метод, который возвращает метод.

public interface MyInterface{ 
    Method getConvertMethod(); 
} 

теперь ваш клиентский код может сделать

yourInterface.getConvertMethod().invoke(objectToBeConverted); 

Это очень мощный, но очень плохой дизайн API

Шон

+0

Это интересно на самом деле - в JS я бы сделал это в одно мгновение или на каком-то другом языке, где функторы были обычными, в Java это кажется немного странным, хотя – jayshao

+0

точно, поскольку методы не являются «гражданами первого класса» на Java. Вскоре это может измениться в JDK1.7. Мы увидим, насколько это изменит стиль программирования ... –

1

Просто другая идея. Не применимо для всех случаев, но может помочь кому-то другому.

Вы можете использовать abstract class, как мост между вашим interface и вашим concrete classes. Абстрактные классы позволяют использовать статические методы, а также определения методов контрактов. Например, вы можете проверить API Collection, где Sun реализовало несколько абстрактных классов, вместо кодирования грубой силы с нуля всех конкретных классов.

В некоторых случаях вы можете просто заменить интерфейсы абстрактными классами.

Смежные вопросы