Список форумов forum.alsor.net forum.alsor.net
Форум небольшого круга друзей
 
 FAQFAQ   ПоискПоиск   ПользователиПользователи   ГруппыГруппы   РегистрацияРегистрация 
 ПрофильПрофиль   Войти и проверить личные сообщенияВойти и проверить личные сообщения   ВходВход 

и так наследование...
На страницу 1, 2, 3, 4  След.
 
Начать новую тему   Ответить на тему    Список форумов forum.alsor.net -> IT-форум
Предыдущая тема :: Следующая тема  
Автор Сообщение
alsor



Зарегистрирован: 25.11.2005
Сообщения: 1166
Откуда: Киберпространство

СообщениеДобавлено: Пт Мар 06, 2009 5:42 pm    Заголовок сообщения: и так наследование... Ответить с цитатой

Код:

class Comparer {
  public boolean isChanged(Obeject o1, Object o2) {
    return !o1.equals(o2)
  }
}

class UserMonitor {
  private UserDao userDao;
  private User user;
  private UserChangedCallback callback;
  private Comparer comparer;

  void monitorIt() {
    if (comparer.isChanged(user, userDao.loadById(user.getId())) {
       callback.process(user);
    }
  }
}
[/code]
Вернуться к началу
Посмотреть профиль Отправить личное сообщение Отправить e-mail Посетить сайт автора
Snoopy



Зарегистрирован: 29.11.2005
Сообщения: 1395

СообщениеДобавлено: Пт Мар 06, 2009 5:49 pm    Заголовок сообщения: Ответить с цитатой

Код:

class DocumentMonitor {
  private DocumentDao documentDao;
  private Document document;
  private DocumentChangedCallback callback;
  private Comparer comparer;

  void monitorIt() {
    if (comparer.isChanged(document, documentDao.loadById(document.getId())) {
       callback.process(document);
    }
  }
}


Вопрос №1: Найди 5 отличий в методах monitorIt() двух классов (чисто алгоритмические, названия и типы переменных не в счет).
Вопрос №2: нафига нам два класса?
Вопрос №3: что будет если надо будет единообразно мониторить сто типов сущностей?
Вернуться к началу
Посмотреть профиль Отправить личное сообщение
alsor



Зарегистрирован: 25.11.2005
Сообщения: 1166
Откуда: Киберпространство

СообщениеДобавлено: Пт Мар 06, 2009 5:57 pm    Заголовок сообщения: Ответить с цитатой

Ответ №1: Названия переменных действительно не в счет, но вот типы как раз в счет.
Ответ №2: см. Ответ №1 - имменно из-за типов я выделил два класса.
Ответ №3: тогда (если мы все еще привязаны к JDBC) ты будешь вынужден написать 100 таких классов. Именно поэтому и сделали Hibernate
_________________
JIT happens!
Вернуться к началу
Посмотреть профиль Отправить личное сообщение Отправить e-mail Посетить сайт автора
Snoopy



Зарегистрирован: 29.11.2005
Сообщения: 1395

СообщениеДобавлено: Пт Мар 06, 2009 6:02 pm    Заголовок сообщения: Ответить с цитатой

№1: С точки зрения алгоритма типы пофиг
№2: А смысл?
№3: См. №2 и умножай на 100
Вернуться к началу
Посмотреть профиль Отправить личное сообщение
Snoopy



Зарегистрирован: 29.11.2005
Сообщения: 1395

СообщениеДобавлено: Пт Мар 06, 2009 6:05 pm    Заголовок сообщения: Ответить с цитатой

ЗЫ DAO на то и DAO, чтоб не быть привязанным к ODBC
Вернуться к началу
Посмотреть профиль Отправить личное сообщение
alsor



Зарегистрирован: 25.11.2005
Сообщения: 1166
Откуда: Киберпространство

СообщениеДобавлено: Пт Мар 06, 2009 6:05 pm    Заголовок сообщения: Ответить с цитатой

чтобы я смог ответить на эти вопросы - ты всеже придумай что-нибудь что будет в callback.process. То есть для чего мы мониторим это?
_________________
JIT happens!
Вернуться к началу
Посмотреть профиль Отправить личное сообщение Отправить e-mail Посетить сайт автора
Snoopy



Зарегистрирован: 29.11.2005
Сообщения: 1395

СообщениеДобавлено: Пт Мар 06, 2009 6:10 pm    Заголовок сообщения: Ответить с цитатой

Будет моргать лампочка. Razz

// С точки зрения инкапсуляции тебя это не должно волновать
Вернуться к началу
Посмотреть профиль Отправить личное сообщение
Snoopy



Зарегистрирован: 29.11.2005
Сообщения: 1395

СообщениеДобавлено: Пт Мар 06, 2009 6:27 pm    Заголовок сообщения: Ответить с цитатой

Для непосвещенных в нашу асечную переписку, тема: "Можно ли делать базовый DAO с некими общими для всех типов методами (в данном случае - loadById(id)), или между DAO в принципе нет и не может быть ничего общего и они должны быть несвязанными классами и использоваться независимо. Рассматривается на примере некоего монитора, задача которого - вызвать callback при изменении сущности.

Мое исходное предложение:

Код:

class ChangeMonitor {
private BaseDao dao; // базовый класс
private Action callback; // базовый класс
private Entity entity; // базовый класс

// тут конструктор

void monitorIt() {
  while (true) {
    if  (entity.equals(dao.getById(entity.getId()))) {
      callback.process(entity);
    }
  }
}


Лехин апгрейд выше.
Вернуться к началу
Посмотреть профиль Отправить личное сообщение
teg



Зарегистрирован: 20.12.2005
Сообщения: 410

СообщениеДобавлено: Пт Мар 06, 2009 6:29 pm    Заголовок сообщения: Ответить с цитатой

Нет, нельзя, не правильно.

Нужно делать инструментальные классы, что-то вроде DAOTools со статикой. И использовать это там, где требуется.
Вернуться к началу
Посмотреть профиль Отправить личное сообщение
alsor



Зарегистрирован: 25.11.2005
Сообщения: 1166
Откуда: Киберпространство

СообщениеДобавлено: Пт Мар 06, 2009 6:51 pm    Заголовок сообщения: Ответить с цитатой

и так продолжаем. тут я уже вооружился эклипсом и вот наваял:

Код:

interface ChangedCallback
{
    void changed();
}

class LightOnCallback implements ChangedCallback
{
    private Light light;

