我正在尝试制作棋盘游戏Carcassonne的C ++实现。我正在尝试制作一个具有四个边和三个基本地形之一(场地,道路,城市)的瓷砖对象。
我能想到的瓷砖创建的最佳界面是:
City city;
city_city_city_city = new Tile(city, city, city, city);
凡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_;
};
我希望构造函数初始化从基类Side继承的每个特定Side(TopSide,RightSide,BottomSide,LeftSide)。我们的想法是在Terrain类中定义一个虚拟的Decorate方法,该方法将为特定类型的Terrain返回SideDecorator的实例。
根据边的地形类型,它将具有不同数量/类型的TerrainSegments。例如:带有Field的Side只需要一个FieldSegment,而带Road的Side需要三个Segments:RoadSegment和两个FieldSegments。因此,向板添加块将需要具有不同实现和成员的Sides。
我可以制作像TopFieldSide,BottomRoadSide等具体的类,但我认为装饰器模式会更清晰。然而,我不确定的是,Decorate()方法的多态使用是否是滥用。
当然,我可以创建以下形式的Tiles:
Tile(CityDecorator(new TopSide),
CityDecorator(new RightSide),
FieldDecorator(new BottomSide),
RoadDecorator(new LeftSide));
但之前的版本似乎更清晰。
我的问题是......这是一种可接受的方法,还是我缺少一种更简单/更清洁的方式?
我使用这种方法让我陷入耦合问题,因为我必须在SideDecorator和派生类中使用Terrain和Terrain中的SideDecorator路径。在terrain.h中使用简单的指令#include“side_decorator.h”会导致许多编译错误,这使得很难判断它是否是一个前向声明问题,或者我的代码中有什么其他东西是什么......
答案 0 :(得分:1)
让Side产生基于Terrain参数的装饰结果,而不是反之?然后Terrain只需要一种方法来指示它需要的分段,并且它们的方向可以由Side安排。也许这会缓和耦合?
与耦合无关,但考虑在您的设计中更广泛地使用泛型编程 - 侧面,地形和/或段不应该使用通用性而不是继承使用通用性并不明显。从某种意义上说,它更像是一个实现而不是一个设计问题,但它确实会影响设计。不幸的是,我认为我对应用领域的理解不够深入,无法提供更具体的建议(很久以前我曾经玩过卡尔卡松一次,但除了它很有趣之外我不记得了很多; - )。
答案 1 :(得分:0)
关于循环依赖问题的注释:您可以使用PImpl惯用法,如此Guru Of The Week article(另一个简单示例here)中所述。
答案 2 :(得分:0)
Tile HAS 4 Sides,每个都是Field,Road,City
之一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 );
为什么这不是你想要的全部? 注意:引用,指针等等,这些实现细节对于设计问题并不重要。我认为设计模式过度
答案 3 :(得分:0)
另一种可能性是使用模板
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();
语法非常简洁,特别是在你可以自动生成的typedef的帮助下,例如:
typedef Tile<Field, Road, City, Road> Tile_F_R_C_R;
另一方面,这可能会导致大量生成的代码。
答案 4 :(得分:0)
我认为您的解决方案很好。实际上,您需要一个虚拟构造函数,以根据它正在装饰的类型创建正确类型的装饰器。这需要工厂方法,Decorate
。它隐藏了为Tile
创建的装饰器的详细信息,但是您的Terrain
类必须知道它们可以被装饰。
如果其他类需要自己特殊类型的装饰器,我只会遇到此设计的问题。然后添加新类型和装饰将成为一个痛苦。但是对于一个或两个定义明确的案例,你的设计很好。
答案 5 :(得分:0)
我可以制作像TopFieldSide,BottomRoadSide等具体的类,但我认为装饰器模式会更清晰。
装饰器模式增加了复杂性,如果您打算添加新的装饰器但又不想修改主应用程序,那么这种复杂性实际上只会得到回报。如果你的游戏规则是固定的(除了你提到的具体类别之外别无他法),那么我会说复杂性不会得到回报。
如果您想应用Decorator来学习或掌握模式,那就去吧。但请注意,它解决的问题可能实际上并不存在于您的设计要求中。