Proprietà / metodi multipli che danno lo stesso risultato

5

Sto cercando di accertare se l'uso di più riferimenti alla stessa proprietà sia l'odore del codice / un anti-modello, in base alle esigenze dell'organizzazione.

Ad esempio, prendi in considerazione:

abstract class Person {

    public string Title { get; set; }
    public string Forename { get; set; }
    public string MiddleNames { get; set; }
    public string Surname { get; set; }

    public string GivenName  { get { return Forename; } set { Forename = value; } }
    public string FamilyName { get { return Surname; }  set { Surname = value; } }
    public string FirstName  { get { return Forename; } set { Forename = value; } }
    public string LastName   { get { return Surname; }  set { Surname = value; } }

}

Come puoi vedere, ho diverse altre proprietà, aliasing (tipo di) o dare nomi alternativi alle proprietà nell'oggetto, ma fondamentalmente ripetendo esattamente la funzionalità della proprietà di base senza eccezioni.

Le ragioni alla base di questo sono le convenzioni di denominazione utilizzate in diverse aree dell'organizzazione; sta fornendo molteplici modi di indirizzare le stesse informazioni, consentendo a ciascuna area diversa di utilizzare il loro metodo di accesso preferito. Poiché ciascuna delle altre proprietà fa riferimento alla proprietà di base, tutte le modifiche programmatiche devono essere incluse solo nella suddetta proprietà di base.

E così per chiarire ...

@GregBurghardt ha gentilmente postato delle alternative ai miei enigmi, ma sento che devo qualificare un po 'di più le mie affermazioni.

In VB.NET posso tranquillamente codificare le interfacce nelle mie classi, ma usare nomi alternativi nell'effettiva implementazione dei membri, come ad esempio ...

Public Interface IPerson
    Property Forename() As String
    Property Surname() As String
End Interface

Public Class BillingPerson
    Implements IPerson

    Public Property GivenName() As String Implements IPerson.Forename
    ...
    Public Property FamilyName() As Int32 Implements IPerson.Surname
    ....
End Class

Si noti, qui, che ho usato un nome di proprietà diverso per la proprietà (VB), ma ho ancora implementato il vero nome dell'interfaccia (es. Proprietà pubblica GivenName () As String Implementa IPerson.Forename ). Ciò significa che posso fare riferimento alla mia classe BillingPerson , nonostante le ovvie differenze del nome della proprietà, utilizzando l'interfaccia ...

'Define our interface type object here...
Dim myP As IPerson = New BillingPerson()
'Assign values directly to the interface members...
myP.Forename = "Fred"
myP.Surname = "Bloggs"
'Output the interface members...
Console.WriteLine(myP.Forename & " " & myP.Surname)

... oppure posso riferirmi direttamente alla classe originale ....

'Define our BillingPerson object here...
Dim myP As BillingPerson
'Assign values directly to the interface members...
myP.GivenName = "Fred"
myP.FamilyName = "Bloggs"
'Output the interface members...
Console.WriteLine(myP.Forename & " " & myP.Surname)

Utilizzando questa metodologia, il mio problema sarebbe molto breve e sarei in grado di creare tutte le classi basate sull'interfaccia di cui ho bisogno.

Questo non sembra funzionare allo stesso modo in C #, tuttavia, in cui non posso direttamente alias il nome dei membri dell'interfaccia.

    
posta Paul 24.05.2017 - 10:10
fonte

4 risposte

7

Dal momento che la tua organizzazione non sembra essere d'accordo nel nominare le cose, sembra che tu debba architettare la tua applicazione supponendo che ci saranno lotti di cose su cui non sono d'accordo. Sembra che peggiorerà solo.

Forse quello che ti serve invece è adottare più di un modello di facciata o adattatore.

Quindi, ecco la tua persona "standard":

public class Person
{
    public string Title { get; set; }
    public string Forename { get; set; }
    public string MiddleNames { get; set; }
    public string Surname { get; set; }

    // Enterprise wide behavior goes here
}

E il reparto fatturazione può fare a modo suo:

public class BillingDepartmentPerson
{
    private readonly Person person;

    public string GivenName
    {
        get { return person.Forename; }
        set { person.Forename = value; }
    }

    public string FamilyName
    {
        get { return person.Surname; }
        set { person.Surname = value; }
    }

    public string FirstName
    {
        get { return person.Forename; }
        set { person.Forename = value; }
    }

    public string LastName
    {
        get { return person.Surname; }
        set { person.Surname = value; }
    }

    public BillingDepartmentPerson(Person person)
    {
        this.person = person;
    }

    // Billing department specific behavior goes here
}

