Подскажите пожалуйста паттерн
От: dormouse  
Дата: 12.09.05 13:01
Оценка:
Уже не в первый раз сталкиваюсь с необходимостью написать параллельную иерархию классов.
Проще объяснить на конкретном примере. Пусть есть:
class Shape {};
class Circle : public Shape{};
class Ellipse : public Shape{};


Допустим, мы хотим сохранять каждый класс в базе данных, то есть добавить методы
save() и load().
В голову не приходит ничего кроме:
class Shape {};
class Cicle : virtual public Shape{};
class Ellipse : virtual public Shape{};
class PersistentShape : virtual public Shape {
    virtual void load() = 0;
    virtual void save() = 0;
};
class PersistentCicle : public Cicle, public PersistentShape
{};
class PersistentEllipse : public Ellipse, public PersistentShape
{};


Можно ли это сделать лучше? Как вы поступаете в таких случаях?
Re: Подскажите пожалуйста паттерн
От: LuciferMoscow Россия  
Дата: 12.09.05 13:04
Оценка:
Здравствуйте, dormouse, Вы писали:

Декоратор?
Re: Подскажите пожалуйста паттерн
От: sadomovalex Россия http://sadomovalex.blogspot.com
Дата: 12.09.05 13:30
Оценка:
Здравствуйте, dormouse, Вы писали:

D>Можно ли это сделать лучше? Как вы поступаете в таких случаях?


читал такую книжку Addison Wesley: Data Access Patterns: Database Interactions in Object-Oriented Applications ?
"Что не завершено, не сделано вовсе" Гаусс
Re[2]: Мне кажется что декоратор не поможет
От: dormouse  
Дата: 12.09.05 13:37
Оценка:
Нужно не декорировать существующие методы, а добавить новую функциональность. Кроме того, как быть в таком случае —
пусть Shape из предыдущего примера является базовым классом компоновщика,
то есть у нас есть:

class Shape {};
class ShapeContainer {};


В этом случае мы должны получить:

