Форум программистов «Весельчак У»
  *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

  • Рекомендуем проверить настройки временной зоны в вашем профиле (страница "Внешний вид форума", пункт "Часовой пояс:").
  • У нас больше нет рассылок. Если вам приходят письма от наших бывших рассылок mail.ru и subscribe.ru, то знайте, что это не мы рассылаем.
   Начало  
Наши сайты
Помощь Поиск Календарь Почта Войти Регистрация  
 
Страниц: [1]   Вниз
  Печать  
Автор Тема: Generic-типы и помесь фабрики с виртуальным конструктором.  (Прочитано 6078 раз)
0 Пользователей и 1 Гость смотрят эту тему.
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« : 09-10-2010 18:30 » 

Имеется пара классов A и B, так что B extends A.

У этих классов разные по числу параметров конструкторы A(int a) и B(int a, int b).

Экземпляры этих классов создаются в результате парсинга строчек, так что:
Код: (Java)
A create(String s) {
  // ...
  return new A(a);
}

B create(String s) {
  // ...
  return new B(a, b);
}
Для удобства доступа есть желание поместить эти методы в некий общий класс Factory, чтобы эту фабрику можно было бы хранить в переменной и передавать в разные места как одно целое. Естественно, поместить в класс два метода, различающиеся лишь типом результата нельзя, но теоретически и по смыслу задачи можно специализировать вызовы для каждого случая использования.

Если брать C++, получается такое решение:
Код: (C++)
#include <iostream>

using namespace std;

class A {
public:
        A(int a) {
                cout << "A" << endl;
        }
};

class B: public A {
public:
        B(int a, int b): A(a) {
                cout << "B" << endl;
        }
};

class Factory {
public:
        template<class T>
        T *create() {
                return new T();
        }

        template<>
        A *create() {
                return new A(0);
        }

        template<>
        B *create() {
                return new B(0, 1);
        }
};

int main() {
        Factory factory;
        delete factory.create<A>();
        delete factory.create<B>();
        return 0;
}

В данном случае используется механизм специализации шаблона. Для каждого вызова create из main по шаблонному параметру выбирается, какую из трёх реализаций выбирать: обобщённую (generic) для неизвестных типов, либо одну из специализированных, если параметром задан класс A или B. В специальных реализациях вызываются нужные конструктры (со своим количеством параметров для каждого случая).

В Java некоторые элементы специализации шаблонов имеются. В Java можно написать нечто вида X<T extends U> или для метода <T extends U>T f(). Но компилятор либо не использует информацию об ограничении типа параметра generic-типа или -функции, либо я чего-то не знаю. Можно ли такое сделать?

P.S. Варианты с использованием Reflections и RTTI (конструкции наподобие instanceof или if(x.class == ...) ) не предлагать. Хочется иметь "автоматический" выбор нужной реализации фабричного метода на этапе компиляции.
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Falsehood
Молодой специалист

ru
Offline Offline
Пол: Женский
не может быть


« Ответ #1 : 08-12-2010 15:41 » 

Dimka, если сам еще не нашел, то вот - http://download.oracle.com/javase/tutorial/java/generics/erasure.html. в java не такие шаблоны как в с++.
Записан

Славная трава...
npak
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #2 : 08-12-2010 19:24 » 

Dimka,

если ещё актуально, то твоя задача решается в лоб созданием статической хэш-таблицы с фабриками. Идея такая:
Код: (Java)
public class Builder {
        public static interface Factory<T> {
                T create();
        }

        @SuppressWarnings("unchecked")
        private static HashMap<Class, Factory> factories = new HashMap<Class, Factory>();
        public static <T> void setFactory(Class<T> clazz, Factory<T> factory) {
                factories.put(clazz, factory);
        }
        @SuppressWarnings("unchecked")
        public static <T> T build(Class<T> clazz){
                Factory<T> f = factories.get(clazz);
                if (f == null) throw new RuntimeException("Factory for " + clazz.getName() + " is not set");
                return f.create();
        }
       
}
Перед тем, как пользоваться таким билдером, необходимо инициализировать фабрики
Код: (Java)
public class Main {
        static {
                Builder.setFactory(A.class, new Builder.Factory<A>() {
                        @Override
                        public A create() {return new A(0);}
                });
                Builder.setFactory(B.class, new Builder.Factory<B>() {
                        @Override
                        public B create() {return new B(0,1);}
                });
        }
        public static void main(String[] args) {
                A a = Builder.build(A.class);
                B b = Builder.build(B.class);
        }
}
« Последнее редактирование: 08-12-2010 19:26 от npak » Записан

UniTesK -- индустриальная технология надежного тестирования.

http://www.unitesk.com/ru/
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #3 : 08-12-2010 21:13 » 

npak,
Цитата: Dimka
P.S. Варианты с использованием Reflections и RTTI (конструкции наподобие instanceof или if(x.class == ...) ) не предлагать. Хочется иметь "автоматический" выбор нужной реализации фабричного метода на этапе компиляции.
Моя цель была не "создать фабрику", а "специализировать шаблон". Фабрика - это лишь повод подобраться к шаблону. Твоё решение использует RTTI, а в этом случае generic-типы вообще необязательны.
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
npak
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #4 : 08-12-2010 22:24 » 

В Java нет специализации шаблонов. Но в случае твоей задачи можно сымитировать специализацию.

Код: (Java)
public class Builder2 {
        public static A build(Class<A> clazz) { return new A(0); }
        public static B build(Class<B> clazz) { return new B(0,1); }
        // И так далее, для всех типов, которым требуется специализация конструктора.
        public static <T> T build(Class<T> clazz) throws InstantiationException, IllegalAccessException { return clazz.newInstance(); }
}
Компилятор правильно определит метод по аргументу
Код: (Java)
        public static void main(String[] args) throws Exception {
                Builder2.build(A.class);
                Builder2.build(B.class);
        }
Записан

UniTesK -- индустриальная технология надежного тестирования.

http://www.unitesk.com/ru/
Страниц: [1]   Вверх
  Печать  
 

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines