Herança, comandos e fornecimento de eventos

5

Para não refazer as coisas várias vezes, eu queria fatorar coisas comuns. Por exemplo, digamos que temos uma vaca e um cavalo. A vaca produz leite, o cavalo corre rápido, mas ambos comem grama.

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

Para comer Grass, meu aplicativo recebe um comando no Json ou xml, ou o que quer que seja desserializado para esta 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;}
   }
}

O manipulador de comandos deve ser:

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

até agora tudo bem. Eu recebo um objeto Herbívoro, tenho acesso à sua função EatGrass, seja um cavalo ou uma vaca, não importa realmente. O único problema está aqui:

EventRepository.GetById<Herbivorous>(cmd.Id)
Na verdade, vamos imaginar que temos uma vaca que produziu leite durante a manhã e agora quer comer grama. O EventRepository contém um evento MilkProduced e, em seguida, vem o comando EatGrass. Com o CommandHandler, não estamos mais na presença de uma vaca e o herbívoro não sabe nada sobre a produção de leite. o que deveria fazer?

Devo ter um comando contextual explícito como:

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

Isso significaria que o componente externo que pede aos meus herbívoros para comer capim deve saber que neste contexto delimitado falamos de uma vaca. no antigo comando, estávamos falando de um comportamento Herbívoro geral, de modo que o contexto da minha ligação não era importante. Nós sabíamos que precisávamos de herbívoros para comer grama que era tudo.

Este é o meu principal problema de possível vazamento de domínio específico de um contexto limitado para outro. Na verdade, pode significar também que não posso suportar abstração ao trabalhar em interfaces entre vários aplicativos. Eu me pergunto ...

Ou eu poderia apenas aceitar qualquer evento ao reconstruir meu herbívoro. Isso significa que, se não encontrar um método para aplicar este evento, ele vai muito bem tentando aplicar o próximo de seu fluxo. Esta é a verdadeira solução simples, mas não me deixa à vontade saber que eventos podem não ser aplicados (e não produzir erros) ao reidratar meu objeto. (Na verdade, quanto mais penso nisso, menos me sinto culpado ..)

Obrigado pela sua ajuda, estou apenas começando com esse tipo de problema, e ficaria feliz em receber algumas notícias de alguém mais experiente.

    
por Arthis 29.08.2012 / 16:55
fonte

2 respostas

1

Primeiro, adicione um método virtual Execute(cmd) ao seu Herbivorous e coloque o código para o envio dos comandos para os métodos de comando lá. Em seguida, cada subclasse de Herbivorous poderá substituir esse método e oferecer suporte a todos os métodos de comando específicos da subclasse. Deve parecer de alguma forma assim:

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

Para criar um novo objeto a partir de um determinado comando, adicione um campo System.Type ao seu cmd e use a reflexão para criar uma instância desse subtipo específico de Herbivorous . Ou use o padrão de protótipo para criar uma nova instância (seu comando precisa armazenar uma referência a esse objeto de protótipo, em vez disso). Uma terceira alternativa é usar o padrão de fábrica abstrato , então seu comando terá que armazenar uma referência ao subtipo de fábrica correto.

Claro, acho que poderia dar uma resposta melhor se você nos mostrasse como a classe de comando se parece e como os comandos são criados. E o uso do comando "var" nos trechos de código oculta alguns detalhes que podem ser importantes para uma resposta mais precisa.

    
por 29.08.2012 / 17:20
fonte
0

Tudo bem que sua classe herbívora ignore eventos que ela não entende.

Eu aprendi muito sobre isso e problemas relacionados em este tópico .

Espero que ajude. Eu gostaria que houvesse vacas e cavalos no meu domínio!

    
por 14.08.2013 / 06:01
fonte