Costruttore dall'interfaccia con ripetizione minima

-1

Prefazione

In un'applicazione che è separata per layer o distribuita dai servizi, è comune avere classi strettamente correlate ai dati, ma che desideriamo avere accoppiate liberamente. La mia solita soluzione (in c #) è di averli implementare un'interfaccia comune. (Ci sono altri pattern, ma questo è quello che imposto di default)

Potrei (per esempio) avere un'entità dati backend Foo

public class Foo:IFoo
{
    public int DatabaseKey {get; set;}
    public string NaturalKey {get; set;}
    public string SomeProperty {get; set;}
}

una classe di contratto dati FooDto

[DataContract]
public class FooDto:IFoo
{
    [DataMember]
    public string NaturalKey {get; set;}
    [DataMember]
    public string SomeProperty {get; set;}
}

e una classe modello MVC FooModel

public class FooModel:IFoo
{
    public string NaturalKey {get; set;}
    public string SomeProperty {get; set;}
    public string CssClass {get; set;}
}

tutti implementano un'interfaccia comune IFoo

public interface IFoo
{
    string NaturalKey {get;}
    string SomeProperty {get;}
}

La mia domanda

Quando mappiamo le incarnazioni di Foo l'una sull'altra potrei usare un object mapper (ad esempio l'automapper), ma per i progetti più piccoli o quando la politica impedisce a software di terze parti aggiungo un costruttore che prende l'interfaccia come parametro, cioè

public Foo(IFoo template):base()
{
    NaturalKey = template.NaturalKey;
    SomeProperty = template.SomeProperty;
    //additional code
}

etc

Come posso evitare di ripetere inutilmente i compiti dal modello alle proprietà attraverso le diverse classi? Supponiamo che il modello generale sia dato come è la decisione di non usare un object mapper. Tieni presente che le assegnazioni potrebbero essere diverse in alcuni casi (deducendo i valori di default espliciti per valori nulli ecc.)

    
posta Guran 01.11.2017 - 09:14
fonte

3 risposte

-2

Nessuno degli oggetti deve contenere proprietà in cui risiedono i suoi dati. Gli oggetti non sono contenitori di dati, sono unità di comportamento intelligenti. Potresti abbracciare ciò che Alan Kay, un uomo dietro Smalltalk, ha pensato agli oggetti. Voleva che fossero per eliminare i dati . Quindi se hai bisogno di persisterli, ok, potrebbe sembrare così:

class User
{
    private $id;
    private $dataSource;

    public function __construct(UUID $id, DataSource $dataSource)
    {
        $this->id = $id;
        $this->dataSource = $dataSource;
    }

    public function register(FullName $fullName, Passport $passport, Email $email)
    {
        $this->dataSource
            ->save(
                $this->id,
                $fullName,
                $passport,
                $email
            )
        ;
    }
}

Questo approccio risolve il problema di root: trattare oggetti come borse di dati. Riflette una mentalità procedurale che comprende buona parte degli sviluppatori di software. Potresti provare diversi hack e soluzioni alternative, come l'uso del reflection o il passaggio di un DTO all'entità modello, ma non risolve la causa principale del tuo problema. Fondamentalmente, qualsiasi oggetto dovrebbe possedere tutte le risorse di cui ha bisogno per attuare le proprie responsabilità. Ecco alcuni altri esempi e linee guida.

Anticipando la tua domanda su come evitare di copiare i dati incollati alle classi View o ModelView, la risposta è la stessa. In OOP, ogni oggetto è responsabile della visualizzazione di se stesso. Quindi il tuo oggetto potrebbe restituire un oggetto xml che potrebbe essere rappresentato nel browser con XSLT:

class User
{
    private $id;
    private $dataSource;

    public function __construct(UUID $id, DataSource $dataSource)
    {
        $this->id = $id;
        $this->dataSource = $dataSource;
    }

    public function display()
    {
        $data = $this->dataSource->byId($this->id);

        return
            (new Xml())
                ->add('name', $data['name'])
                ->add('passport_number', $data['passport_number'])
                ->add('email', $data['email'])
            ;
    }
}

Ecco di più su questa tecnica.

Ancora una volta, a meno che tu non interrompa il tuo programma come un flusso di dati passivi che viene eseguito su alcune procedure, non ti libererai mai di un costante get-and-setting poiché è la natura della programmazione procedurale.

    
risposta data 01.11.2017 - 09:49
fonte
-2

Questi tipi di classi sono essenzialmente strutture dati utilizzate per spostare i dati oltre i limiti dell'applicazione, e quindi non sono oggetti nel senso OOP. Le classi generalmente non hanno logica, o hanno pochissima logica, e agiscono principalmente come sacchetti di proprietà.

E, a meno che non mi sbagli, una delle cose fondamentali che devono supportare nello scenario che descrivi è copiare i valori delle proprietà da un tipo all'altro; Un modo per supportare questo è avere la tua interfaccia IFoo per dichiarare le proprietà di lettura / scrittura.

public interface IFoo
{
    string NaturalKey {get; set;}
    string SomeProperty {get; set;}
}

Quindi potresti semplicemente definire un metodo di estensione che prende due istanze di IFoo e copia le proprietà:

public static void CopyFrom(this IFoo destination, IFoo source) 
{
    destination.NaturalKey = source.NaturalKey;
    destination.SomeProperty = source.SomeProperty;
}

Puoi quindi usarlo in questo modo:

foo1.CopyFrom(foo2);

Potresti ritenere che questo non sia interamente OO, ma le applicazioni non sono orientate agli oggetti confini , ed è qui che compaiono queste classi.

P.S. Capisco che potrebbe esserci qualcosa che ti impedisce di usare questo approccio e che ci potrebbe essere una buona ragione per decidere di dichiarare l'interfaccia con le proprietà get-only - ma forse puoi adattare l'idea in qualche modo per adattarla al tuo caso.

    
risposta data 01.11.2017 - 11:13
fonte
-3

Potresti scrivere una classe base che esegue una copia predefinita di tutte le proprietà, assegnando valori predefiniti (che devono essere implementati dalle sottoclassi) se i valori delle proprietà non sono validi.

Esempio:

public interface IFoo
{
    string NaturalKey {get;}
    string SomeProperty {get;}
}

public abstract class BaseFoo : IFoo
{
    public string NaturalKey {get;}
    public string SomeProperty {get;}

    public abstract string DefaultNaturalKey {get;}
    public abstract string DefaultSomeProperty {get;}

    protected void CopyFrom(IFoo sourceFoo) 
    {
        if (string.IsNullOrEmpty(sourceFoo.NaturalKey)) 
        {
            NaturalKey = DefaultNaturalKey;
        } 
        else
        {
            NaturalKey = sourceFoo.NaturalKey;
        }

        if (string.IsNullOrEmpty(sourceFoo.SomeProperty)) 
        {
            SomeProperty = DefaultSomeProperty;
        } 
        else
        {
            SomeProperty = sourceFoo.SomeProperty;
        }

        //...and so on...
    }
}

public class Foo : BaseFoo {

    public override string DefaultNaturalKey { get { return "MyKey"; } }
    public override string DefaultSomeProperty { get { return "MyProperty"; } }

    public Foo(IFoo template) : base()
    {
        CopyFrom(template);

        //additional assignments, if necessary
    }
}
    
risposta data 01.11.2017 - 11:01
fonte

Leggi altre domande sui tag