简体   繁体   English

如何在贫血域 model 设计中使用多态性?

[英]How to use polymorphism in anemic domain model design?

Now i am working on a mmorpg server,Let's talk one scene that player drop something from inventory to world现在我在一个 mmorpg 服务器上工作,让我们谈谈玩家从库存到世界的场景

If I design the Drop with rich domain model,i will create code like this如果我设计具有富域 model 的 Drop,我将创建这样的代码

class Player {
    void Drop(IDropable dropable,vector3 pos){
        RemoveFromInventory();
        dropable.BeDroped(pos);
        Net.Broadcast(pos);
    }
}

As you can see,the dropable will be called BeDroped(pos),and each entity which implement IDropable will do something special,for example,a weapon be drop,it will hit somebody;a magic ball be dropped,the player's heath point will decrease...如你所见,dropable 将被称为 BeDroped(pos),每个实现 IDropable 的实体都会做一些特殊的事情,例如,武器掉落,它会击中某人;魔法球掉落,玩家的健康点将减少...

but if I want to design by anemic domain model,how could I use polymorphism for each IDropable?here is the example,the way i can achieve is using if else or switch,but I know it is stupid.但是如果我想通过贫血域 model 进行设计,我如何为每个 IDropable 使用多态性?这是示例,我可以实现的方法是使用 if else 或 switch,但我知道这是愚蠢的。

class DropService {
    void RemoveFromPlayerInventory(Player player){
        //.....
    }
    void Drop(Player player, IDropable dropable,vector3 pos){
        RemoveFromPlayerInventory(player);
        if(dropable is Weapon)
            OnDropWeapon(dropable,pos);
        else if(dropable is magicball)
            OnDropMagicBall(dropable,pos);
        //.....
        Net.Broadcast(pos);
    }
}

One way to get rid of those if/else chains: double dispatch摆脱那些 if/else 链的一种方法:双重分派

Example:例子:

class DropService:IDropService {
    void RemoveFromPlayerInventory(Player player){
        //.....
    }
    void Drop(Player player, IDropable dropable,vector3 pos){
        RemoveFromPlayerInventory(player);
        dropable.BeDropped(this, pos);
        //.....
        Net.Broadcast(pos);
    }

    void IDropService.Drop(Weapon item, Vector3 pos){ /*impl.*/ }
    void IDropService.Drop(Magicball item, Vector3 pos){ /*impl.*/ }
}

public interface IDropable
{
    void Bedropped(IDropService dropper, Vector3 pos);
}

public interface IDropService
{
    void Drop(Weapon item, Vector3 pos);
    void Drop(Magicball item, Vector3 pos);
}

public class Weapon: IDropable
{
    public override void BeDropped(IDropService dropper, Vector3 pos)
    {
        dropper.Drop(this, pos); // Will automagically call the right overload
    }
}

// Same for Magicball

As you see, there is no more if/else or switch.如您所见,不再有 if/else 或 switch。

Downside : What people consider to be a code smell is that DropService now needs to know every implementation of IDropable.缺点:人们认为是代码味道的是 DropService 现在需要知道 IDropable 的每个实现。 So anytime a new class implements IDropable, you also need to change DropService.因此,只要新的 class 实现了 IDropable,您还需要更改 DropService。

To avoid this, you could consider a variety of possible patterns.为避免这种情况,您可以考虑多种可能的模式。 Like several Factory patterns (Factory that creates an appropriate DropBehavior for example?), (Drop-)Strategy pattern...像几个工厂模式(例如创建适当的 DropBehavior 的工厂?),(Drop-)策略模式......

One approach could be to treat game objects more as database-entries than actual .net types.一种方法可能是将游戏对象更多地视为数据库条目,而不是实际的 .net 类型。 So you could for example create an game object in an editor, assign it some graphics, and one or more effects that should happen on some event.例如,您可以在编辑器中创建一个游戏 object,为其分配一些图形,以及一个或多个应该在某个事件上发生的效果。 So your object might look something like所以你的 object 可能看起来像

private EffectManager effectManager;
private Dictionary<EventId, EffectId> effectsOnEvents= new ();
public void OnEvent(EventId eventId) {
    if(effectsOnEvents.TryGet(eventId, out var effectId){
        effectManager(this, effectId);
    }
}

So a weapon would have the DropWeapon effect attached to the Drop event, a magic ball would have the DropMagicBall effect attached and so on.因此,武器将具有附加到Drop事件的DropWeapon效果,魔法球将具有附加的DropMagicBall效果等等。 Some class would need to define what each of these effects actually does, but there might be much fewer effects than game objects.有些 class 需要定义每个效果的实际作用,但效果可能比游戏对象少得多。 Now your player would just need to call item.OnEvent(EventId.Drop) , and that would trigger any associated behaviors.现在您的玩家只需要调用item.OnEvent(EventId.Drop) ,这将触发任何关联的行为。

You could also for example add support for multiple scripts to allow things like a magic ball weapon that both damages and heals.例如,您还可以添加对多个脚本的支持,以允许像魔法球这样的武器既能造成伤害又能治愈。 And add parameters to the script, so you can easily change the amount of damage dealt or hitpoints restored by updating a value in a database.并向脚本添加参数,这样您就可以通过更新数据库中的值轻松更改造成的伤害量或恢复的生命值。

I would highly recommend Eric Lippers articles on Wizards and warriors where he points out that using the type system to model domain behaviors can be problematic.我强烈推荐 Eric Lippers 关于Wizards and warriors 的文章,他在文章中指出将类型系统用于 model 域行为可能会出现问题。

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

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