E lo stesso può fare il reparto spedizioni:

public class ShippingDepartmentPerson
{
    private readonly Person person;

    public string Title
    {
        get { return person.Title; }
        set { person.Title = value; }
    }

    public string Forename
    {
        get { return person.Forename; }
        set { person.Forename = value; }
    }

    public string MiddleNames
    {
        get { return person.MiddleNames; }
        set { person.MiddleNames = value; }
    }

    public string Surname
    {
        get { return person.Surname; }
        set { person.Surname = value; }
    }

    public ShippingDepartmentPerson(Person person)
    {
        this.person = person;
    }

    // Shipping department specific behavior goes here
}

Sì, c'è qualche ripetizione del codice, ma ora hai un modo per incapsulare la funzionalità "specifica per l'area dell'organizzazione" e separarla dalla comune, funzionalità aziendale nella classe Person.

Significa anche che non è possibile richiamare accidentalmente operazioni specifiche del dipartimento di fatturazione nel contesto del reparto spedizioni. Ti dà una bella Separazione dei dubbi in modo che il refactoring di queste diverse aree possa essere isolato, e gli effetti di qualsiasi lavoro di refactoring possono essere di portata limitata (vedi anche il Principale Aperto / Chiuso ).

Paul ha commentato:

if this had been VB.NET, it wouldn't have been a problem, I could have used an interface and aliased all the member names. But it isn't!

... in VB.NET I can do something like: Public Function x() As Boolean Implements IY.a. I haven't seen anything in C# to allow this.

C #:

public class MyItems : IEnumerable<int>
{
    public IEnumerator<int> GetEnumerator()
    {
        throw new NotImplementedException();
    }

    // Implement an interface method explicitly
    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        throw new NotImplementedException();
    }
}

VB.NET e C # vengono compilati allo stesso esatto codice MSIL che viene eseguito nello stesso esatto Common Language Runtime.

C # e VB.NET, a patto di confrontare le versioni di Visual Studio e .NET Framework, supportano le stesse funzionalità.

    
risposta data 24.05.2017 - 14:16
fonte
4

Non è tanto un odore di codice quanto l'odore di un'operazione. Penso che dovresti chiedere a quelle "diverse aree dell'organizzazione" di concordare una convenzione di denominazione aziendale prima di procedere. Stai cercando di soddisfare la loro incapacità di comunicare e quindi rendere più difficile per sempre raddrizzare questa eredità.

Metti un termine su di esso, dai loro due settimane per tornare da te. Che probabilmente non lo faranno. Quindi scegli quali nomi tu piacciono e dichiara quelli come standard aziendale.

    
risposta data 24.05.2017 - 13:30
fonte
1

Penso che dovresti non avere proprietà alias ma selezionarne una, forse la più comune e documentare gli alias in un commento xml:

/// <summary>
/// Gets person's first name also known as Forename.
/// </summary>
public string FirstName { get; set; }

Se tutti usassero più proprietà per la stessa cosa avremmo array e liste con proprietà come:

public int Count { get; }
public int NumberOfItems { get; }
public int Length { get; }

Non è solo difficile da mantenere, ma anche la curva di apprendimento è più ripida. Devi sapere tutti loro per sapere che in realtà sono identici. Se qualcuno volesse comunque utilizzare un alias, può sempre scrivere la propria estensione.

    
risposta data 26.05.2017 - 20:18
fonte
0

In alternativa, si potrebbe ancora avere l'approccio a una sola classe con i propri nomi oltre alle singole proprietà, ma fornire una funzione di accesso generica mediante la reflection:

public static T GetPropertyValue<T>(object obj, string propName) 
{ 
    return (T)obj.GetType().GetProperty(propName).GetValue(obj, null); 
}

Di fronte a quello avremo bisogno di una configurazione di ricerca (xml, json, ecc.) in modo che elementi come:

FamilyName

viene trasformato in:

SurName

Quindi il cognome viene passato al metodo riflettente. Pertanto, gli alias di denominazione vengono memorizzati in una configurazione anziché in codice o classi aggiuntive. Se vengono aggiunti nuovi alias, basta aggiornare "alias config".

Accedere direttamente alla proprietà o utilizzare l'approccio più complesso di consentire al chiamante di provare e accedere alla proprietà utilizzando il proprio nome, il codice eseguirà l'aspetto e utilizzerà il metodo riflettente.

Questo mantiene pulito il tuo codice base. Tieni presente che l'accesso alla riflessione e l'accesso diretto saranno più lenti.

    
risposta data 24.05.2017 - 18:42
fonte

Leggi altre domande sui tag