简体   繁体   English

多态地应用装饰器模式并在C ++中耦合问题

[英]Applying the decorator pattern polymorphically and coupling problems in C++

I am trying to make a C++ implementation of the board game Carcassonne . 我正在尝试制作卡尔卡松棋盘游戏的C ++实现。 I am trying to make a tile object which has four sides and one of three basic terrains(field, road, city). 我正在尝试制作一个具有四个边和三个基本地形之一(田地,道路,城市)的瓷砖对象。

The best interface for tile creation I could think of was of the form: 我能想到的最好的瓷砖创建界面是:

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

Where a Tile class is defined somewhat as follows... Tile类的定义如下......

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_;
};

I wanted the constructor to initialize each specific Side(TopSide, RightSide, BottomSide, LeftSide) that inherits from the base class Side. 我希望构造函数初始化从基类Side继承的每个特定Side(TopSide,RightSide,BottomSide,LeftSide)。 The idea was to define a virtual Decorate method in the Terrain class that would return an instance of a SideDecorator for the specific type of Terrain. 我们的想法是在Terrain类中定义一个虚拟Decorate方法,该方法将返回特定类型Terrain的SideDecorator实例。

Depending on the Type of terrain the side has, it will have a different number/type of TerrainSegments. 根据边的地形类型,它将具有不同数量/类型的TerrainSegments。 For example: A Side with a Field has the need for only one FieldSegment, whereas a Side with a Road needs three Segments: a RoadSegment and two FieldSegments. 例如:带有Field的Side只需要一个FieldSegment,而带Road的Side需要三个Segments:RoadSegment和两个FieldSegments。 Thus the adding of a tile to the board will need Sides with different implementations and members. 因此,向板添加块将需要具有不同实现和成员的Sides。

I could make concrete classes like TopFieldSide, BottomRoadSide, etc, but I figured the decorator pattern would be cleaner. 我可以制作像TopFieldSide,BottomRoadSide等具体的类,但我认为装饰器模式会更清晰。 The thing I am not sure about however is whether or not the polymorphic use of the Decorate() method is a misuse. 然而,我不确定的是,Decorate()方法的多态使用是否是滥用。

Certainly I could create Tiles of the form: 当然我可以创建形式的Tiles:

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

But the previous version seems much cleaner. 但以前的版本似乎更清洁。

My question being... Is this an acceptable approach or is there a simplier/cleaner way that I am missing? 我的问题是......这是一种可接受的方法,还是我缺少一种更简单/更清洁的方法?

My use of this approach is running me into coupling problems because I have to include the path to SideDecorator in Terrain and Terrain is used in SideDecorator and in derived classes. 我使用这种方法让我陷入耦合问题,因为我必须在SideDecorator和派生类中使用Terrain和Terrain中的SideDecorator路径。 The simple directive #include "side_decorator.h" in the terrain.h causes many compile errors making it hard to tell if it is a forward declaration problem or what something else unoticed in my code... 在terrain.h中使用简单的指令#include“side_decorator.h”会导致许多编译错误,这使得很难判断它是否是一个前向声明问题或者我的代码中有什么其他东西是什么...

What about having the Side produce the decorated result based on the Terrain argument, rather than viceversa? 那么让Side产生基于Terrain参数的装饰结果,而不是反之? Then Terrain would only need a method to indicate the Segments it needs and their orientation might be arranged by the Side. 然后Terrain只需要一种方法来指示它需要的分段,并且它们的方向可以由Side安排。 Maybe that would ease the coupling? 也许这会缓和耦合?

Unrelated to coupling, but consider using generic programming more widely in your design -- it's not obvious that sides, terrains and/or segments shouldn't use genericity instead of, or as well as, inheritance. 与耦合无关,但考虑在您的设计中更广泛地使用泛型编程 - 侧面,地形和/或段不应该使用通用性而不是继承性,也不应该使用通用性。 In a sense that's more of an implementation than a design issue, but it does influence design. 从某种意义上说,它更像是一个实现而不是一个设计问题,但它确实会影响设计。 Unfortunately I don't think I understand the application domain deeply enough to offer more specific suggestions (I did play Carcassonne once, quite a while ago, but besides it being fun I don't recall much;-). 不幸的是,我认为我对应用领域的理解不够深入,无法提供更具体的建议(很久以前我曾经玩过卡尔卡松一次,但除了它很有趣之外,我不记得太多了;-)。

I could make concrete classes like TopFieldSide, BottomRoadSide, etc, but I figured the decorator pattern would be cleaner. 我可以制作像TopFieldSide,BottomRoadSide等具体的类,但我认为装饰器模式会更清晰。

The Decorator pattern adds complexity that really only pays off if you intend to add new decorators but don't want to modify your main application. Decorator模式增加了复杂性,如果您打算添加新的装饰器但又不想修改主应用程序,那么这种复杂性实际上只会带来回报。 If the rules of your game are fixed (there can never be anything else but the concrete classes you mentioned), then I'd say the complexity won't pay off. 如果你的游戏规则是固定的(除了你提到的具体类别之外别无他法),那么我会说复杂性不会得到回报。

If you want to apply Decorator to learn or master the pattern, then go for it. 如果您想应用Decorator来学习或掌握模式,那就去吧。 But beware that the problem it solves may not actually be present in your design requirements. 但请注意,它解决的问题可能实际上并不存在于您的设计要求中。

关于你的循环依赖问题的注释:你可以使用PImpl习语,正如本周的Guru文章这里的另一个简单例子)中所解释的那样。

a Tile HAS 4 Sides, each of which IS one of Field, Road, City 瓷砖有4个边,每个边都是田野,道路,城市之一

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 );

Why does this not do all you would want? 为什么这不是你想要的全部? Note: references, pointers, whatever, that's implementation detail not important for the design question. 注意:引用,指针等等,这些实现细节对于设计问题并不重要。 I think the design pattern is overkill 我认为设计模式过度

Another possibility would be to use templates 另一种可能性是使用模板

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();

The syntax would be very clean, especially with the aid of typedefs that you could automatically generate, such as: 语法非常干净,特别是在你可以自动生成的typedef的帮助下,例如:

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

On the other hand, this could lead to a large amount of generated code. 另一方面,这可能导致大量生成的代码。

I think your solution is fine. 我认为您的解决方案很好。 Effectively you require a virtual constructor , to create the right type of decorator based on the type it is decorating. 实际上,您需要一个虚拟构造函数 ,根据它正在装饰的类型创建正确类型的装饰器。 This requires a factory method , which Decorate is. 这需要一个工厂方法 ,即Decorate It hides the details of which decorator to create for a Tile , but your Terrain classes have to know they can be decorated. 它隐藏了为Tile创建的装饰器的细节,但是你的Terrain类必须知道它们可以被装饰。

I would only have a problem with this design if other classes required their own special type of decorators. 如果其他类需要他们自己特殊类型的装饰器,我只会遇到这个设计的问题。 Then adding new types and decorator would become a pain. 然后添加新类型和装饰将成为一个痛苦。 But for one or two well defined cases your design is fine. 但对于一个或两个明确定义的案例,您的设计很好。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM