Home > Blockchain >  How to use polymorphism in anemic domain model design?
How to use polymorphism in anemic domain model design?

Time:05-23

Now i am working on a mmorpg server,Let's talk one scene that player drop something from inventory to world

If I design the Drop with rich domain model,i will create code like this

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...

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.

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

CodePudding user response:

One way to get rid of those if/else chains: double dispatch

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.

Downside: What people consider to be a code smell is that DropService now needs to know every implementation of IDropable. So anytime a new class implements IDropable, you also need to change 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 ...

CodePudding user response:

One approach could be to treat game objects more as database-entries than actual .net types. 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. So your object might look something like

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. Some class would need to define what each of these effects actually does, but there might be much fewer effects than game objects. Now your player would just need to call item.OnEvent(EventId.Drop), and that would trigger any associated behaviors.

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.

  • Related