Domanda

Mi chiedo solo se Specification modello è inutile, dato seguente esempio:

Dire che si desidera controllare se un cliente ha abbastanza equilibrio nella sua / il suo account, è necessario creare una specifica qualcosa come:

new CustomerHasEnoughMoneyInTheirAccount().IsSatisfiedBy(customer)

Tuttavia, quello che mi chiedo è che posso ottenere gli stessi "benefici" di Specification modello (ad esempio solo la necessità di cambiare le regole di business in su luogo) utilizzando getter proprietà nella classe cliente in questo modo:

public class Customer
{

     public double Balance;

     public bool HasEnoughMoney
     {
          get { return this.Balance > 0; }
     }
}

Da codice client:

customer.HasEnoughMoney

Quindi la mia domanda è in realtà; qual è la differenza tra l'utilizzo del getter proprietà per avvolgere la logica di business, e la creazione di una classe specifica?

Grazie a tutti in anticipo!

È stato utile?

Soluzione

A causa con la classe specifica è possibile creare nuovi criteri senza modifiche i oggetti stessi.

Altri suggerimenti

In senso generale, un oggetto specifica è solo un predicato avvolto in un oggetto. Se un predicato è molto comunemente utilizzato con una classe, potrebbe avere senso per metodo Move il predicato nella classe si applica a.

Questo modello viene veramente il meglio di sé quando si sta costruendo qualcosa di più complicato in questo modo:

var spec = new All(new CustomerHasFunds(500.00m),
                   new CustomerAccountAgeAtLeast(TimeSpan.FromDays(180)),
                   new CustomerLocatedInState("NY"));

e passandolo intorno o serializzazione esso; può rendere ancora più senso quando si sta fornendo una sorta di "specifica costruttore" UI.

Detto questo, C # fornisce modi più idiomatiche per esprimere questo genere di cose, come i metodi di estensione e LINQ:

var cutoffDate = DateTime.UtcNow - TimeSpan.FromDays(180); // captured
Expression<Func<Customer, bool>> filter =
    cust => (cust.AvailableFunds >= 500.00m &&
             cust.AccountOpenDateTime >= cutoffDate &&
             cust.Address.State == "NY");

Ho giocato in giro con qualche codice sperimentale che implementa specifiche in termini di Expressions, con molto semplice statica costruttore metodi.

public partial class Customer
{
    public static partial class Specification
    {
        public static Expression<Func<Customer, bool>> HasFunds(decimal amount)
        {
            return c => c.AvailableFunds >= amount;
        }

        public static Expression<Func<Customer, bool>> AccountAgedAtLeast(TimeSpan age)
        {
            return c => c.AccountOpenDateTime <= DateTime.UtcNow - age;
        }


        public static Expression<Func<Customer, bool>> LocatedInState(string state)
        {
            return c => c.Address.State == state;
        }
    }
}

Detto questo, questo è un intero carico di boilerplate che non aggiunge valore! Queste Expressions guardare solo a proprietà pubbliche, e quindi si può facilmente utilizzare un semplice vecchio lambda! Ora, se una di queste specifiche esigenze di stato di accesso non pubblico, abbiamo davvero Non bisogno di un metodo costruttore con accesso a state non pubbliche. Userò lastCreditScore come un esempio qui.

public partial class Customer
{
    private int lastCreditScore;

    public static partial class Specification
    { 
        public static Expression<Func<Customer, bool>> LastCreditScoreAtLeast(int score)
        {
            return c => c.lastCreditScore >= score;
        }
    }
}

Abbiamo anche bisogno di un modo per fare un composito di queste tecniche - in questo caso, un composito che richiede a tutti i bambini per essere vero:

public static partial class Specification
{
    public static Expression<Func<T, bool>> All<T>(params Expression<Func<T, bool>>[] tail)
    {
        if (tail == null || tail.Length == 0) return _0 => true;
        var param = Expression.Parameter(typeof(T), "_0");
        var body = tail.Reverse()
            .Skip(1)
            .Aggregate((Expression)Expression.Invoke(tail.Last(), param),
                       (current, item) =>
                           Expression.AndAlso(Expression.Invoke(item, param),
                                              current));

        return Expression.Lambda<Func<T, bool>>(body, param);
    }
}