    public LightOnCallback(Light light)
    {
        this.light = light;
    }

    public void changed()
    {
        light.on();
    }
}

abstract class ChangedMonitor
{
    private ChangedCallback callback;

    public ChangedMonitor(ChangedCallback callback)
    {
        this.callback = callback;
    }

    void monitorIt()
    {
        while (true)
        {
            if (getOriginal().equals(getReloaded()))
            {
                callback.changed();
            }
        }
    }

    abstract Object getOriginal();

    abstract Object getReloaded();
}

class UserChangedMonitor extends ChangedMonitor
{
    private UserDao userDao;
    private User original;

    public UserChangedMonitor(UserDao userDao, User original, ChangedCallback callback)
    {
        super(callback);
        this.original = original;
        this.userDao = userDao;
    }

    @Override
    Object getOriginal()
    {
        return original;
    }

    @Override
    Object getReloaded()
    {
        return userDao.loadById(original.id());
    }


Пример использования:
Код:

        Light light = new Light();
        LightOnCallback callback = new LightOnCallback(light);
        UserDao userDao = new UserDao();
        User user = new User();
        ChangedMonitor userChangedMonitor = new UserChangedMonitor(userDao, user, callback);
        userChangedMonitor.monitorIt();


Суть:
до тех пор пока ты мне не сформулировал никаких требований относительно обработки в callback (кроме лампочки) я не вижу смысл передавать туда объект. Потому что кроме того что это объект у тебя в callback'е нет больше никакой информации.

Алгоритм я вынес в абстрактный класс - он в одном месте

Остальные классы придется плодить все по тойже причине - безопасность на уровне типов, отсутствие универсального кода который может загружать любые объекты.
Вернуться к началу
Посмотреть профиль Отправить личное сообщение Отправить e-mail Посетить сайт автора
Snoopy



Зарегистрирован: 29.11.2005
Сообщения: 1395

СообщениеДобавлено: Пт Мар 06, 2009 6:58 pm    Заголовок сообщения: Ответить с цитатой

teg писал(а):
Нет, нельзя, не правильно.


Почему?

teg писал(а):
Нужно делать инструментальные классы, что-то вроде DAOTools со статикой. И использовать это там, где требуется.


В чем преимущество?
Вернуться к началу
Посмотреть профиль Отправить личное сообщение
Snoopy



Зарегистрирован: 29.11.2005
Сообщения: 1395

СообщениеДобавлено: Пт Мар 06, 2009 7:09 pm    Заголовок сообщения: Ответить с цитатой

alsor писал(а):

Суть:
до тех пор пока ты мне не сформулировал никаких требований относительно обработки в callback (кроме лампочки) я не вижу смысл передавать туда объект.


Без разницы, пусть так - callback не принципиален.

alsor писал(а):
Алгоритм я вынес в абстрактный класс - он в одном месте


Да, но он у тебя так же оперирует Object'ами - т.е. фактически базовыми для User классами. А ведь изначально, вспомни, речь шла как раз о том, что значение, которое вернет DAO может использоваться только как конкретный тип.

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


И то, и другое - зависит от языка реализации и библиотек, с помощью которых загружаются объекты. Т.е. это офтоп, т.к. говорим о теории.
Вернуться к началу
Посмотреть профиль Отправить личное сообщение
alsor



Зарегистрирован: 25.11.2005
Сообщения: 1166
Откуда: Киберпространство

СообщениеДобавлено: Пт Мар 06, 2009 7:17 pm    Заголовок сообщения: Ответить с цитатой

Snoopy писал(а):
Да, но он у тебя так же оперирует Object'ами - т.е. фактически базовыми для User классами. А ведь изначально, вспомни, речь шла как раз о том, что значение, которое вернет DAO может использоваться только как конкретный тип.


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

Код:

class UserDao {
  Object loadById(Long id) {
    User user;
    .. here we load and fill user object ..
    return user; // up-cast! WTF
  }
}


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

Snoopy писал(а):
И то, и другое - зависит от языка реализации и библиотек, с помощью которых загружаются объекты. Т.е. это офтоп, т.к. говорим о теории.


Конечно зависит от языка и библиотек. Сейчас я рассматриваю случай когда мы пишем на JDBC а значит у нас нет возможности загружать объекты универсальным способом. Если бы мы использовани Hibernate то можно было бы использовать объект Session в качестве BaseDao, но опять же остальные Dao мы бы не наследовали от чего-то общего.
_________________
JIT happens!
Вернуться к началу
Посмотреть профиль Отправить личное сообщение Отправить e-mail Посетить сайт автора
Snoopy



Зарегистрирован: 29.11.2005
Сообщения: 1395

СообщениеДобавлено: Пт Мар 06, 2009 7:26 pm    Заголовок сообщения: Ответить с цитатой

alsor писал(а):

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


Да, но твой код оперирует переменными типа Object, при том, что в них объекты конкретных типов. Это ровно то же самое.

alsor писал(а):
Конечно зависит от языка и библиотек. Сейчас я рассматриваю случай когда мы пишем на JDBC


А не надо так делать. Механизмы реализации - дело восьмое и к нашему спору отношения оно не имеет.
Вернуться к началу
Посмотреть профиль Отправить личное сообщение
teg



Зарегистрирован: 20.12.2005
Сообщения: 410

СообщениеДобавлено: Пт Мар 06, 2009 7:27 pm    Заголовок сообщения: Ответить с цитатой

Snoopy писал(а):

Почему?

Потому что два DAO похожи друг на друга только именами методов. Мы же не делаем интерфейсов типа ObjectWithLoadByIdMethod, и тем более не делаем абстрактных классов по такому принципу. Повторное использование кода и наличие иерархии -- разные вещи.

Посмотри, чем отличается Builder и Factory. Чтобы собрать два разных объекта с помощью фабрики, понадобится делать иерархию (с учётом реюза). Builder легко позволяет обойтись одним классом. При этом прозрачность и повторный реюз будет в разы. Но использовать Builder сложнее. Поэтому под каждый кейс делаем фасадный Factory, который использует Builder. При этом каждый класс фабрики -- сам по себе.
Вернуться к началу
Посмотреть профиль Отправить личное сообщение
alsor



Зарегистрирован: 25.11.2005
Сообщения: 1166
Откуда: Киберпространство

СообщениеДобавлено: Пт Мар 06, 2009 7:36 pm    Заголовок сообщения: Ответить с цитатой

Snoopy писал(а):
Да, но твой код оперирует переменными типа Object, при том, что в них объекты конкретных типов. Это ровно то же самое.


Мой код оперирует объектами типа Object именно потому что ты мне так и не сформулировал никаких требований про callback кроме лампочки - чтобы зажечь лампочку мне достаточно знать что у объектов есть метод equals и все - я их могу сравнить. А в callback'е вообще ничего не зависит от объекта.
Если бы ты придумал более сложную задачу то возможно я бы уже не обошелся только Object - тогда скорее всего пришлось бы ввести какой-то интерфейс в сущности которые могут быть таким образом обработаны. Ну скажем если бы при изменении объекта мне нужно было выводить имя этого объекта, то скорее всего я бы сделал интерфейс Named и смог бы оперировать сущностями реализующими этот интерфейс.

Задача сразу бы стала сложнее - потому что в таком виде как она написана выше сразу бы слишком тесно оказались связанными монитор и callback.. и тут нужно уже думать дальше как их развязать, если это возможно.
Вернуться к началу
Посмотреть профиль Отправить личное сообщение Отправить e-mail Посетить сайт автора
Snoopy



Зарегистрирован: 29.11.2005
Сообщения: 1395

СообщениеДобавлено: Пт Мар 06, 2009 7:37 pm    Заголовок сообщения: Ответить с цитатой

teg писал(а):

Потому что два DAO похожи друг на друга только именами методов.


Опять же - почему? Суть методов тоже одна и та же - "загрузить объект по Id". Скорее уж они отличаются только типами возвращаемого значения.

teg писал(а):

Повторное использование кода и наличие иерархии -- разные вещи.


Ну OK. Как же будет выглядеть повторное использование кода в твоем случае? Можно пример?
Вернуться к началу
Посмотреть профиль Отправить личное сообщение
Snoopy



Зарегистрирован: 29.11.2005
Сообщения: 1395

СообщениеДобавлено: Пт Мар 06, 2009 7:45 pm    Заголовок сообщения: Ответить с цитатой

alsor писал(а):

Если бы ты придумал более сложную задачу то возможно я бы уже не обошелся только Object - тогда скорее всего пришлось бы ввести какой-то интерфейс в сущности которые могут быть таким образом обработаны. Ну скажем если бы при изменении объекта мне нужно было выводить имя этого объекта, то скорее всего я бы сделал интерфейс Named и смог бы оперировать сущностями реализующими этот интерфейс.


В каком-то приближении можно представить, что Object - это тоже интерфейс, не в этом суть. Суть в том, что ты используешь базовый тип для выполнения equals/getName/итд, а остальные методы доопределяешь к нему уже свободно. Т.е. примерно то, о чем мы и говорили изначально.
Вернуться к началу
Посмотреть профиль Отправить личное сообщение
alsor



Зарегистрирован: 25.11.2005
Сообщения: 1166
Откуда: Киберпространство

СообщениеДобавлено: Пт Мар 06, 2009 7:45 pm    Заголовок сообщения: Ответить с цитатой

Snoopy писал(а):
Опять же - почему? Суть методов тоже одна и та же - "загрузить объект по Id". Скорее уж они отличаются только типами возвращаемого значения.


Нет. Тип это очень важно. И поэтому в каждом конктерном случае суть метода примерно такая "загрузить объект User по Id".

Если у тебя некоторые сущности будут действительно обладать каким-то общим типом, ну например как я написал выше будут Named, то тогда можно будет написать метод "загрузить объект Named по Id". Но для этого не достаточно изменений только в ДАО - тебе придется отразить эту иерархию на уробне базы данных (одним из способов отражения наследования на реляционные таблицы) - эта задача уже совсем не тривиальная и тут мы потихоньку приближаемся к написанию хибернейта.
_________________
JIT happens!
Вернуться к началу
Посмотреть профиль Отправить личное сообщение Отправить e-mail Посетить сайт автора
Snoopy



Зарегистрирован: 29.11.2005
Сообщения: 1395

СообщениеДобавлено: Пт Мар 06, 2009 7:51 pm    Заголовок сообщения: Ответить с цитатой

alsor писал(а):

Нет. Тип это очень важно. И поэтому в каждом конктерном случае суть метода примерно такая "загрузить объект User по Id".


Да блин, почему? Твой же собственный код использует их как Object, а не как User.

alsor писал(а):
для этого не достаточно изменений только в ДАО


Напоминаю: другие уровни мы сейчас не рассматриваем.
Вернуться к началу
Посмотреть профиль Отправить личное сообщение
alsor



Зарегистрирован: 25.11.2005
Сообщения: 1166
Откуда: Киберпространство

СообщениеДобавлено: Пт Мар 06, 2009 7:54 pm    Заголовок сообщения: Ответить с цитатой

Snoopy писал(а):
В каком-то приближении можно представить, что Object - это тоже интерфейс, не в этом суть. Суть в том, что ты используешь базовый тип для выполнения equals/getName/итд, а остальные методы доопределяешь к нему уже свободно. Т.е. примерно то, о чем мы и говорили изначально.


да. Но в случае доменных объектов у меня будет почти весь остальной код системы использовать их именно как конкретные типы. Т.е. здесь как раз на лицо обоснованная иерархия типов. Но там где несколько разных объектов будут использоваться как Named объекты - у них у всех будет одна и та же семантика метода name() - он возвращает имя объекта. Наверно Named не очень удачное название для интерфейса - пусть это будет RealName который возвращает реальное имя человека. Тогда такой интерфейс может быть реализован в объектах скажем User, Employee, Manager и прочее. Но он не должен быть реализован в объекте File где name вернет имя файла. Хм... с именем действительно члишком хитрый пример - потому что само понятие "имя" слишком обширно трактуется... но я думаю ты суть уловил. У DAO же нет таких общих по семантике методов. DAO вообще довольно неприятные объекты - как я уже сказал это способ структурировать и сгруппировать код.
_________________
JIT happens!
Вернуться к началу
Посмотреть профиль Отправить личное сообщение Отправить e-mail Посетить сайт автора
alsor



Зарегистрирован: 25.11.2005
Сообщения: 1166
Откуда: Киберпространство

СообщениеДобавлено: Пт Мар 06, 2009 7:57 pm    Заголовок сообщения: Ответить с цитатой

Snoopy писал(а):
Да блин, почему? Твой же собственный код использует их как Object, а не как User.


код в абстрактном классе - да, использует как Object. Код в конкретной реализации для пользователей - использует как User. И в конструтор конкретного класса ты не сможешь передать NonUserDao и объект User как original - в это мой код и безопаснее твоего
_________________
JIT happens!
Вернуться к началу
Посмотреть профиль Отправить личное сообщение Отправить e-mail Посетить сайт автора
alsor



Зарегистрирован: 25.11.2005
Сообщения: 1166
Откуда: Киберпространство

СообщениеДобавлено: Пт Мар 06, 2009 8:04 pm    Заголовок сообщения: Ответить с цитатой

Snoopy писал(а):
Напоминаю: другие уровни мы сейчас не рассматриваем.


К сожалению это возможно лишь для сферического коня в вакууме. Другие уровни мы вынуждены рассматривать - так же как и какие-то бизнес требования сверху, низлежащие уровни диктуют нам ограничения. Вся бадяга именно из-за того что у нас задача "монитроить на изменение любой объект" не может быть реализована именно из-за отсутствия возможности "универсально загрузить любой объект". Как я уже сказал - то что ты выделишь некий BaseDao - это иллюзия.
_________________
JIT happens!
Вернуться к началу
Посмотреть профиль Отправить личное сообщение Отправить e-mail Посетить сайт автора
Snoopy



Зарегистрирован: 29.11.2005
Сообщения: 1395

СообщениеДобавлено: Пт Мар 06, 2009 8:22 pm    Заголовок сообщения: Ответить с цитатой

alsor писал(а):

да. Но в случае доменных объектов у меня будет почти весь остальной код системы использовать их именно как конкретные типы.


Но здесь-то как базовый ты их все-таки заюзал.

alsor писал(а):
У DAO же нет таких общих по семантике методов.


Почему? Чем у getById не одна семантика? Чем он принципиально отличается от RealName.getName(), кроме того, что мы, якобы, не знаем как загрузить универсальный объект? Мы ведь и имя в общем случае не знаем, как получить - однако работать с RealName это нам не мешает.

alsor писал(а):

код в абстрактном классе - да, использует как Object. Код в конкретной реализации для пользователей - использует как User.


Чем плох вариант наоборот - написать обертку вокруг общего метода, которая будет возвращать конкретный тип? И, кстати, не для таких ли случаев придумали generic'и?

alsor писал(а):

И в конструтор конкретного класса ты не сможешь передать NonUserDao и объект User как original - в это мой код и безопаснее твоего


Ты тоже можешь в одном из ста мониторов завести поля несвязанных между собой типов. А я, кстати, original могу и не передавать - вычислить прямо на месте. Wink
Вернуться к началу
Посмотреть профиль Отправить личное сообщение
alsor



Зарегистрирован: 25.11.2005
Сообщения: 1166
Откуда: Киберпространство

СообщениеДобавлено: Пт Мар 06, 2009 8:57 pm    Заголовок сообщения: Ответить с цитатой

Snoopy писал(а):
Но здесь-то как базовый ты их все-таки заюзал.


Да. Ты видишь в этом проблемы? Я их заюзал как базовый потому что у меня гарантированно придет туда Object и мне не нужно от него ничего более чем в нем есть. Я не собираюсь нигде делать Down-Cast.

Snoopy писал(а):
Почему? Чем у getById не одна семантика? Чем он принципиально отличается от RealName.getName(), кроме того, что мы, якобы, не знаем как загрузить универсальный объект? Мы ведь и имя в общем случае не знаем, как получить - однако работать с RealName это нам не мешает.

Убери слово "якобы". Тут нет места никакому якобы. В этом и есть проблема. Чтобы реализовать наследование тебе нужно соблюсти принцип подстановки. В моем случае любой производный от RealName объект может быть полностью использован как RealName и не нарушит семантики. Потому что все что я ожидаю от RealName - это возможность получить имя. В случае реализации BaseDao - нет. Мы сейчас опять сойдем к спору о том что у getById не одна или одна и та же семантика. Я говорю что нет. Допустим ты действительно выделил BaseDao и сделал в нем метод findById который возвращает Entity. Во первых тут сразу ожидается что ты можешь загрузить любою сущность через этот метод - ЛОЖЬ! Тебе нужно еще этого добиться. Ведь я могу написать код который будет предполагать что любой переданный мне BaseDao может загрузить любой объект - у меня в коде нет ограничений говорящих о том что это на самом деле не так. А на самом деле так что - если ты мне передашь UserDao как BaseDao он будет способен загружать только объекты User.

Snoopy писал(а):
Чем плох вариант наоборот - написать обертку вокруг общего метода, которая будет возвращать конкретный тип?


Приведи мне пример - я уверен что смогу разгромить его в пух и прах Smile

Snoopy писал(а):
И, кстати, не для таких ли случаев придумали generic'и?

Generics - способ выразить обобщенный алгоритм сохранив типизацию. Не вижу где в моем коде я могу применить Generics - опять же приведи пример - посмотрим.

Snoopy писал(а):
Ты тоже можешь в одном из ста мониторов завести поля несвязанных между собой типов.

Согласен. Если мне нужно писать 100 таких классов - то наверно я рано или поздно уйду от JDBC в пользу Hibernate где у меня действительно будет возможность загружать объекты универсальным способом, а соответственно я смогу написать универсальный монитор.
Вернуться к началу
Посмотреть профиль Отправить личное сообщение Отправить e-mail Посетить сайт автора
Показать сообщения:   
Начать новую тему   Ответить на тему    Список форумов forum.alsor.net -> IT-форум Часовой пояс: GMT + 4
На страницу 1, 2, 3, 4  След.
Страница 1 из 4

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


Powered by phpBB © 2001, 2005 phpBB Group
Русская поддержка phpBB