Sostituisci il codice di tipo con la classe (da Refactoring [Fowler])

9

Questa strategia comporta la sostituzione di "Mi piace" di questo:

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 del tipo un'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, in ogni caso, dovresti comunque utilizzare un diverso tipo di refactoring, ad esempio "Sostituisci il codice del tipo con sottoclassi" + "Sostituisci il condizionale con il polimorfismo".

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

Numeric type codes, or enumerations, are a common feature of C-based languages. With symbolic names they can be quite readable. The problem is that the symbolic name is only an alias; the compiler still sees the underlying number. The compiler type checks using the number 177 not the symbolic name. Any method that takes the type code as an argument expects a number, and there is nothing to force a symbolic name to be used. This can reduce readability and be a source of bugs.

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

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

Non verrà compilato. Quindi questo refactoring può essere considerato non necessario nei nuovi linguaggi di alto livello, come C #, o non sto considerando qualcosa?

    
posta Merritt 20.04.2012 - 23:20
fonte

3 risposte

6

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

Il secondo pezzo di codice è preferito rispetto al primo perché offre la sicurezza del tipo. Con il primo pezzo, se hai una enumerazione simile di Fish, puoi dire qualcosa come

MostNotableGrievance grievance = Fish.Haddock;

e il compilatore non si preoccuperà. Se Fish.Haddock = 2 allora 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 ti lascia lo stesso identico problema.

Ma, in C #, non c'è una conversione implicita, quindi un enum è una struttura migliore da usare. Raramente vedrai uno dei primi due approcci usati.

    
risposta data 21.04.2012 - 02:52
fonte
5

Se non c'è alcun comportamento associato al valore e non ci sono informazioni aggiuntive che è necessario memorizzare accanto a esso e il linguaggio non supporta la conversione implicita in numeri interi, userei un enum; questo è quello per cui sono lì.

    
risposta data 21.04.2012 - 00:35
fonte
1

Un altro esempio che potrebbe aiutarti a trovarlo in Java efficace (elemento 30, se si vuole cercare). È valido anche per C #.

Supponi 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 forse anche definite in classi diverse. Questo può sembrare a posto 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 del tipo ed è in realtà il motivo per cui sei codice di esempio

var temp = new Politician { MostNotableGrievance = 1 };

non verrà compilato. MostNotableGrievance è MostNotableGrievance non un intero. Con enumerazione puoi esprimere questo nel sistema di tipi. Ed è per questo che Martin Fowler e io pensiamo che le enumerazioni siano grandiose.

    
risposta data 22.04.2012 - 17:09
fonte

Leggi altre domande sui tag