2012-06-02 3 views
1

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

  • имеет общий интерфейс,
  • имеет различные общие базовые классы реализуют ее ,
  • предоставляют подклассы для базовых базовых классов,
  • каким-то образом получают не общий интерфейс для подклассов.

Confused? Читайте дальше и расскажите мне, что вы думаете о решении.

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

Для хорошего начала давайте создадим соответствующий интерфейс:

public interface ObjectProvider<K, V> { 

    V provide(K key); 
} 

В этом случае я буду в состоянии обеспечить любой вид реализации. Я хочу предоставить объекты из базы данных и файла.

Предположим, что предоставление V на основе K может быть выполнено с той же логикой DB независимо от типов объектов. Так что я могу написать общий базовый класс для доступа к БД:

public class DBObjectProvider<K, V> implements ObjectProvider<K, V> { 

    public V provide(K key) { 
     V v = null; 
     //some common DB logic to get V based on K 
     return v; 
    } 
} 

Собственно, получение объектов из файла также объект типа независимый:

public class FileObjectProvider<K, V> implements ObjectProvider<K, V> { 

    public V provide(K key) { 
     V v = null; 
     //some common file reading logic to get V based on K 
     return v; 
    } 
} 

Ok, теперь у меня есть две общие классы, Я могу использовать, чтобы получить все, что хочу.

Теперь я хочу использовать одну из этих общих функций, чтобы получить объект String на основе строкового ключа из базы данных. Кроме того, я хочу, чтобы он был определен как bean-компонент с использованием Spring XML. Я думаю, что нет способа определить общий боб в Spring XML (я прав?), Поэтому я создаю вместо этого класс нужного класса . Все, что мне нужно сделать, это:

public class DBStringStringProvider extends DBObjectProvider<String, String> { 
} 

Сейчас я могу вводить этот компонент в любой:

private ObjectProvider<String, String> objectProvider; 

Все отлично, теперь ключевая часть. Я мог бы использовать мой полезный DB String-String провайдер во многих отношениях (hhmmm ... ok на самом деле). Предположим, я хочу сделать веб-сервис из него. Предположим, я хочу открыть DBStringStringProvider в качестве веб-службы CXF и протестировать его. Проблема заключается в том, что клиентский код для такой веб-службы выглядит следующим образом:

JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean(); 
factory.setServiceClass(<INTERFACE>.class); 
<INTERFACE> client = (<INTERFACE>) factory.create(); 

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

я могу сделать следующее:

Продлить ObjectProvider < String, > интерфейса Строки с другой:

@WebService 
public interface StringStringProvider extends ObjectProvider<String, String> { 

    @WebMethod 
    String provide(String key); 
} 

Кроме его реализации, где она вроде уже внедренного родового базового класса:

public class DBStringStringProvider extends DBObjectProvider<String, String> 
implements StringStringProvider { 
} 

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

factory.setServiceClass(StringStringProvider.class); 
StringStringProvider client = (StringStringProvider) factory.create(); 

Мой вопрос: действительно ли это хорошая практика, чтобы делать то, что я только что сделал? Если нет, есть ли другой способ?

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

+3

Я всегда представляю бедного парня, который наследует такой код. –

+0

Что в этом плохого? – Dzik

+3

@Dzik: ответить на ваш вопрос: просто слишком сложно ... держите его простым и спросите себя, каково преимущество такого дизайна, как это на самом деле (помните о поддерживаемости). – home

ответ

1

Я ненавижу критиковать мотивы за уровень абстракции в заданном вопросе. Для согласованности интерфейсов и сокращения дублированного кода эти сценарии возникают время от времени. Java и нечеткие рамочные службы, делают эту задачу намного более сложной, чем она должна быть; такая ненужная сложность не означает, что исходный дизайн плох. Каждый день Java заставляет меня сжимать зубы по ряду причин, связанных с бессмысленной многословностью.

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

Единственное, что нужно сделать, это то, что вы действительно пытаетесь сделать typedef в Java. Лучшее, что я смог сделать w.r.t. typedefs в Java приводит к поразительно уродливой конструкции, которая заставила бы комментаторов на вашем исходном посту, судя по их отвращению к сложности, хотеть оценивать собственные глаза.

Я сомневаюсь, что на практике у вас возникнут какие-либо проблемы, но в целом psuedo-typedefs, например, этот пример может вызвать неожиданную боль. В общем случае, когда вы создаете тривиальный субинтерфейс T интерфейса S, заманчиво использовать T вместо S, где бы он ни применим. S может быть длинным параметризованным выражением класса Java, которое является болью в заднице, чтобы читать или печатать, например, и T может быть приятным и коротким. Но для аргументов метода/службы/конструктора вам по-прежнему нужен S в качестве объявленного типа или вы рискуете ограничить полезность своего метода или класса. Надуманный пример:

public interface Info extends Map< String, List<FooBar> > {} 

public interface Service { 
    public Info doSomething(Info info); 
} 

Там нет никаких оснований для Service только принять Info, поскольку Info не добавляет API для Map< String, List<FooBar> >; как написано, вызывающие doSomething не могут принимать какую-либо старую карту в качестве входа; они должны создать новый файл и инициализировать его содержимым этой карты, потому что Java (например, почти любой другой широко используемый язык предприятия) использует именную типизацию.

+0

Спасибо за отличный объективный ответ и ссылку на псевдотипы. – Dzik

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