Альтернативы статическим методам для интерфейсов для осуществления непротиворечивости

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

public interface TextTransformable{

  public static T fromText(String text);

  public String toText();

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

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

Примечание: в нашем случае, который наш основной пример использования, у нас есть многие, много "объектных значением" типов - перечисления или другие объекты, которые имеют ограниченное количество значений, обычно не несут состояния вне своего значения, и который мы parse/de-parse тысячи времени секунда, поэтому на самом деле заботьтесь о многократном использовании экземпляров (как Плавание, Целое число, и т.д.) и его влияние на потребление памяти/g.c.

Какие-либо мысли?

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

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

С точки зрения наших мыслей для Боксера наилегчайшего веса Фабрик - они - оба возможности, которые мы рассмотрели, действительно мы пытаемся видеть, можем ли мы найти что-то более изящным, хотя, чем доверие JavaDoc, говоря "реализацию Фабрика и делегирует вызовы к нему или выставляет его в XXX местоположениях условно"

28
задан Community 23 May 2017 в 12:34
поделиться

6 ответов

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

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

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

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

А затем используйте это так:

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

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

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

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

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

Вышеупомянутое можно сделать с помощью статического импорта фабрика и метод с такой подписью:

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

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

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

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

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

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

7
ответ дан 28 November 2019 в 03:55
поделиться

Похоже, вам нужно отделить фабрику от созданного объекта.

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);

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

0
ответ дан 28 November 2019 в 03:55
поделиться

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

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

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

}

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


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

1
ответ дан 28 November 2019 в 03:55
поделиться

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

Базовый класс перечисления 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
ответ дан 28 November 2019 в 03:55
поделиться

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

public interface Transformer<T>{ 

  public T fromText(String text); 

  public String toText(T obj); 
}

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

4
ответ дан 28 November 2019 в 03:55
поделиться

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

public interface MyInterface{
    Method getConvertMethod();
}

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

yourInterface.getConvertMethod().invoke(objectToBeConverted);

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

Шон

2
ответ дан 28 November 2019 в 03:55
поделиться
Другие вопросы по тегам:

Похожие вопросы: