Ereditarietà, comandi e sourcing di eventi

5

Al fine di non ripetere le cose più volte volevo prendere in considerazione elementi comuni. Per esempio, diciamo che abbiamo una mucca e un cavallo. La mucca produce latte, il cavallo corre veloce, ma entrambi mangiano erba.

public class Herbivorous
{
   protected int Quantity;

   public void EatGrass(int quantity)
   {
      var evt= Build.GrassEaten
                    .WithQuantity(quantity);
      RaiseEvent(evt);
   }

   public void Apply(GrassEaten evt)
   {
       _quantity= evt.Quantity;
   }
}

public class Horse : Herbivorous
{
   private bool _HasFastRun;

   public void RunFast()
   {
      var evt= Build.FastRun;
      RaiseEvent(evt);
   }

   public void Apply(FastRunevt)
   {
       _HasFastRun= true;
   }
}

public class Cow: Herbivorous
{
   private bool _IsMilkProduced;

   public void ProduceMilk()
   {
      var evt= Build.MilkProduced;
      RaiseEvent(evt);
   }

   public void Apply(MilkProduced evt)
   {
       _IsMilkProduced= true;
   }
}

Per mangiare Grass, la mia applicazione riceve un comando in Json o xml o qualsiasi altra cosa che deserializza in questa classe:

namespace Herbivorous
{
   public class EatGrass : CommandBase
   {
      public Guid IdHerbivorous {get; set;}
      public Guid CommitId {get; set;}
      public long Version {get; set;}
      public int Quantity {get; set;}
   }
}

Il gestore comandi dovrebbe essere:

public class EatGrassHandler : CommandHandler<EatGrass>
{
   public override CommandValidation Execute(EatGrass cmd)
   {
      Contract.Requires<ArgumentNullException>(cmd != null);
      Herbivorous herbivorous= EventRepository.GetById<Herbivorous>(cmd.Id);
      if (herbivorous.IsNull())
         throw new AggregateRootInstanceNotFoundException();
      herbivorous.EatGrass(cmd.Quantity);
      EventRepository.Save(herbivorous, cmd.CommitId);
   }
}

Fin qui tutto bene. Prendo un oggetto erbivoro, ho accesso alla funzione EatGrass, non importa se sia un cavallo o una mucca. L'unico problema è qui:

EventRepository.GetById<Herbivorous>(cmd.Id)

In effetti, immaginiamo di avere una mucca che ha prodotto latte durante la mattinata e ora vuole mangiare erba. EventRepository contiene un evento MilkProduced, quindi viene il comando EatGrass. Con CommandHandler, non siamo più in presenza di una mucca e gli erbivori non sanno nulla della produzione di latte. cosa dovrebbe fare?

Devo avere un comando contestuale esplicito come:

namespace Herbivorous.Cow
    {
       public class EatGrass : CommandBase
       {
          public Guid IdHerbivorous {get; set;}
          public Guid CommitId {get; set;}
          public long Version {get; set;}
          public int Quantity {get; set;}
       }
       public class ProduceMilk : CommandBase
       {
          public Guid IdHerbivorous {get; set;}
          public Guid CommitId {get; set;}
          public long Version {get; set;}
       }
    }

Ciò significherebbe che la componente esterna che chiede al mio erbivoro di mangiare erba dovrebbe sapere che in questo contesto limitato parliamo di una mucca. nel precedente comando, stavamo parlando di un comportamento erbivoro generale, quindi il contesto della mia chiamata non era importante. Sapevamo che avevamo bisogno di erbivori per mangiare l'erba che era tutto.

Questo è il mio problema principale possibile perdita di dominio specifico da un contesto limitato a un altro. In realtà potrebbe anche significare che non posso supportare l'astrazione quando lavoro su interfacce tra diverse applicazioni. Mi chiedo ...

O potrei semplicemente accettare qualsiasi evento per ricostruire il mio erbivoro. Significa che se non trova un metodo per applicare questo evento a, andrà bene cercando di applicare il prossimo del suo stream. Questa è la vera soluzione semplice, ma non mi mette a mio agio sapere che gli eventi potrebbero non essere applicati (e non produrre errori) durante la reidratazione del mio oggetto. (In realtà più ci penso meno mi sento in colpa ..)

Grazie per il tuo aiuto, sto solo iniziando con questo tipo di problemi e sarei lieto di ricevere notizie da qualcuno più esperto.

    
posta Arthis 29.08.2012 - 16:55
fonte

2 risposte

1

Per prima cosa, aggiungi un metodo virtuale Execute(cmd) al tuo Herbivorous e inserisci il codice per l'invio dei comandi ai metodi di comando presenti. Quindi ciascuna sottoclasse di Herbivorous può sovrascrivere quel metodo e supportare tutti i metodi di comando specifici per la sottoclasse. Dovrebbe assomigliare in questo modo:

public class Cow: Herbivorous
{
   public override void Execute(Command cmd)
   {
      if(cmd is ProduceMilkCommand)
          ProduceMilk(cmd);  // execute specific command
      else
          base.Execute(cmd); // delegate general commands to the base class
   }
}

Per creare un nuovo oggetto da un dato comando, aggiungi un campo System.Type al tuo cmd e usa reflection per creare un'istanza di quel sottotipo specifico di Herbivorous . Oppure usa il modello prototipo per creare una nuova istanza (il tuo comando deve invece memorizzare un riferimento all'oggetto prototipo). Una terza alternativa è usare il schema di fabbrica astratto , il tuo comando quindi dovrà memorizzare un riferimento al sottotipo di fabbrica corretto.

Certo, immagino di poterti dare una risposta migliore se ci avessi mostrato come appare la classe di comando e come vengono creati i comandi. E il tuo uso del comando "var" nei frammenti di codice nasconde alcuni dettagli che potrebbero essere importanti per una risposta più precisa.

    
risposta data 29.08.2012 - 17:20
fonte
0

Va bene che la tua classe erbivora salti gli eventi che non capisce.

Ho imparato molto su questo e sui problemi correlati in questa discussione .

Spero che aiuti. Vorrei che ci fossero mucche e cavalli nel mio dominio !!

    
risposta data 14.08.2013 - 06:01
fonte

Leggi altre domande sui tag