Mehr von Jürgen Gutsch

Mehr von Jürgen Gutsch

Empfehlungen von Jürgen Gutsch

Blog-Empfehlungen von Jürgen Gutsch

Willkommen bei ASP.NET Zone. Anmelden | Registrieren | Hilfe

Jürgen Gutsch

ASP.NET und mehr...
Event Based Components

In den vergangen Monaten habe ich einiges theoretisches über Event Based Components (kurz EBCs) gelesen. Das meiste von Ralf Westphal, in seinem Blog oder aus seiner Feder in der dotnetpro. Auch Golo Roden schrieb in seinem Blog über das Thema.

Verwirrung

Angespornt durch die Beiträge habe ich in den letzten Wochen selber versucht EBCs zu schreiben und zu entwickeln und bin zuerst gescheitert. Sobald ich die ersten Komponenten geschrieben und “zusammengesteckt” hatte, verlor ich relativ schnell den Überblick über die Komponenten und deren Verbindungen.

Woran lag das?

Golo wies mich auf die fehlerhafte Benennung der Events in Ralf Westphals Beispielen hin, die entgegen dem .NET Framework mit dem Präfix “On” begannen.

Ich habe also von vorne begonnen und die Events wie im .NET-Framework üblich benannt.

Beispiel:

public event Action<Customer> EntitySaving (wird vor dem Speichern ausgelöst)
public event Action<Customer> EntitySaved (wird nach den Speichern ausgelöst)

Und siehe da: die Verwirrung war beseitigt :-)

Vorteile

Die Vorteile der EBCs sind klar und wurden in den oben genannten Beiträgen auch oft genug angesprochen. Die zwei größten Vorteile sind – aus meiner Sicht – zum einen die isolierte Testbarkeit der Komponenten und die flexible Möglichkeit die Komponenten zusammenzuführen.

Refactoring

In den letzten Wochen habe ich also meinen angehenden Lizenz-Manager, einem großen Refactoring unterzogen und auf EBCs umgebaut. Ich habe allerdings vorerst meine Service-Klassen nicht komplett auseinandergerissen. Also nicht fürs Speichern, Löschen und Lesen separate Komponenten gebaut (das wäre dann der nächste schritt), sondern es bei jeweils einer Komponente mit standardmäßig drei Input- und drei Output-Pins belassen. Ich habe lediglich die generische Validierung komplett entkoppelt und dafür eine weitere Komponente angelegt, die vor jedem Speichern “eingesteckt” werden kann.

Die vorhandenen Unit-Tests musste ich leicht anpassen. Die Tests für die Servies, die hauptsächlich die Validierung getestet haben konnte ich entfernen und in die Test für die Validator-Komponente aufnehmen. Die Tests sind alle kleiner geworden, da Abhängigkeiten gar nicht mehr berücksichtigt werden müssen, wenn sie nicht zusammengesteckt worden sind. Beispiel: Die Service-Methode zum Speichern, muss keine Validierung mehr testen. Und die Tests der Validierung ist unabhängig vom Speichervorgang.



[TestFixture]
[Category("GO.LicenceManager.Services.ModelValidator")]
public class Wenn_ein_Kunde_validiert_wird
{
    [Test]
    public void darf_der_Vorname_nicht_fehlen()
    {
        Customer customer = new Customer
                                {
                                    [….]
                                };

        IModelValidator<Customer> modelValidator = new ModelValidator<Customer>();
        Assert.Throws<PropertyNotSetException<Customer>>(
            () => modelValidator.Validate(customer),
            "'Name' must not be null or empty");
    }
}

public class ModelValidator<T> : IModelValidator<T> where T : IEntity
{
    public void Validate(T model)
    {
        Type type = typeof(T); 
        IEnumerable<PropertyInfo> propertyInfos = type
                .GetProperties(BindingFlags.Public | BindingFlags.Instance); 
        [….]
    }
}

public class CustomerService : ICustomerService
{
    private readonly ICustomerRepository _customerRepository; 
    public CustomerService(ICustomerRepository customerRepository)
    {
        _customerRepository = customerRepository;
    } 
    public void Save(Customer customer)
    {
       OnEntitySaving(customer); 
       if(customer.Id==Guid.Empty)
       {
           customer.Id = Guid.NewGuid();
       } 
        _customerRepository.Save(customer);
    } 
    public event Action<Customer> EntitySaving;
    public void OnEntitySaving(Customer entity)
    {
        var entitySaving = EntitySaving;
        if (entitySaving != null)
        {
            entitySaving(entity);
        }
    }

    [….]
}

Zusammenstecken:

Customer customer = new Customer
                                    {
                                        [….]
                                    }; 
IModelValidator<Customer> validator = new ModelValidator<Customer>();
ICustomerService customerService = new CustomerService(customerRepository);
customerService.EntitySaving += validator.Validate; // <== zusammenstecken
customerService.Save(customer);

Die Tests hätten wir damit vereinfacht, die Komponenten müssen sich nicht mehr kennen und sind komplett entkoppelt. Die Komponenten müssen aber irgendwie und irgendwo zusammengeführt werden, um zu funktionieren.

Die Komponenten müssen “zusammensteckt” werden.

Dabei ist entweder Handarbeit und eine gute Dokumentation gefordert, oder man versucht das zu automatisieren. Ralf Westphal hat dafür einen EBC-Binder geschrieben, der allerdings nur funktioniert, wenn gewisse Konventionen eingehalten werden: Unter anderem müssen die Events eben mit dem Präfix “On” beginnen…

Ich werde hierfür wahrscheinlich eine Art Binding-Datei anlegen die im XML-Format nicht nur das Binding selber regelt, sondern dieses auch dokumentiert. Mal sehen, was sich so machen lässt :-)

Vorerst werde ich im Lizenz-Manager allerdings noch die Repositories und die verschiedenen Views implementieren müssen. Bis zur See# Party werde ich wohl aber nicht mehr dazu kommen…

DotNetKicks-DE Image
Posted: Mittwoch, 18. August 2010 22:22 von Jürgen Gutsch

Kommentare

dotnet-kicks.de sagte:

Sie wurden gekickt (eine gute Sache) - Trackback von dotnet-kicks.de

# August 19, 2010 08:38
Anonyme Kommentare sind nicht zugelassen