2013-11-22 4 views
0

Я хочу иметь абстрактный класс, скажем Foo, который предоставляет набор API, которые будут реализованы подклассами. Но я не хочу, чтобы кто-нибудь знал, что является фактическим экземпляром этого подкласса, только чтобы их можно было восстановить с помощью статических методов Foo. Примерно такие классы будут выглядеть так:Зарегистрировать различные реализации класса

class Foo { 

    protected Hashtable foos = new Hashtable; 

    public static Foo getFoo(String key) { 
     return (Foo) foos.get(key); 
    } 

    public abstract void doSomething(); 

} 

class FooA extends Foo { 

    static { 
     Foo.foos.put("A", new FooA()); 
    } 

    public void doSomething() { 
     System.out.println("A FooA instance is doing something!!"); 
    } 

} 

class FooB extends Foo { 

    static { 
     Foo.foos.put("B", new FooB()); 
    } 

    public void doSomething() { 
     System.out.println("A FooB instance is doing something!!"); 
    } 

} 

И использование было бы следующее:

Foo foo = Foo.get("A"); 
foo.doSomething(); 

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

Некоторые могут подумать, что решение состоит в том, чтобы не иметь неизвестных реализаций, но это действительно требование моей проблемы.

+0

Считаете ли вы использование 'Class.forName' для загрузки ваших подклассов? –

ответ

0

Вы можете загрузить «неизвестные» регистрации (услуг), если они каким-либо образом зарегистрируются в базовом классе. Различные контейнеры времени выполнения предлагают различные способы регистрации услуг, но стандартный способ, доступный в JDK, поскольку JDK6 заключается в использовании ServiceLoader.load (Foo.class) (javadoc в http://docs.oracle.com/javase/7/docs/api/java/util/ServiceLoader.html). Я использую этот метод для реализации того, что я называю инъекционными синглонами (где singleton API не знает, где его фактическая реализация, см. http://wiki.apidesign.org/wiki/Injected_Singleton), но этот метод можно было бы использовать и в вашем примере.

Просто убедитесь, что META-INF/услуги/your.pkg.Foo файл находится в баночке с содержанием упоминая следующие две строки (могут быть получены с помощью @ org.openide.util.lookup.ServiceProvider аннотацию, но делает не надо):

your.pkg.FooA 
your.pkg.FooB 

, а затем вы можете использовать

for (Foo instance : ServiceLoader.load(Foo.class)) { 
    /* do something with instance of FooA and FooB */ 
} 
0

в качестве альтернативного решения (в зависимости от сложности задачи) может быть проще для вас использовать инъекции зависимостей. В этом шаблоне вы просто определяете реализации своего интерфейса и назначаете ему квалификаторы, а затем позволяете инфраструктуре DI «внедрять» фактические реализации в классы, которые в них нуждаются. Кластеры не только не знают, какие конкретные реализации они используют, они не заботятся о том, как их получить, что может упростить многое. Там уже много таких фреймворков (наиболее заметны Spring и Guice), а java 5+ имеет стандартизованный набор аннотаций, которые вы используете для определения правил впрыска.

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