class PersistentShape : public ?
{
  virtual void save()
  {//do smtng}
}
class PersistentShapeContainer : public ?
{
  virtual void save()
  {
    for_each( shapes_.begin(), shapes_.end(), mem_fun(&Shape::save) );
  }
  deque<Shape*> shapes_;
}

Как добиться такого поведения в рамках декоратора?
Re[2]: Неа, не читал
От: dormouse  
Дата: 12.09.05 13:39
Оценка:
Там есть ответ на мой вопрос?
Re[3]: Мне кажется что декоратор не поможет
От: LuciferMoscow Россия  
Дата: 12.09.05 13:41
Оценка:
Здравствуйте, dormouse, Вы писали:

D>Нужно не декорировать существующие методы, а добавить новую функциональность. Кроме того, как быть в таком случае —

D>пусть Shape из предыдущего примера является базовым классом компоновщика,
D>то есть у нас есть:

D>
D>class Shape {};
D>class ShapeContainer {};
D>


D>В этом случае мы должны получить:


D>
D>class PersistentShape : public ?
D>{
D>  virtual void save()
D>  {//do smtng}
D>}
D>class PersistentShapeContainer : public ?
D>{
D>  virtual void save()
D>  {
D>    for_each( shapes_.begin(), shapes_.end(), mem_fun(&Shape::save) );
D>  }
D>  deque<Shape*> shapes_;
D>}
D>

D>Как добиться такого поведения в рамках декоратора?
А это ты уже другой паттерн приводишь Composite.
Re[4]: Ну и я говорю - компоновщик
От: dormouse  
Дата: 12.09.05 13:50
Оценка:
Так вот, как мне в этом случае поможет декоратор?
Мне нужно не декорировать какой-то из методов класса Shape, типа:

class Shape
{
virtual void rotate() = 0;
};

class ShapeDecorator : public Shape
{
virtual void rotate()
{
  shape_ ->rotate();
}
Shape * shape_;
};

class PersistentDecorator : public ShapeDecorator
{
virtual void save() = 0;
virtual void rotate()
{
  save();
  ShapeDecorator::rotate();
};
};


Мне просто нужно добавить в каждый из наследников Shape методы save и load, которые будут реализованы ПО РАЗНОМУ для каждого из этих наследников.
Re[3]: Мне кажется что декоратор не поможет
От: Gaperton http://gaperton.livejournal.com
Дата: 12.09.05 14:44
Оценка:
Здравствуйте, dormouse, Вы писали:

D>Нужно не декорировать существующие методы, а добавить новую функциональность. Кроме того, как быть в таком случае —

D>пусть Shape из предыдущего примера является базовым классом компоновщика,
D>то есть у нас есть:

Если не стоит цели максимально запутать простую вещь, то я бы посоветовал просто добавить к Shape методы save и load. Работать будет совершенно также . Если надо, что далеко не факт, то пронаследуй Shape от интерфейса ISerializable. И все. Не надо мудрить.
Re: Подскажите пожалуйста паттерн
От: ZevS Россия  
Дата: 12.09.05 15:04
Оценка:
Вообще то применение / не применение любого паттерна зависит от целей / ограничений архитектуры. Короче — если не знаешь, зачем нужен паттерн — не применяй вообще.
Re: Подскажите пожалуйста паттерн
От: Хитрик Денис Россия RSDN
Дата: 12.09.05 15:05
Оценка: +1
Здравствуйте, dormouse, Вы писали:

D>Уже не в первый раз сталкиваюсь с необходимостью написать параллельную иерархию классов.

D> ...
D>Можно ли это сделать лучше? Как вы поступаете в таких случаях?

Может, Visitor
Правила нашего с вами форума.
Как правильно задавать вопросы. © 2001 by Eric S. Raymond; перевод: © 2002 Валерий Кравчук.
Re: Подскажите пожалуйста паттерн
От: GlebZ Россия  
Дата: 12.09.05 15:20
Оценка:
Здравствуйте, dormouse, Вы писали:

D>Можно ли это сделать лучше? Как вы поступаете в таких случаях?


class Shape 
{
    virtual Serializator* GetSerializator(){return static_cast<Serializator>(new ShapeSerializator(this));};
};
class Circle : public Shape
{
    virtual Serializator* GetSerializator(){return static_cast<Serializator>(new CicleSerializator(this));};
};
class Ellipse : public Shape
{
    virtual Serializator* GetSerializator(){return static_cast<Serializator>(new ShapeSerializator(this));};
}; 

class Serializator
{
public:
void load()=0;
void save()=0;
}
class ShapeSerializator:public Serializator
{
public:
void load(){};
void save(){};
ShapeSerializator(Shape* shp){};

}
class CircleSerializator:public Serializator
{
public:
void load(){};
void save(){};
CicleSerializator(Cicle* shp){};
}
....

Я делаю примерно так.

С уважением, Gleb.
Re: Подскажите пожалуйста паттерн
От: Козьма Прутков Россия  
Дата: 12.09.05 15:27
Оценка:
> Проще объяснить на конкретном примере. Пусть есть:
>
> class Shape {};
> class Circle : public Shape{};
> class Ellipse : public Shape{}; 
>

>
> Допустим, мы хотим сохранять каждый класс в базе данных, то есть добавить методы
> save() и load().
> Можно ли это сделать лучше? Как вы поступаете в таких случаях?
А почему бы не отделить мух от котлет и не заморачивать класс Shape подробностями персистенции? Пусть живут сами по себе, а некие другие классы, типа mapper'ы какие-нить, будут заниматься их персистентностью?
Либо сделать нечто аналогичное уже предложенному, то есть придумать интерфейс IPersistent с парой методов save/load и его реализовать.
Просто если идти путем Shape-Circle-PersistentCircle, то отнаследовавшись от Circle придется дублировать код из PersistentCircle по сохранению свойств базового класса, а это не есть гуд, либо извращаться как-то использованием PersistentCircle — лишняя по сути зависимость.
Posted via RSDN NNTP Server 1.9
Да хранит вас господь в сухом прохладном месте...
Re[2]: Спасибо, в данном случае согласен
От: dormouse  
Дата: 12.09.05 15:37
Оценка:
Но.. может я не совсем удачный пример привел.. Попробую переформулировать:
Допустим, мы пишем библиотеку, в которой есть абстрактный класс


class Painter
{
  virtual void drawCircle( Circle * ) = 0;
  virtual void drawEllipse( Ellipse * ) = 0;
};


Мы реализовали этот интерфейс для окон
class WidgetPainter : public Painter {};
принтера
class PrinterPainter : public Painter {};
и так далее.
Пусть Вася Пупкин, который пользуется нашей либой, хочет расширить наш интерфейс:
class ExtendedPainter : public Painter
{
virtual void drawCat( Cat * ) throw() = 0;
};

Затем он должен реализовать его для окон, принтера и так далее, но у него нет никакого желания писать каждый раз:
class ExtendedWidgetPainter : public Painter
{
virtual void drawCircle( Circle * c )
{
painter_ ->drawCicle(c);
}
WidgetPainter * painter_;
};
Возникает жаление сделать так:
class ExtendedWidgetPainter : public Painter, public WidgetPainter
{};
чтобы унаследовать и интерфейс, и подходящую ему реализацию, но в этом случае необходимо виртуальное наследование. Можно, конечно, не наследовать ExtendedWidgetPainter от Painter'а — но в этом случае придется постоянно использовать cast'ы, либо же я чего-то не понимаю
На месте этого Painter'а, кстати, может быть Ваш Serializator.
Re[2]: Согласен, я слегка переформулировал вопрос
От: dormouse  
Дата: 12.09.05 15:38
Оценка:
http://www.rsdn.ru/Forum/Message.aspx?mid=1378232&amp;only=1
Автор: dormouse
Дата: 12.09.05
Re[3]: [patch] Должно быть так:
От: dormouse  
Дата: 12.09.05 15:48
Оценка:
Но.. может я не совсем удачный пример привел.. Попробую переформулировать:
Допустим, мы пишем библиотеку, в которой есть абстрактный класс

class Painter
{
  virtual void drawCircle( Circle * ) = 0;
  virtual void drawEllipse( Ellipse * ) = 0;
};


Мы реализовали этот интерфейс для окон
class WidgetPainter : public Painter {};
принтера
class PrinterPainter : public Painter {};
и так далее.
Пусть Вася Пупкин, который пользуется нашей либой, хочет расширить наш интерфейс:
class ExtendedPainter : public Painter
{
virtual void drawCat( Cat * ) throw() = 0;
};

Затем он должен реализовать его для окон, принтера и так далее, но у него нет никакого желания писать каждый раз:

class ExtendedWidgetPainter : public ExtendedPainter
{
virtual void drawCircle( Circle * c )
{
painter_ ->drawCicle(c);
}
WidgetPainter * painter_;
};


Возникает жаление сделать так:
class ExtendedWidgetPainter : public ExtendedPainter, public WidgetPainter
{};
чтобы унаследовать и интерфейс, и подходящую ему реализацию, но в этом случае необходимо виртуальное наследование. Можно, конечно, не наследовать ExtendedPainter от Painter'а — но в этом случае придется постоянно использовать cast'ы, либо же я чего-то не понимаю
На месте этого Painter'а, кстати, может быть Ваш Serializator.
Re[4]: [patch] Должно быть так:
От: GlebZ Россия  
Дата: 12.09.05 16:15
Оценка:
Здравствуйте, dormouse, Вы писали:

D>Возникает жаление сделать так:


D>class ExtendedWidgetPainter : public ExtendedPainter, public WidgetPainter
D>{};


Нормальное желание. Это у нас как Painter(по старому) так и ExtendedPainter
D>чтобы унаследовать и интерфейс, и подходящую ему реализацию, но в этом случае необходимо виртуальное наследование. Можно, конечно, не наследовать ExtendedPainter от Painter'а — но в этом случае придется постоянно использовать cast'ы, либо же я чего-то не понимаю
D>На месте этого Painter'а, кстати, может быть Ваш Serializator.
В данном случае, как раз описан недостаток паттерна Visitor. В частности то, что он хреново расширяется. Приходится писать практически во все классы. Но я не вижу особо в этом смысл. Легче написать:

class Cicle
{
Painter* GetPainter(bla-bla){static_cast<Painter>(new ExtendedWidgetPainter (....};
ExtendedPainter* GetExtendedPainter(bla-bla){static_cast<ExtendedPainter>(new ExtendedWidgetPainter (....}
}

Это более полезно, так как вам не придется модифицировать код который уже пользуется Painter. И вы можете реализовать GetExtendedPainter только для тех объектов которые вам нужны. Что именно будет браться Painter или ExtendedPainter решает вызывающий код. Смотря какая функциональность ему нужна. Старые вызовы, могут так и не узнать о расширенной функциональности.
Правда тут нужно иметь ввиду, как логически связаны функциональности Painter и ExtendedPainter. Если это разные вещи, то и смешивать их не стоит, потому как могут произойти изменения в которых придется вымарать Painter из ExtendedPainter. А это будет проблематично.

С уважением, Gleb.
Re: Подскажите пожалуйста паттерн
От: bayda Украина  
Дата: 13.09.05 11:47
Оценка:
Здравствуйте, dormouse, Вы писали:

D>Уже не в первый раз сталкиваюсь с необходимостью написать параллельную иерархию классов.

D>Проще объяснить на конкретном примере. Пусть есть: ..........................

ИМХО — Visitor.

Добавь у каждого класса по методу Accept который будет принимать некий абстрактный Visitor,
у этого визитора должны быть на каждый класс по методу.
и твой Accept будет вызывать подходящий.
Для, например, сохранения ты сможешь в цикле бежать по всем фигурам,
и вызывать Accept передавая Visistor предназначеный для сохраненния.

Наверно я путанно объяснил, почитай лучше о нем гдето (например у Andrei Alexandrescu)
Re: Подскажите пожалуйста паттерн
От: xtile  
Дата: 13.09.05 13:58
Оценка:
Здравствуйте, dormouse, Вы писали:

D>Уже не в первый раз сталкиваюсь с необходимостью написать параллельную иерархию классов.

D>Проще объяснить на конкретном примере. Пусть есть:
D>
D>class Shape {};
D>class Circle : public Shape{};
D>class Ellipse : public Shape{}; 
D>


D>Допустим, мы хотим сохранять каждый класс в базе данных, то есть добавить методы

D>save() и load().
D>В голову не приходит ничего кроме:
D>
D>class Shape {};
D>class Cicle : virtual public Shape{};
D>class Ellipse : virtual public Shape{};
D>class PersistentShape : virtual public Shape {
D>    virtual void load() = 0;
D>    virtual void save() = 0;
D>};
D>class PersistentCicle : public Cicle, public PersistentShape
D>{};
D>class PersistentEllipse : public Ellipse, public PersistentShape
D>{};
D>


D>Можно ли это сделать лучше? Как вы поступаете в таких случаях?


Используем сериализацию. Оператор сериализации генерит макрос из описания полей класса.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.