Các phương pháp hay nhất để sử dụng và bền bỉ enums


77

Tôi đã thấy một số câu hỏi / thảo luận ở đây về cách tốt nhất để xử lý và duy trì các giá trị giống enum (ví dụ: Dữ liệu duy trì phù hợp với enum , Cách duy trì một enum bằng cách sử dụng NHibernate ) và tôi muốn hỏi consenus chung là gì .

Đặc biệt:

  • Các giá trị này nên được xử lý như thế nào trong mã?
  • Làm thế nào để chúng được duy trì trong cơ sở dữ liệu (dưới dạng văn bản / dưới dạng số)?
  • Sự cân bằng của các giải pháp khác nhau là gì?

Lưu ý: Tôi đã chuyển phần giải thích ban đầu có trong câu hỏi này thành câu trả lời.

Câu trả lời:


19

Tôi đồng ý với phần lớn những gì bạn nói. Tuy nhiên, tôi muốn bổ sung một điều về sự tồn tại của các enum: Tôi không tin rằng việc tạo ra các enum tại thời điểm xây dựng từ các giá trị DB là có thể chấp nhận được, nhưng tôi cũng nghĩ rằng kiểm tra thời gian chạy không phải là một giải pháp tốt . Tôi định nghĩa phương tiện thứ ba: có một bài kiểm tra đơn vị sẽ kiểm tra các giá trị của enum so với cơ sở dữ liệu. Điều này ngăn chặn sự phân kỳ "ngẫu nhiên" và tránh chi phí kiểm tra các phần tử so với cơ sở dữ liệu mỗi khi mã được chạy.


14

Bài báo ban đầu có vẻ ổn đối với tôi. Tuy nhiên, dựa trên các nhận xét, có vẻ như một số nhận xét liên quan đến Java enums có thể làm rõ một số điều.

Enum type trong Java là một lớp theo định nghĩa, nhưng nhiều lập trình viên có xu hướng quên điều này, bởi vì họ thích liên hệ nó với "danh sách các giá trị được phép" như trong một số ngôn ngữ khác. Nó còn hơn thế nữa.

Vì vậy, để tránh những câu lệnh switch đó, có thể hợp lý khi đặt một số mã và phương thức bổ sung trong lớp enum. Hầu như không bao giờ cần tạo một "lớp thực giống enum" riêng biệt.

Cũng nên xem xét điểm của tài liệu - bạn có muốn ghi lại ý nghĩa thực sự của enum của bạn trong cơ sở dữ liệu không? Trong mã nguồn phản ánh các giá trị (kiểu enum của bạn) hay trong một số tài liệu bên ngoài? Cá nhân tôi thích mã nguồn hơn.

Nếu bạn muốn trình bày các giá trị enum dưới dạng số nguyên trong cơ sở dữ liệu do tốc độ hoặc lý do nào đó, thì ánh xạ đó cũng phải nằm trong enum Java. Bạn sẽ nhận được ánh xạ tên chuỗi theo mặc định và tôi đã hài lòng với điều đó. Có một số thứ tự được liên kết với mỗi giá trị enum, nhưng việc sử dụng trực tiếp số đó làm ánh xạ giữa mã và cơ sở dữ liệu không sáng sủa lắm, vì số thứ tự đó sẽ thay đổi nếu ai đó sắp xếp lại các giá trị trong mã nguồn. Hoặc thêm các giá trị enum bổ sung vào giữa các giá trị hiện có. Hoặc loại bỏ một số giá trị.

(Tất nhiên, nếu ai đó thay đổi tên của enum trong mã nguồn, ánh xạ chuỗi mặc định cũng trở nên sai lệch, nhưng điều đó ít có khả năng xảy ra vô tình hơn. Và bạn có thể dễ dàng bảo vệ khỏi điều đó nếu cần bằng cách kiểm tra thời gian chạy và kiểm tra các ràng buộc trong cơ sở dữ liệu như đã đề xuất ở đây.)


1
Có hai tình huống cần hỗ trợ: ai đó sắp xếp lại thứ tự các enums trong tệp của tôi HOẶC ai đó thực hiện một số cấu trúc lại (để làm rõ các lựa chọn tên ban đầu không tốt) và phá vỡ dữ liệu vẫn tồn tại. Tôi nghĩ cái sau quan trọng hơn, và thứ tự là cách để duy trì sự bền bỉ của dữ liệu.
Justin

7

