3 Stimmen

Implementieren einer Fabrik, die anhand von Spezifikationen bestimmt, welcher Objekttyp erstellt werden soll

Dies ist hauptsächlich ein Gedankenexperiment. Das alles ist also Beispielcode. Mein Ziel war es, das Spezifikationsmuster zu verwenden, um riesige Blöcke von bedingtem Code innerhalb einer Fabrik zu vermeiden. In diesem Beispiel habe ich also ein StatusData-Objekt, für das ich eine Implementierung von IStatusUpdate finden möchte, die für dieses Objekt geeignet ist.

Ich habe die folgende Reihe von Tests:

    [TestMethod]
    public void Factory_Interface_Should_Return_IStatusUpdate()
    {
      var factory = MockRepository.GenerateMock<IUpdateFactory<StatusData>>();
      var obj = MockRepository.GenerateStub<IStatusUpdate>();

      var data = new StatusData();
      factory.Stub(x => x.Get(data)).Return(obj);

      var item = factory.Get(data);

      Assert.IsInstanceOfType(item, typeof(IStatusUpdate));
    }

    [TestMethod]
    public void StatusUpdateFactory_Should_Return_IStatusUpdate()
    {
      var factory = new StatusUpdateFactory();
      var data = new StatusData();

      var item = factory.Get(data);

      Assert.IsInstanceOfType(item, typeof(IStatusUpdate));   
    }

    [TestMethod]
    public void StatusUpdateFactory_Should_Return_NewStatusUpdate_When_Status_Is_New()
    {
      var data = new StatusData(Status.New);
      var factory = new StatusUpdateFactory();

      var item = factory.Get(data);

      Assert.IsInstanceOfType(item, typeof(NewStatusUpdate));
    }

Meine bisherige Implementierung von Factory sieht folgendermaßen aus:

public class StatusUpdateFactory:IUpdateFactory<StatusData>
  {
    public IStatusUpdate Get(StatusData item)
    {
      IList<ISpecification<StatusData>> specs = GetSpecifications();

      foreach (var spec in specs)
      {
        if (spec.IsSatisfiedBy(item))
          //how do I do this?
           return new NewStatusUpdate();

      }
      return null;
    }

    private IList<ISpecification<StatusData>> GetSpecifications()
    {
      var returnList = new List<ISpecification<StatusData>>();
      var specTypes = this.GetType().Assembly.GetTypes()
                        .Where(z => z.IsInstanceOfType(typeof(ISpecification<StatusData>)))
                        .ToList();

      specTypes.ForEach(x => returnList.Add(Activator.CreateInstance(x) as ISpecification<StatusData>));

      return returnList;

    }
  }

Wo ich falle ist, sobald ich eine Spezifikation entdeckt habe, die durch das Statusobjekt erfüllt ist, wie kann ich diese Spezifikation auf einen Typ, der IStatusUpdate implementiert abbilden. Ich bin verblüfft.

Jemand hat zu Recht vorgeschlagen, dass ich eine Zuordnung von Spezifikationen zu IStatusUpdate-Implementierern brauche. Diese Zuordnung scheint in der Verantwortung der Fabrik zu liegen, sie an die Spezifikation zu hängen, riecht nach einer Verletzung der SRP. Ich könnte eine Mapper-Klasse erstellen, die diese Verantwortung trägt, aber das scheint nicht sehr generisch zu sein und wirft auch die Frage auf, wie ich den Mapper auf die Spezifikation abbilde.

Es gibt noch einen kleinen Sprung, den ich übersehen habe.

3voto

Richard Punkte 103159

Wenn ich richtig verstanden habe, möchten Sie, gegeben ein Objekt, das ISpecification implementiert Sie ein Objekt, das IStatusUpdate implementiert?

In Ihrem Beispiel ist keiner dieser Typen definiert, so dass ich nicht weiß, ob es eine Beziehung zwischen ihnen gibt, die Sie verwenden könnten.

Wahrscheinlich werden Sie aber entweder eine Fabrik benötigen, die den Code enthält, oder eine Methode ISpecification.GetUpdate(), um das Objekt zu erstellen.

1voto

Justin Bozonier Punkte 7280

Ich gehe also davon aus, dass wir uns wirklich auf diesen Satz von Zeilen konzentrieren:

if (spec.IsSatisfiedBy(item))
          return new NewStatusUpdate();

und ich nehme an, Sie fragen, wie das in der jetzigen Form möglich ist. Es scheint, als sollte item unterstützen wie

interface ISpecSupport<T>
{
    bool ItemSpecsContain(ISpecification<T> spec);
}

Dann kann die Methode spec.IsSatisfiedBy diesen Schnittstellentyp aufnehmen und die Methode ausführen.

Mit anderen Worten, ich denke, ich will damit sagen, dass das Objekt eine Art Beschreibung dessen enthalten sollte, was es ist (in Form von Spezifikationen). Ich vermute, das ist eine Art Liste, aber ich bin mir nicht sicher. Ich bin sicher, Sie haben wahrscheinlich daran gedacht, also wenn Sie etwas hinzufügen können, wäre das hilfreich.

Anstelle der obigen Formulierung könnten Sie sie auch folgendermaßen umstellen:

if (item.Satisfies(spec))
    return new NewStatusUpdate();

Auf diese Weise müssen Sie nicht das viel geschmähte Besuchermuster verwenden (ich glaube, das habe ich zuvor beschrieben). Es ist direkter, da das Element scheint, wie es die Spezifikationen besitzen würde, und auf diese Weise lassen Sie das Element zu entscheiden, ob es die Spezifikation erfüllt.

Wenn Sie nicht wollen, dass diese Logik innerhalb des Objekts gehalten wird (was ich verstehen würde) UND Sie verwenden eine Eigenschaft Tasche von einer Art (oder Sie sind cool mit Reflexion) Sie könnten in die Details des Objekts mit einem unabhängigen spec Validator graben. Eigentlich ist ein unabhängiger Validator vielleicht gar keine schlechte Idee. Ich bin mir nicht so sicher, ob die Fähigkeit zu wissen, ob eine Spezifikation mit einem Objekt übereinstimmt, eine Verantwortung ist, die bei einer einzelnen Spezifikation bleiben sollte.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X