Credo che parte del lato negativo di questo è che può risultare in alberi Expression complicate. Per esempio, la costruzione di questo:

 var spec = Specification.All(Customer.Specification.HasFunds(500.00m),
                              Customer.Specification.AccountAgedAtLeast(TimeSpan.FromDays(180)),
                              Customer.Specification.LocatedInState("NY"),
                              Customer.Specification.LastCreditScoreAtLeast(667));

produce un albero Expression che assomiglia a questo. (Questi sono leggermente versioni di ciò che ToString() rendimenti formattati quando viene chiamato sul Expression - nota che non sarebbe in grado di vedere la struttura dell'espressione a tutti se si aveva solo un semplice delegato Un paio di note: un DisplayClass è un classe generato dal compilatore che contiene le variabili locali catturati in una chiusura, per far fronte alla verso l'alto funarg problema ,. e il dumping Expression utilizza un unico segno = per rappresentare confronto di uguaglianza, piuttosto che C # 's tipico ==)

_0 => (Invoke(c => (c.AvailableFunds >= value(ExpressionExperiment.Customer+Specification+<>c__DisplayClass0).amount),_0)
       && (Invoke(c => (c.AccountOpenDateTime <= (DateTime.UtcNow - value(ExpressionExperiment.Customer+Specification+<>c__DisplayClass2).age)),_0) 
           && (Invoke(c => (c.Address.State = value(ExpressionExperiment.Customer+Specification+<>c__DisplayClass4).state),_0)
               && Invoke(c => (c.lastCreditScore >= value(ExpressionExperiment.Customer+Specification+<>c__DisplayClass6).score),_0))))

disordinato! Un sacco di invocazione di lambda immediati e riferimenti non distribuiti alle chiusure create nei metodi Builder. Sostituendo riferimenti chiusura con i loro valori acquisiti e ß-riduzione la lambda nidificati (ho anche a-convertiti tutti i nomi dei parametri di unico simboli generati come intermedio per semplificare ß-riduzione), molto più semplice risultati albero Expression:

_0 => ((_0.AvailableFunds >= 500.00)
       && ((_0.AccountOpenDateTime <= (DateTime.UtcNow - 180.00:00:00))
           && ((_0.Address.State = "NY")
               && (_0.lastCreditScore >= 667))))

Questi alberi Expression possono poi essere ulteriormente combinati, compilati in delegati, pretty-stampati, modificati, passato a interfacce LINQ che capiscono alberi Expression (come quelli forniti da EF), o quello che hai.

Su un lato nota, ho costruito un po 'sciocco micro-benchmark ed effettivamente scoperto che l'eliminazione riferimento chiusura ha avuto un notevole impatto sulle prestazioni della velocità della valutazione dell'esempio Expression se compilato a un delegato - tagliare il tempo di analisi quasi in la metà (!), da 134.1ns a 70.5ns per chiamata sulla macchina mi capita di essere seduto davanti. D'altra parte, ß-riduzione ha fatto alcuna differenza rilevabile, forse perché la compilazione lo fa comunque. In ogni caso, dubito un insieme classe specifica convenzionale potrebbe raggiungere questo tipo di velocità di valutazione per un composito di quattro condizioni; se tale classe convenzionaleset doveva essere costruito per altri motivi, come la comodità di Generatore di codice-UI, penso che sarebbe opportuno avere il set di classe produrre un Expression piuttosto che direttamente valutare, ma in primo luogo considerare se è necessario il modello a tutti in C # - ho visto il modo troppo codice Specification-overdose.

Sì, è inutile.

L'articolo di Wikipedia critica questo modello a lungo. Ma vedo il più grande critica è solo il Inner-Platform Effetto . Perché reinventare l'operatore? Anche in questo caso, leggere l'articolo di Wikipedia per la critica più ampia.

Henry, lei ha ragione ad assumere l'ottenere la proprietà è superiore. Perché eschew un concetto OO più semplice, ben compreso, per un "modello" oscura, che nella sua concezione, non risponde alla tua stessa domanda? E 'un'idea, ma una cattiva. Si tratta di un anti-modello, un modello che funziona contro di voi, il codificatore.

hanno chiesto qual è la differenza, ma una domanda più utile è, quando dovrebbe essere utilizzato un modello di specifica?

Non utilizzare questo modello , è la mia regola generale per questo modello.

In primo luogo, si dovrebbe capire che non è una teoria generica, è solo un modello specifico; con una specifica modellazione di classi {Specification, AndSpecification, ...}. Con la più ampia teoria del dominio-driven in mente, è possibile abbandonare questo modello, e hanno ancora opzioni superiori che tutti sono a conoscenza:., Per esempio, ben nome-oggetti / metodi / proprietà al linguaggio modello di dominio e la logica

Jeffrey ha detto:

  

un oggetto Specification è solo un predicato avvolto in un oggetto

Questo è vero per dominio-driven, ma non il modello specifico Specification. Jeffrey, descrive esaurientemente una situazione in cui uno potrebbe voler costruire dinamicamente un'espressione IQueryable, in modo che possa efficacemente eseguire sul archivio di dati (database SQL). La sua conclusione finale, è che non si può fare che con il modello specifica come è stato prescritto. alberi di espressione IQueryable di Jeffrey sono un modo alternativo per isolare regole logiche e le applichino in diversi materiali compositi. Come si può vedere dal suo codice di esempio, è prolisso e molto difficile da lavorare. Non riesco a immaginare una situazione che richiederebbe tali materiali compositi dinamici sia. E, se necessario, ci sono molte altre tecniche disponibili, che sono più semplici.

Sappiamo tutti si dovrebbe ottimizzare le prestazioni dello scorso. Il tentativo qui per ottenere Sanguinamento bordo con alberi di espressione IQueryable, è una trappola. Invece, iniziare con i migliori strumenti, un semplice e laconico proprietà Getter prima. Poi prova, valutare e resti privilegiare ciò che di lavoro.

Sono ancora sperimentare una situazione in cui è necessario questo modello Specifiche / migliore. Come faccio venire attraverso situazioni presunti, ti elenco qui e li confutare. Se mi imbatto in una situazione buona, io rivedere questa risposta con una nuova sezione.

RE: zerkms risposta

  

A causa con la classe specifica è possibile creare nuovi criteri [sic]   senza modifica degli oggetti stessi.

C # già approvvigiona per queste situazioni:

  • Inheritance (in generale), dove è poi estendere la classe ereditata (questo è buono quando non si possiede lo spazio dei nomi / libreria da cui la classe viene)
  • Metodo imperativo di ereditarietà
  • Parziale - grandi quando si hanno le classi-modello di dati. È possibile aggiungere [NotStored] proprietà al fianco, e godere di tutti la beatitudine di accedere alle informazioni necessarie direttamente fuori l'oggetto. Quando si preme '' IntelliSense ti dice quali sono disponibili i membri.
  • Metodi di estensione sono grandi quando eredità non è pratico (architettura non lo supporta), o se la classe padre è sigillata.

In progetti che prendere in consegna dai, ho incontrato anti-pattern come specifica del modello, e altro ancora. Sono spesso in un separato Progetto / libreria (over-frammentazione dei progetti è un altro terribile practghiaccio) e tutti sono troppo spaventati per estendere gli oggetti.

See zerkms risposta, oltre a: una specifica può funzionare anche su tipi astratti come le interfacce o come un making generica è applicabile a tutta una serie di oggetti.

O il controllo che deve essere fatto sul cliente potrebbe dipendere dal contesto. Ad esempio, un oggetto cliente potrebbe non essere valido per il sistema di ruoli paga ancora, ma valida per il salvataggio nel database nel bel mezzo di un processo per l'ulteriore elaborazione quando l'utente accede nuovamente. Con le specifiche è possibile costruire gruppi di controlli correlati in una posizione centralizzata e passare fuori l'intero set a seconda del contesto. In questa situazione che ci si combinano con un modello di fabbrica, per esempio.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top