很抱歉这些冗长的例子,但我最近遇到过这种类型的软件设计问题,并且一直在思考它。不确定是否有一个术语,所以我将给出两个一般的例子来提出一个想法:
前1:你正在制作一个RPG游戏,你有一个主要角色的课程。角色对游戏世界的反应会根据你穿着/持有的技能,分配的技能,基本上是对象的内部状态而改变。
假设你在游戏中有以下项目:
Ex 2:你有一辆丰田卡罗拉。它有不同的配置,但它们都是丰田花冠。 ABS和牵引力控制是您可以添加到基线模型的可选功能(行为)。
两个例子的共同属性:
潜在的解决方案(但不充分):
if statement:
if ch.isWearing 'doofy glasses':
render with doofy shader
else if ch.isWearing ...
不起作用。需要为每个部分添加一个子句。班级可以非常快速地变得大而复杂。
策略模式:
class TractionControlStrategy
class ABSstrategy
class Toyota:
TractionControlStrategy tcs
ABSstrategy abs
run():
if tcs not null:
tcs.run()
if abs not null:
abs.run()
carWithTCS = new Toyota(new TractionControlStrategy())
没有比之前的解决方案好多了,因为你仍然有很长的if语句列表
带子类的策略:
class Toyota:
run():
// does nothing
class ToyotaWithABS : Toyota
ABSstrategy abs = new ABSstrategy()
run():
abs.run
class ToyotaWithTCS : Toyota ...
我认为满足开放/封闭原则。可能比前一个好吗?但是现在你必须为每个配置组合创建一个类。如果你后来发现还有其他可选功能,那么你需要实现的每个功能的类数都会增加一倍...
如何使用OOP对这些类型的交互和行为进行建模?什么设计模式或设计模式的组合促进了这种事情?
真的不确定这是一个好问题,还是我清楚我要问的是什么,因为我从来没有真正实践过良好的软件设计。
我正在学习OpenGL,正在研究我的3D网格/模型类。这个问题是相关的,因为在我的渲染器中,索引和纹理对于网格是可选的。因此,网格可以只是顶点,索引顶点,顶点和纹理,或全部3.另外,我无法预见未来我可能想要添加哪些功能,因为我不知道我将学习什么一个月下来,所以我需要这个类灵活,可扩展
答案 0 :(得分:1)
你是对的,你需要描述所有可能组合的任何选项(无论是通过switch / if还是通过类层次结构)都不好。
一种方法是使用decorator pattern来包装主类并添加动态属性。
或者您可以将单独的Stats
类作为主类中的字段并使用其他项目进行装饰。
class Thing
BasicStats stats
constructor()
this->stats = new BasicStats()
addItem(Item item)
// Decorate current stats with new stats
item->setComponent(this->stats)
this->stats = item
return this
int getHealth()
return this->stats->getHealth()
你可以像这样使用它:
thing = new Thing() // has basic stats
thing->addItem(new MagicMirror)->addItem(new SilverBullet)
// will go through the chain of decorators to get the value
health = thing->getHealth()
另一种方法是在主类中列出动态选项(或项目):
class Thing
Stats stats
ItemList items
updateStats()
for item in this->items
item->updateStats(this->stats)
// OR if we want to not disclose the stats to items
// we can pass this and items should use
// character's methods to change stats
item->updateStats(this)
add(Item item)
this->items->append(item)
return this
可以这样使用:
thing = new Thing()
thing->add(new MagicMirror())->add(new RingOfRegeneration)->updateStats()
如果您不想直接更改角色统计数据,Memento也很有用。例如,如果您有一些“比较”功能,用户(或玩家)可以组合不同的项目集来查看影响,然后“应用”它们。
同时查看chain-of-responsibility,类似于统计列表选项 - 您可以创建一个“项目”链并从此链请求统计信息。就像你可以从传递基础健康值开始,然后由每个项目转换,你将在链的末尾获得“升级”值。
更新:还有一个想法,visitor也很有用:
# Base class for stats, Element
class Stat
abstract accept(StatVisitor visitor)
class Health extends Stat
private int health
accept(StatVisitor visitor)
# or you can have visitor->visitHealth(this)
visitor->visit(this)
multiply(int mult)
this->health = this->health * mult
class Strength extends Stat
private int strength
accept(StatVisitor visitor)
visitor->visit(this)
add(int strength)
this->strength = this->strength + strength
统计数据是访客模式的“元素”。 Thing
类代表“客户”:
# Thing contains stats, this is Client
class Thing
StatsList stats
accept(StatsVisitor visitor)
for stat in this->stats
stat->visit(visitor)
“访客”是我们的项目,可以修改统计数据:
# Base visitor class
class StatVisitor
abstract visit(Health health)
abstract visit(Strength strength)
class MagicMirror
# magic mirror multiplies health by 10
visit(Health health)
health->multiply(10)
# magic mirror increases strength +5
visit(Strength strength)
strength->add(5)
现在你可以这样做:
thing = new Thing()
item = MagicMirror()
# now update all the stats with MagicMirror
thing->accept(item)
答案 1 :(得分:0)
不要过分复杂化:
再生之戒:让你的角色随着时间的推移重生健康
这根本不应该修改你的对象。例如。打开戒指可以添加一个试图增加实体健康状况的计时器(即佩戴者)并取消戒指再次移除该计时器。
偷偷摸摸的运动鞋:增加潜行
这可以建模为统计数据的修改:
class Entity
Map<Skill, SortedList<Modifier>> skillModifiers
getSneakChance()
sneakChance = .. // compute base value from attributes
for each mod in skillModifiers[SneakChance]
sneakChance = mod.apply(sneakChance)
return sneakChance
地图避免了装饰和责任链的两个缺点:并非每个修饰符都会为评估添加另一个间接,您可以使用更简单的基于值的修饰符(将值增加x%)而不是推动修改统计/技能的逻辑或如何将其访问到修改器中。
一个难点在于决定应用修饰符的顺序:如果你首先在你的攻击伤害上加20,然后再增加5%或者反过来,则会失去1点伤害点?某些修饰符是否仅适用于基值,即您是否需要同时传递mod.apply
中的累计值和基值?
魔镜:反映传入伤害的百分比
与2.相同。此时间图修饰符和对特定事件的反应(即受到伤害)。这允许将相同的效果应用于多个实体,如光环或其他一些aoe法术。
Doofy眼镜:突出制作资源
这又是一个不会影响实体的东西。 gui-controller需要这种信息,因此只需向玩家查询视觉修饰符(除非游戏中的每个实体都需要操纵你的gui的行为和外观)。