Ganti Kode Jenis dengan Kelas (Dari Refactoring [Fowler])


9

Strategi ini melibatkan penggantian yang seperti ini:

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

Dengan:

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

Mengapa hal ini lebih disukai daripada membuat jenis penghitungan, seperti:

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

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

Tidak ada perilaku yang terkait dengan jenis dan jika ada Anda akan menggunakan jenis refactoring yang berbeda, misalnya, 'Ganti Kode Jenis dengan Subkelas' + 'Ganti Kondisional dengan Polimorfisme'.

Namun, penulis menjelaskan mengapa dia tidak menyukai metode ini (di Jawa?):

Kode jenis numerik, atau enumerasi, adalah fitur umum dari bahasa berbasis C. Dengan nama simbolis mereka dapat dibaca. Masalahnya adalah bahwa nama simbolik hanya alias; kompiler masih melihat nomor yang mendasarinya. Tipe kompiler memeriksa menggunakan nomor 177 bukan nama simbolik. Metode apa pun yang menggunakan kode jenis sebagai argumen mengharapkan nomor, dan tidak ada yang memaksa nama simbol untuk digunakan. Ini dapat mengurangi keterbacaan dan menjadi sumber bug.

Tetapi ketika mencoba menerapkan pernyataan ini ke C #, pernyataan ini tampaknya tidak benar: itu tidak akan menerima angka karena enumerasi sebenarnya dianggap sebagai kelas. Jadi kode berikut:

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

Tidak akan dikompilasi. Jadi bisakah refactoring ini dianggap tidak perlu dalam bahasa tingkat tinggi yang lebih baru, seperti C #, atau apakah saya tidak mempertimbangkan sesuatu?


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

Jawaban:


6

Saya pikir Anda hampir menjawab pertanyaan Anda sendiri di sana.

Potongan kode kedua lebih disukai daripada yang pertama karena menawarkan keamanan jenis. Dengan potongan pertama, jika Anda memiliki enumerasi serupa Ikan maka Anda bisa mengatakan sesuatu seperti

MostNotableGrievance grievance = Fish.Haddock;

dan kompiler tidak akan peduli. Jika Fish.Haddock = 2 maka di atas akan sama persis dengan

MostNotableGrievance grievance = MostNotableGrievance.FlipFlopping;

tapi jelas tidak langsung bisa dibaca seperti itu.

Alasan pencacahan sering kali tidak lebih baik adalah karena konversi implisit ke dan dari int meninggalkan Anda dengan masalah yang sama persis.

Tetapi, dalam C #, tidak ada konversi tersirat, sehingga enum adalah struktur yang lebih baik untuk digunakan. Anda jarang akan melihat salah satu dari dua pendekatan pertama yang digunakan.


5

Jika tidak ada perilaku yang terkait dengan nilai dan tidak ada informasi tambahan yang perlu Anda simpan di sampingnya dan bahasa tidak mendukung konversi implisit ke integer, saya akan menggunakan enum; untuk itulah mereka ada di sana.


1

Contoh lain yang mungkin membantu Anda dapat ditemukan di Java Efektif (item 30, jika Anda ingin mencarinya). Ini juga berlaku untuk C #.

Misalkan Anda menggunakan konstanta int untuk memodelkan hewan yang berbeda, katakanlah ular dan anjing.

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

int DOG_TERRIER=0;
int DOG_PITBULL=1;

Asumsikan ini adalah konstanta dan bahkan mungkin didefinisikan dalam kelas yang berbeda. Ini mungkin tampak baik bagi Anda dan mungkin benar-benar berhasil. Tetapi pertimbangkan baris kode ini:

snake.setType(DOG_TERRIER);

Jelas, ini adalah kesalahan. Kompiler tidak akan mengeluh dan kode akan dieksekusi. Tetapi ular Anda tidak akan berubah menjadi seekor anjing. Itu karena kompiler hanya melihat:

snake.setType(0);

Ular Anda sebenarnya adalah python (dan bukan terrier). Fitur ini disebut tipe safety dan ini sebenarnya alasan mengapa Anda menjadi contoh kode

var temp = new Politician { MostNotableGrievance = 1 };

tidak akan dikompilasi. MostNotableGrievance adalah MostNotableGrievance bukan bilangan bulat. Dengan enum Anda dapat mengekspresikan ini dalam sistem tipe. Dan itulah mengapa Martin Fowler dan saya pikir enumerasi itu hebat.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.