Sostituisci il codice del tipo con la classe (dal refactoring [Fowler])


9

Questa strategia prevede la sostituzione di simili:

public class Politician
{
    public const int Infidelity = 0;
    public const int Embezzlement = 1;
    public const int FlipFlopping = 2;
    public const int Murder = 3;
    public const int BabyKissing = 4;

    public int MostNotableGrievance { get; set; }
}

Con:

public class Politician
{
    public MostNotableGrievance MostNotableGrievance { get; set; }
}

public class MostNotableGrievance
{
    public static readonly MostNotableGrievance Infidelity = new MostNotableGrievance(0);
    public static readonly MostNotableGrievance Embezzlement = new MostNotableGrievance(1);
    public static readonly MostNotableGrievance FlipFlopping = new MostNotableGrievance(2);
    public static readonly MostNotableGrievance Murder = new MostNotableGrievance(3);
    public static readonly MostNotableGrievance BabyKissing = new MostNotableGrievance(4);

    public int Code { get; private set; }

    private MostNotableGrievance(int code)
    {
        Code = code;
    }
}

Perché è esattamente preferibile fare questo tipo di enumerazione, in questo modo:

public class Politician
{
    public MostNotableGrievance MostNotableGrievance { get; set; }
}

public enum MostNotableGrievance
{
    Infidelity = 0,
    Embezzlement = 1,
    FlipFlopping = 2,
    Murder = 3,
    BabyKissing = 4
}

Non esiste alcun comportamento associato al tipo e se esistesse si utilizzerebbe comunque un diverso tipo di refactoring, ad esempio "Sostituisci il codice del tipo con sottoclassi" + "Sostituisci condizionale con polimorfismo".

Tuttavia, l'autore spiega perché aggrotta le sopracciglia su questo metodo (in Java?):

I codici numerici o le enumerazioni sono una caratteristica comune dei linguaggi basati su C. Con nomi simbolici possono essere abbastanza leggibili. Il problema è che il nome simbolico è solo un alias; il compilatore vede ancora il numero sottostante. Il tipo di compilatore controlla usando il numero 177 non il nome simbolico. Qualsiasi metodo che prende il codice del tipo come argomento si aspetta un numero e non c'è nulla per forzare l'uso di un nome simbolico. Ciò può ridurre la leggibilità ed essere fonte di bug.

Ma quando si tenta di applicare questa affermazione a C #, questa affermazione non sembra vera: non accetterà un numero perché un'enumerazione è in realtà considerata una classe. Quindi il seguente codice:

public class Test
{
    public void Do()
    {
        var temp = new Politician { MostNotableGrievance = 1 };
    }
}

Non compilerà. Quindi questo refactoring può essere considerato non necessario in linguaggi di alto livello più recenti, come C #, o non sto prendendo in considerazione qualcosa?


var temp = new Politician { MostNotableGrievance = MostNotableGrievance.Embezzlement };
Robert Harvey,

Risposte:


6

Penso che tu abbia quasi risposto alla tua domanda lì.

Il secondo pezzo di codice è preferito al primo perché offre sicurezza del tipo. Con il primo pezzo, se hai un'enumerazione simile di Fish, puoi dire qualcosa del genere

MostNotableGrievance grievance = Fish.Haddock;

e al compilatore non importa. Se Fish.Haddock = 2, quanto sopra sarà esattamente equivalente a

MostNotableGrievance grievance = MostNotableGrievance.FlipFlopping;

ma ovviamente non immediatamente leggibile come tale.

Il motivo per cui le enumerazioni spesso non sono migliori è perché la conversione implicita da e verso int lascia lo stesso identico problema.

Ma, in C #, non esiste una conversione così implicita, quindi un enum è una struttura migliore da usare. Raramente vedrai uno dei primi due approcci utilizzati.


5

Se non ci sono comportamenti associati al valore e non ci sono informazioni extra che è necessario memorizzare al suo fianco e la lingua non supporta la conversione implicita in numeri interi, userei un enum; ecco a cosa servono.


1

Un altro esempio che potrebbe aiutarti può essere trovato in Effective Java (elemento 30, se vuoi cercarlo). È valido anche per C #.

Supponiamo di usare costanti int per modellare diversi animali, ad esempio serpenti e cani.

int SNAKE_PYTHON=0;
int SNAKE_RATTLE=1;
int SNAKE_COBRA=2;

int DOG_TERRIER=0;
int DOG_PITBULL=1;

Supponiamo che queste siano costanti e che possano essere definite in classi diverse. Questo potrebbe sembrare perfetto per te e potrebbe effettivamente funzionare. Ma considera questa riga di codice:

snake.setType(DOG_TERRIER);

Chiaramente, questo è un errore. Il compilatore non si lamenterà e il codice verrà eseguito. Ma il tuo serpente non si trasformerà in un cane. Questo perché il compilatore vede solo:

snake.setType(0);

Il tuo serpente è in realtà un pitone (e non un terrier). Questa funzione si chiama sicurezza di tipo ed è in realtà il motivo per cui si è un codice di esempio

var temp = new Politician { MostNotableGrievance = 1 };

non verrà compilato. MostNotableGrievance è MostNotableGrievance non un numero intero. Con enums puoi esprimerlo nel sistema dei tipi. Ed è per questo che io e Martin Fowler pensiamo che le enumerazioni siano fantastiche.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.