Tôi đã cố gắng tóm tắt sự hiểu biết của mình. Vui lòng chỉnh sửa điều này nếu bạn có bất kỳ chỉnh sửa nào. Vì vậy, ở đây nó đi:

Trong mã

Trong mã, enum nên được xử lý bằng cách sử dụng kiểu enum gốc của ngôn ngữ (ít nhất là trong Java và C #) hoặc sử dụng một cái gì đó như "kiểu enum an toàn kiểu" . Việc sử dụng các hằng số thuần túy (Số nguyên hoặc tương tự) không được khuyến khích, vì bạn mất an toàn kiểu (và khó hiểu giá trị nào là đầu vào hợp pháp cho ví dụ như một phương thức).

Sự lựa chọn giữa hai điều này phụ thuộc vào số lượng chức năng bổ sung được gắn vào enum:

  • Nếu bạn muốn đưa vô số chức năng vào enum (điều này tốt, vì bạn tránh chuyển () vào nó mọi lúc), một lớp thường thích hợp hơn.
  • Mặt khác, đối với các giá trị giống enum đơn giản, enum của ngôn ngữ thường rõ ràng hơn.

Đặc biệt, ít nhất trong Java một enum không thể kế thừa từ một lớp khác, vì vậy nếu bạn có một số enum có hành vi tương tự mà bạn muốn đưa vào một lớp cha, bạn không thể sử dụng enum của Java.

Enums dai dẳng

Để tồn tại enum, mỗi giá trị enum phải được gán một ID duy nhất. Đây có thể là một số nguyên hoặc một chuỗi ngắn. Một chuỗi ngắn được ưu tiên hơn, vì nó có thể dễ nhớ (giúp DBAs, v.v. hiểu dữ liệu thô trong db dễ dàng hơn).

  • Trong phần mềm, mọi enum sau đó phải có các chức năng ánh xạ để chuyển đổi giữa enum (để sử dụng bên trong phần mềm) và giá trị ID (để tồn tại). Một số khuôn khổ (ví dụ: (N) Hibernate) có hỗ trợ hạn chế để thực hiện việc này tự động. Nếu không, bạn phải đặt nó vào kiểu / lớp enum.
  • Cơ sở dữ liệu nên (lý tưởng) chứa một bảng cho mỗi enum liệt kê các giá trị pháp lý. Một cột sẽ là ID (xem ở trên), là PK. Các cột bổ sung có thể có ý nghĩa đối với ví dụ như mô tả Tất cả các cột của bảng sẽ chứa các giá trị từ enum đó sau đó có thể sử dụng "bảng enum" này làm FK. Điều này đảm bảo rằng các giá trị enum không chính xác không bao giờ có thể tồn tại và cho phép DB "tự đứng vững".

Một vấn đề với cách tiếp cận này là danh sách các giá trị enum hợp pháp tồn tại ở hai nơi (mã và cơ sở dữ liệu). Điều này là khó tránh và do đó thường được coi là chấp nhận được, nhưng có hai lựa chọn thay thế:

  • Chỉ giữ danh sách các giá trị trong DB, tạo kiểu enum tại thời điểm xây dựng. Thanh lịch, nhưng có nghĩa là cần phải có kết nối DB để bản dựng chạy, điều này có vẻ có vấn đề.
  • Xác định danh sách các giá trị trong mã là có thẩm quyền. Kiểm tra các giá trị trong DB khi chạy (thường là khi khởi động), khiếu nại / hủy bỏ khi không khớp.

6

Trong quá trình xử lý mã cho C #, bạn đã bỏ lỡ việc xác định xóa giá trị 0. Tôi hầu như không thất bại luôn luôn khai báo giá trị đầu tiên của mình là:

public enum SomeEnum
{
    None = 0,
}

Vì vậy, để phục vụ như một giá trị null. Bởi vì kiểu sao lưu là một số nguyên và một số nguyên được mặc định là 0 nên nó rất hữu ích ở nhiều nơi để biết liệu một enum đã thực sự được thiết lập theo chương trình hay chưa.


6
Tôi không đồng ý. Điều này sẽ chỉ có ý nghĩa nếu đôi khi bạn để các biến chưa được khởi tạo, điều mà tôi sẽ coi là hành động xấu nghiêm trọng. Tôi thường thấy ý tưởng này có giá trị "none", nhưng tôi tin rằng nó chỉ ẩn vấn đề thực sự (biến chưa được khởi tạo).
sleske

3
Làm thế nào nó ẩn vấn đề? Nó làm cho nó rõ ràng giống như một int nullable. Tôi để các giá trị không được khởi tạo trong mã vì tôi biết CLR sẽ đặt chúng thành gì theo mặc định. Chúng vẫn được khởi tạo chỉ là ẩn.
Quibblesome

Chà, đó có lẽ là vấn đề về phong cách. Tôi thực sự tin tưởng vào việc khởi tạo đầy đủ tất cả các biến khi khai báo (hoặc nhiều nhất là trong if-else ngay sau khi khai báo). Nếu không, bạn có thể quên khởi tạo chúng, đặc biệt nếu dòng mã phức tạp. Xem thêm c2.com/cgi/wiki?SingleStepConstructor .
sleske

5

Java hoặc C # nên luôn sử dụng enum trong mã. Tuyên bố từ chối trách nhiệm: Nền tảng của tôi là C #.

Nếu giá trị được duy trì trong cơ sở dữ liệu, các giá trị tích phân của mỗi thành viên liệt kê phải được xác định rõ ràng để thay đổi mã sau này không vô tình làm thay đổi các giá trị enum đã dịch và do đó hành vi ứng dụng.

Các giá trị phải luôn được duy trì trong cơ sở dữ liệu dưới dạng các giá trị tích hợp, để bảo vệ khỏi việc tái cấu trúc tên enum. Giữ tài liệu về mỗi kiểu liệt kê trong wiki và thêm nhận xét vào trường cơ sở dữ liệu trỏ đến trang wiki ghi lại kiểu tài liệu. Cũng thêm tài liệu XML vào kiểu enum có chứa liên kết đến mục nhập wiki để nó có sẵn thông qua Intellisense.

Nếu bạn sử dụng một công cụ để tạo mã CRUD, nó phải có khả năng xác định kiểu liệt kê để sử dụng cho một cột để các đối tượng mã được tạo luôn sử dụng các thành viên được liệt kê.

Nếu logic tùy chỉnh cần được áp dụng cho một thành viên điều tra, bạn có một số tùy chọn:

  • Nếu bạn có enum MyEnum, hãy tạo một lớp tĩnh MyEnumInfo cung cấp các phương thức tiện ích để khám phá thông tin bổ sung về thành viên enum, bằng các câu lệnh switch hoặc bất kỳ phương tiện nào cần thiết. Việc thêm "Thông tin" vào cuối tên enum trong tên lớp đảm bảo rằng chúng sẽ ở cạnh nhau trong IntelliSense.
  • Trang trí các thành viên điều tra với các thuộc tính để chỉ định các tham số bổ sung. Ví dụ, chúng tôi đã phát triển một điều khiển EnumDropDown tạo một menu thả xuống ASP.NET chứa đầy các giá trị liệt kê và EnumDisplayAttribute chỉ định văn bản hiển thị được định dạng độc đáo để sử dụng cho từng thành viên.

Tôi chưa thử điều này, nhưng với SQL Server 2005 trở lên, về mặt lý thuyết, bạn có thể đăng ký mã C # với cơ sở dữ liệu chứa thông tin enum và khả năng chuyển đổi giá trị thành enum để sử dụng trong các khung nhìn hoặc cấu trúc khác, tạo ra một phương pháp dịch dữ liệu theo cách dễ dàng hơn cho các DBA sử dụng.


1 gán một cách rõ ràng giá trị là cách duy nhất để tránh "tham nhũng" khi bạn thay đổi enum
ashes999

3

Imho, đối với phần mã:

Bạn nên luôn sử dụng kiểu 'enum' cho các bảng liệt kê của mình, về cơ bản bạn sẽ nhận được rất nhiều phần mềm miễn phí nếu làm như vậy: Nhập an toàn, đóng gói và tránh chuyển đổi, sự hỗ trợ của một số bộ sưu tập như EnumSetEnumMap và mã rõ ràng.

đối với phần kiên trì, bạn luôn có thể duy trì biểu diễn chuỗi của enum và tải lại bằng phương thức enum.valueOf (String).


Đồng ý về nguyên tắc, tuy nhiên ít nhất trong Java "enum" bị hạn chế ở chỗ nó không thể có lớp cha (như đã lưu ý ở trên), vì vậy đôi khi lớp "typeafe enum" có lẽ tốt hơn.
sleske

3

Lưu trữ giá trị văn bản của một enum trong cơ sở dữ liệu ít được ưu tiên hơn so với lưu trữ một số nguyên, do cần thêm không gian và tìm kiếm chậm hơn. Nó có giá trị ở chỗ nó có nhiều ý nghĩa hơn là một con số, tuy nhiên cơ sở dữ liệu là để lưu trữ và lớp trình bày là để làm cho mọi thứ trông đẹp mắt.


1
Giá trị int của enum không được đảm bảo là giống nhau theo thời gian.
Miguel Ping

Ngoài ra, nếu bạn sử dụng một chuỗi ngắn, hiệu suất sẽ giống nhau. Một char (2) mất 2 byte, một int thường cũng mất 2 hoặc 4.
sleske

6
@Miguel Ping: Ý tưởng là gán rõ ràng một ID (int hoặc char) cho mỗi enum. Sử dụng int được tạo bên trong của enum thực sự rất nguy hiểm.
sleske

1
nếu ý nghĩa vượt quá giá trị số nguyên được mong muốn trong DB, một bảng ánh xạ các giá trị số nguyên thành các chuỗi có thể đọc được của con người là những gì tôi sẽ sử dụng. Ngoài ra, có, các giá trị số nguyên không được thay đổi sau này. không nên là một vấn đề mặc dù; sự liên quan duy nhất mà các giá trị số nguyên enum cơ bản phải có là chúng khác với các thành viên khác của enum. (nghĩa là: họ không nên có lý do gì để thay đổi). nếu các giá trị số nguyên có ý nghĩa ngoài nhận dạng duy nhất, có thể nên sử dụng cấu trúc dữ liệu khác.
Dave Cousineau

3

Vâng, theo kinh nghiệm của tôi, việc sử dụng enum cho bất kỳ thứ gì khác ngoài việc chuyển các tùy chọn (dưới dạng cờ) đến một cuộc gọi phương thức tức thì, kết quả là switch-ing tại một số điểm.

  • Nếu bạn định sử dụng enum trên toàn bộ mã của mình, thì bạn có thể sẽ gặp phải mã không dễ bảo trì như vậy (tai tiếng switch câu nói )
  • Mở rộng enums là một nỗi đau. Bạn thêm một mục enum mới và kết thúc bằng việc xem qua tất cả mã của bạn để kiểm tra tất cả các điều kiện.
  • Với .NET 3.5, bạn có thể thêm các phương thức mở rộng vào enums để làm cho chúng hoạt động giống các lớp hơn một chút. Tuy nhiên, việc thêm chức năng thực theo cách này không dễ dàng như vậy vì nó vẫn chưa phải là một lớp (bạn sẽ phải sử dụng switch-es trong các phương thức mở rộng của mình nếu không phải ở nơi khác.

Vì vậy, đối với một thực thể giống enum với nhiều chức năng hơn, bạn nên dành chút thời gian và tạo nó dưới dạng một lớp, với một số điều cần lưu ý:

  • Để làm cho lớp của bạn hoạt động giống như một enum, bạn có thể buộc mỗi lớp dẫn xuất phải khởi tạo như một Singleton hoặc ghi đè các Equals để cho phép so sánh giá trị của các trường hợp khác nhau.
  • Nếu lớp của bạn giống enum, điều đó có nghĩa là nó không được chứa trạng thái có thể tuần tự hóa - việc giải mã hóa chỉ có thể thực hiện được từ loại của nó (một loại "ID", như bạn đã nói).
  • Logic bền vững chỉ nên được giới hạn trong lớp cơ sở, nếu không việc mở rộng "enum" của bạn sẽ là một cơn ác mộng. Trong trường hợp bạn sử dụng mẫu Singleton, bạn sẽ cần đảm bảo giải mã hóa phù hợp thành các thể hiện singleton.

3

Mỗi lần bạn tìm thấy chính mình bằng cách sử dụng "số ma thuật" trong mã thay đổi thành enums. Bên cạnh việc tiết kiệm thời gian (vì phép thuật sẽ biến mất khi lỗi đến ...), nó sẽ tiết kiệm đôi mắt và trí nhớ của bạn (enum có ý nghĩa làm cho mã dễ đọc hơn và tự ghi lại), vì hãy đoán xem - bạn có lẽ là người duy trì và phát triển nhất mã của riêng bạn


1

Tôi biết đây là một diễn đàn cũ, điều gì sẽ xảy ra nếu cơ sở dữ liệu có thể có những thứ khác tích hợp trực tiếp vào nó? Ví dụ: khi DB kết quả là mục đích DUY NHẤT của mã. Sau đó, bạn sẽ xác định các enum ở mỗi lần tích hợp. Tốt hơn là nên có chúng trong DB. Nếu không, tôi đồng ý với bài viết gốc.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.