Замініть тип коду класом (From Refactoring [Fowler])


9

Ця стратегія передбачає заміну подібного:

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; }
}

З:

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;
    }
}

Чому саме це бажано робити перерахуванням типу, наприклад:

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

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

Немає поведінки, пов'язаної з типом, і якби вона була, ви б у будь-якому разі використовували інший тип рефакторингу, наприклад, "Замінити код типу підкласами" + "Замінити умовним поліморфізмом".

Однак автор пояснює, чому він нахмурився на цей метод (на Java?):

Коди числення чисельних типів є загальною ознакою мов на основі С. За допомогою символічних назв вони можуть бути досить читабельними. Проблема полягає в тому, що символічна назва є лише псевдонімом; компілятор все ще бачить основний номер. Тип компілятора перевіряє, використовуючи число 177, а не символічну назву. Будь-який метод, який приймає код типу як аргумент, очікує числа, і немає нічого, що змушує використовувати символічне ім'я. Це може зменшити читабельність і стати джерелом помилок.

Але при спробі застосувати це твердження до C #, це твердження не видається істинним: воно не прийме число, оскільки перерахування насправді вважається класом. Отже наступний код:

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

Не збирається. Тож чи можна вважати таке рефакторинг необхідним у нових мовах високого рівня, як-от C #, чи я щось не розглядаю?


var temp = new Politician { MostNotableGrievance = MostNotableGrievance.Embezzlement };
Роберт Харві

Відповіді:


6

Я думаю, ви майже відповіли на власне запитання там.

Другий фрагмент коду є кращим перед першим, оскільки він забезпечує безпеку типу. З першим твором, якщо у вас подібне перерахування Риб, то ви можете сказати щось на кшталт

MostNotableGrievance grievance = Fish.Haddock;

і компілятор буде байдужим. Якщо Fish.Haddock = 2, то вищезгадане буде точно рівнозначним

MostNotableGrievance grievance = MostNotableGrievance.FlipFlopping;

але, очевидно, не миттєво читається як таке.

Причина перерахування часто не є кращою через те, що неявна конверсія до та з int залишає вас із точно такою ж проблемою.

Але в C # немає такого неявного перетворення, тому enum є кращою структурою для використання. Ви рідко бачите жоден із перших двох підходів, що використовуються.


5

Якщо немає поведінки, пов'язаної зі значенням, і немає додаткової інформації, яку потрібно зберігати поруч, і мова не підтримує неявне перетворення в цілі числа, я б використовував enum; ось для чого вони там.


1

Інший приклад, який може допомогти вам, ви можете знайти в Ефективній Java (пункт 30, якщо ви хочете переглянути її). Він дійсний і для C #.

Припустимо, ви використовуєте ін-константи для моделювання різних тварин, скажімо, змій та собак.

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

int DOG_TERRIER=0;
int DOG_PITBULL=1;

Припустимо, це константи і, можливо, навіть визначені в різних класах. Вам це може здатися прекрасним, і це може справді вийти. Але врахуйте цей рядок коду:

snake.setType(DOG_TERRIER);

Зрозуміло, що це помилка. Компілятор не поскаржиться і код виконає. Але ваша змія не перетвориться на собаку. Це тому, що компілятор бачить лише:

snake.setType(0);

Ваша змія - це насправді пітон (а не тер’єр). Ця функція називається безпекою типу, і це насправді причина, чому ви є прикладом коду

var temp = new Politician { MostNotableGrievance = 1 };

не компілюється MostNotableGrievance - це MostNotableGrievance, а не ціле число. За допомогою перерахунків ви можете це виразити в системі типів. І тому Мартін Фаулер і я думаю, що перерахування великі.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.