pattern patrones patron estructural diseño c++ design-patterns include polymorphism decorator

patrones - Aplicando el patrón decorador de forma polimórfica y problemas de acoplamiento en C++



patron proxy (6)

¿Qué tal si Side produce el resultado decorado según el argumento de Terrain, en lugar de viceversa? Entonces Terrain solo necesitaría un método para indicar los Segmentos que necesita y su orientación podría organizarse por el Lado. Tal vez eso facilitaría el acoplamiento?

No relacionado con el acoplamiento, pero considere usar la programación genérica de forma más amplia en su diseño: no es obvio que los lados, los terrenos y / o los segmentos no deberían usar genericity en lugar de, o además, de herencia. En cierto sentido, es más una implementación que un problema de diseño, pero sí influye en el diseño. Lamentablemente, no creo entender el dominio de la aplicación lo suficiente como para ofrecer sugerencias más específicas (ya jugué a Carcassonne una vez, hace bastante tiempo, pero además de ser divertido, no recuerdo mucho ;-).

Estoy tratando de hacer una implementación en C ++ del juego de mesa Carcassonne . Estoy tratando de hacer un objeto de mosaico que tenga cuatro lados y uno de tres terrenos básicos (campo, camino, ciudad).

La mejor interfaz para la creación de mosaicos que podía pensar era de la forma:

City city; city_city_city_city = new Tile(city, city, city, city);

Donde una clase de Tile se define de la siguiente manera ...

class Tile { public: Tile(Terrain& top_terrain, Terrain& right_terrain, Terrain& bottom_terrain, Terrain& left_terrain) { top_side_.reset(top_terrain.Decorate(new TopSide())); right_side_.reset(right_terrain.Decorate(new RightSide()); bottom_side_.reset(bottom_terrain.Decorate(new BottomSide())); left_side_.reset(left_terrain.Decorate(new LeftSide())); } private: boost::scoped_ptr<TopSide> top_side_; boost::scoped_ptr<RightSide> right_side_; boost::scoped_ptr<BottomSide> bottom_side_; boost::scoped_ptr<LeftSide> left_side_; };

Quería que el constructor inicializara cada lado específico (TopSide, RightSide, BottomSide, LeftSide) que hereda del lado de la clase base. La idea era definir un método virtual Decorate en la clase Terrain que devolvería una instancia de un SideDecorator para el tipo específico de Terrain.

Dependiendo del tipo de terreno que tenga el lado, tendrá un número / tipo diferente de TerrainSegments. Por ejemplo: un lado con un campo tiene la necesidad de un solo FieldSegment, mientras que un lado con un camino necesita tres segmentos: un RoadSegment y dos FieldSegments. Por lo tanto, la adición de un mosaico al tablero necesitará lados con diferentes implementaciones y miembros.

Podría hacer clases concretas como TopFieldSide, BottomRoadSide, etc., pero pensé que el patrón del decorador sería más limpio. De lo que no estoy seguro es de si el uso polimórfico del método Decorate () es o no un uso indebido.

Ciertamente podría crear Tiles de la forma:

Tile(CityDecorator(new TopSide), CityDecorator(new RightSide), FieldDecorator(new BottomSide), RoadDecorator(new LeftSide));

Pero la versión anterior parece mucho más limpia.

Mi pregunta es ... ¿Es esto un enfoque aceptable o hay una manera más simple / limpia que me falta?

Mi uso de este enfoque me lleva a problemas de acoplamiento porque tengo que incluir la ruta a SideDecorator en Terrain y Terrain se usa en SideDecorator y en clases derivadas. La directiva simple #include "side_decorator.h" en el terreno.h causa muchos errores de compilación, lo que hace difícil saber si se trata de un problema de declaración directa o qué otra cosa no se ha detectado en mi código ...


Creo que tu solución está bien. De hecho, necesita un constructor virtual para crear el tipo correcto de decorador según el tipo que decora. Esto requiere un método de fábrica , que Decorate es. Oculta los detalles de qué decorador crear para un Tile , pero tus clases de Terrain deben saber que pueden decorarse.

Solo tendría un problema con este diseño si otras clases requieren su propio tipo especial de decoradores. Entonces agregar nuevos tipos y decorador se convertiría en un dolor. Pero para uno o dos casos bien definidos, su diseño está bien.


Otra posibilidad sería usar plantillas

template <class Top, class Right, class Bottom, class Left> class Tile { TopSide top; RightSide right; BottomSide bottom; LeftSide left; Tile() { // imaginary syntax to "decorate" a side top.type = new Top(); right.type = new Right(); bottom.type = new Bottom(); left.type = new Left(); } } Tile<City, Road, City, Field> tile();

La sintaxis sería muy clara, especialmente con la ayuda de typedefs que podría generar automáticamente, como por ejemplo:

typedef Tile<Field, Road, City, Road> Tile_F_R_C_R;

Por otro lado, esto podría generar una gran cantidad de código generado.


Una nota sobre su problema de dependencias circulares: puede usar la expresión PImpl, como se explica en este artículo de Guru Of The Week (otro ejemplo simple aquí ).


un mosaico tiene 4 lados, cada uno de los cuales es uno de campo, carretera, ciudad

class Side { // things common to all sides }; class Field : public Side { // things only found in a field }; class Road : public Side { // things only found in a road }; class City : public Side { // things only in a city }; class Tile { collection of 4 Sides; Tile(Side *top, Side *left, Side *right, Side *bottom); }; ... // ex. usage: Tile myTile( new Road, new City, new City, new Field );

¿Por qué esto no hace todo lo que quisieras? Nota: referencias, punteros, lo que sea, eso es detalle de implementación no importante para la pregunta de diseño. Creo que el patrón de diseño es excesivo


Podría hacer clases concretas como TopFieldSide, BottomRoadSide, etc., pero pensé que el patrón del decorador sería más limpio.

El patrón Decorator agrega complejidad que realmente solo vale la pena si tiene la intención de agregar nuevos decoradores pero no quiere modificar su aplicación principal. Si las reglas de tu juego son fijas (nunca puede haber otra cosa que las clases concretas que mencionas), entonces diría que la complejidad no dará resultado.

Si quieres aplicar Decorator para aprender o dominar el patrón, entonces ve a buscarlo. Pero tenga en cuenta que el problema que resuelve puede no estar presente en sus requisitos de diseño.