Có phải là thực tế xấu để lưu trữ các giá trị nhất định như chuỗi?


19

Đó là một tiêu đề rất mơ hồ nhưng tôi không thể nghĩ ra cách nào tốt hơn để diễn đạt nó. Nhưng, chỉ là một ví dụ, hãy nghĩ về hướng mà một nhân vật trong trò chơi đang di chuyển. Nó chỉ cảm thấy sai lầm khi sử dụng một chuỗi và sau đó làm những việc như thế if(character.direction == "left"). Dường như với tôi rằng nó để lại quá nhiều chỗ cho sai sót ngớ ngẩn, như vô tình sử dụng Lefthoặc lhoặc bất cứ điều gì thay vì left. Những nghi ngờ của tôi có đúng không? Nếu vậy, cách ưa thích để đạt được một cái gì đó như thế này là gì?


10
ý bạn là, ngoài enums nếu ngôn ngữ của bạn hỗ trợ những thứ đó?
hoffmale

12
Bạn có nghĩa là Stringly Typed ?
RubberDuck


Một số ngôn ngữ không thể lưu trữ các giá trị ngoài chuỗi, ví dụ : bash.
mouviciel

không biết tại sao nhưng tôi nhớ lại "xác định" trong C. Bạn có thể làm những việc như: #define đúng sai
linuxunil

Câu trả lời:


28

Nếu ngôn ngữ bạn đang sử dụng hỗ trợ việc sử dụng enum, tôi sẽ sử dụng chúng. Nó cho phép bạn giới hạn số lượng tùy chọn có sẵn cho một loại nhất định. ví dụ: trong Java:

public enum Direction {
    LEFT,
    RIGHT,
    UP,
    DOWN
}

2
Câu trả lời này không góp phần tìm hiểu vấn đề OP; Tôi thấy không có lý do ở đây, chỉ có một ý kiến ​​giới hạn trong phạm vi đối với các ngôn ngữ có lớp học enumnhư java. Nhiều ngôn ngữ (ví dụ VBA) có enum, nhưng nó có thể không phải là lựa chọn tốt nhất vì nó thiếu cùng một bằng chứng trong tương lai. Xem câu trả lời của tôi cho một số cuộc thảo luận về lý do tại sao enummột mình là một giải pháp không đầy đủ, thường dễ vỡ và ngôn ngữ cụ thể.
sqykly

12

Đó là cách thực hành khủng khiếp để sử dụng các chuỗi ký tự (hoặc số ma thuật) trong mã.

Enums là tốt, hoặc ít nhất là sử dụng hằng số (enums tất nhiên chỉ là một loại bao bọc cho một hoặc nhiều hằng số liên quan).

const string LEFT = "left"
if (someThing == LEFT) { doStuff(); }

Công tắc đơn giản này có rất nhiều lợi thế tôi thậm chí không biết bắt đầu giải thích chúng ở đâu (ít nhất là không có thêm cà phê). Đọc các số ma thuật, đó là khái niệm chính xác tương tự, chỉ áp dụng cho một giá trị số thay vì giá trị chuỗi.

Đây là một giới thiệu nhanh, tôi chắc chắn còn hàng trăm: /programming/47882/what-is-a-magic-number-and-why-is-it-bad


1
Vấn đề tôi gặp phải với cách tiếp cận này là bạn gặp lỗi đánh máy như --const chuỗi LEFT = "letf" - trong khi đó nếu bạn làm việc với các enum bị bắt tại thời gian biên dịch.
Pieter B

@PieterB - Tôi nghĩ rằng câu hỏi của OP đủ rộng và ví dụ cho "bên trái" chỉ là một ví dụ tùy ý - trong đó tất cả các trường hợp sẽ không khớp với enum. Tôi đồng ý rằng đối với một tập hợp các hướng cụ thể, enum là lựa chọn tốt nhất, nhưng câu trả lời của tôi có nghĩa chung chung hơn ở chỗ "nếu bạn đặt nó thành một nghĩa đen, ít nhất là biến nó thành một hằng số" (tất nhiên, enums là một loại hằng số)
jleach

3
Tôi đã rất vui khi làm việc với một ứng dụng được chỉ định những ngày cuối tuần là những ngày mà tên bắt đầu bằng "s". Rất ngạc nhiên khi họ nói về "sự thật" rằng khi họ quốc tế hóa ứng dụng rằng Pháp chỉ có một ngày cuối tuần.
Pieter B

4
Gọi nó là vi mô hóa, nhưng một số ngôn ngữ có thể hiểu đây là so sánh giá trị và lãng phí rất nhiều thời gian để kiểm tra từng ký tự trong chuỗi. Thậm chí nhiều khả năng nếu bạn làm như người hỏi đã làm và so sánh với chuỗi mã hóa "trái". Nếu không cần giá trị không đổi là một chuỗi (nghĩa là bạn không in nó), tốt hơn là sử dụng một const int thay thế.
Devsman

4

Câu trả lời ngắn: Có, các chuỗi không lý tưởng cho bất kỳ tác vụ nào ngoài việc lưu trữ và truy cập một chuỗi các ký tự văn bản và ngay cả khi các bit cơ bản của trừu tượng là các chuỗi, có những lợi ích khi gọi chúng là biến hoặc hằng .

Câu trả lời dài: hầu hết các ngôn ngữ cung cấp các loại gần với miền của vấn đề của bạn và ngay cả khi chúng không có, chúng có thể có một số phương pháp mà bạn có thể xác định leftlà một thực thể khác với right(v.v.). Biến nó thành một chuỗi bằng cách sử dụng "left"chỉ đơn giản là bị mất ngôn ngữ và chức năng hữu ích của IDE như kiểm tra lỗi. Ngay cả khi bạn phải sử dụng

left = "left";

hoặc một cái gì đó tương đương, nó có lợi thế khi sử dụng chuỗi khi bạn tham chiếu nó qua mã của bạn. Ví dụ,

if (player.direction == Left)

sẽ được phát hiện là lỗi thời gian biên dịch (trường hợp tốt nhất, java và tương tự) hoặc bị đánh dấu bởi bất kỳ IDE nào có giá trị bit của nó là sai (trường hợp xấu nhất, cơ bản và như vậy).

Ngoài ra, có khái niệm leftlà một thực thể có thể tham chiếu sẽ giúp bạn điều chỉnh bất kỳ hướng nào bạn quyết định đi với loại hướng. Bằng cách sử dụng "left", hướng được cam kết là a String. Nếu bạn tìm thấy hoặc tạo ra một sự trừu tượng hóa tốt hơn cho loại, bạn cần phải đi tìm toàn bộ phần thân mã của mình thay đổi mọi trường hợp "left". Một hằng hoặc biến sẽ không yêu cầu bất kỳ thay đổi nếu loại được xây dựng.

Loại bạn thực sự muốn là đặc biệt cho miền của bạn. Mã của bạn có kế hoạch để làm gì với của bạn left? Có lẽ bạn sẽ muốn phản chiếu trục x của một kết cấu hoặc sprite hướng về bên trái hoặc di chuyển về phía trước; nếu vậy, bạn có thể muốn tạo leftmột đối tượng bằng một thuộc tính hoặc phương thức phản ánh hành vi đó, với rightđối tượng của bạn có kết quả không được nhân đôi ngược lại. Bạn có thể thực hiện logic đó trong một đối tượng đại diện cho các họa tiết hoặc kết cấu, trong trường hợp đó bạn sẽ phải chịu đựng khi sử dụng "left"thay vì các leftlý do đã nêu ở trên.

Trong Java (và có lẽ các ngôn ngữ khác mà tôi chưa biết), enumlà một lựa chọn tốt vì trong Java, enumcũng hữu ích final classvới một tập hợp hữu hạn. Bạn có thể xác định các hành vi nếu bạn cần chúng và bạn có thể chuyển sang một lớp mở mà không gặp rắc rối nào bên ngoài tệp khai báo enum, nhưng nhiều ngôn ngữ xem enumlà một kiểu nguyên thủy hoặc yêu cầu cú pháp đặc biệt để sử dụng. Điều đó làm rò rỉ enummui xe thành mã không có kinh doanh với nó và có thể cần phải được sửa chữa sau này. Hãy chắc chắn rằng bạn hiểu khái niệm ngôn ngữ của enumbạn trước khi bạn xem xét tùy chọn.


2

Bạn đã không chỉ định ngôn ngữ của mình, nhưng để đưa ra câu trả lời chung chung, bạn nên sử dụng các chuỗi khi bạn thực sự cần văn bản, không phải để thể hiện một cái gì đó. Ví dụ bạn đưa ra chẳng hạn sẽ không được chấp nhận trong phần mềm sản xuất.

Mỗi ngôn ngữ có các loại dữ liệu cơ bản khác nhau và bạn nên sử dụng một loại phù hợp nhất cho nhiệm vụ. Để sử dụng ví dụ của bạn, bạn có thể sử dụng các hằng số để biểu diễn các trạng thái khác nhau. Vì vậy, bên trái có thể có giá trị bằng 0 và bên phải sẽ là một. Bên cạnh lý do bạn đã đề cập, các giá trị số chiếm ít không gian (bộ nhớ) và việc so sánh các số luôn nhanh hơn so với nhiều chuỗi ký tự.

Như đã đề xuất, hãy sử dụng bảng liệt kê nếu ngôn ngữ của bạn cho phép. Trong ví dụ cụ thể của bạn, rõ ràng đó là sự lựa chọn tốt hơn.


Tốc độ và bộ nhớ dường như không quan trọng. Điều đó nói rằng, thích enums hoặc hằng. Các chuỗi có ý nghĩa hơn nếu dữ liệu đến từ một định dạng văn bản, ví dụ XML, nhưng thậm chí sau đó phải phù hợp với tất cả các giới hạn hoặc tất cả các bản khai. Hoặc, luôn luôn chuyển đổi , e g. if (uppercase(foo) == "LEFT")
dùng949300

2
@ user949300 Chuyển đổi chuỗi thành tất cả chữ hoa hoặc tất cả chữ thường đều không đáng tin cậy. Có khả năng ai đó có thể quyết định đến thăm một địa điểm khác (hoặc mở rộng kinh doanh ở đó), như Thổ Nhĩ Kỳ , và phá vỡ các so sánh chuỗi vỏ trên (hoặc vỏ dưới) ngây thơ.
8bittree

Quan tâm đến tốc độ và bộ nhớ luôn là thông lệ tốt, chúng không phải là tài nguyên vô hạn. Mặc dù bạn có thể hy sinh nó một cách có ý thức vì mục đích dễ đọc hoặc một số sự đánh đổi khác, đó không phải là trường hợp ở đây. Nhưng không quan tâm đến chúng có thể dễ dàng dẫn đến các ứng dụng chậm chạp hoặc lãng phí.
Nagev

So sánh các chuỗi có thể chậm hơn 50 lần so với so sánh các enum. Hầu hết thời gian nó không quan trọng. Nhưng nếu mã của bạn đã chậm một chút, sự khác biệt đó có thể khiến nó không được chấp nhận. Tất nhiên quan trọng hơn là trình biên dịch không thể giúp bạn với bất kỳ lỗi chính tả nào nếu bạn sử dụng chuỗi.
gnasher729

1

Chỉ cần sử dụng kiểu dữ liệu có ý nghĩa trong tình huống của bạn.

Việc sử dụng stringloại như một giao tiếp nội bộ / ứng dụng cơ bản không phải là một vấn đề. Trên thực tế, đôi khi nên sử dụng các chuỗi: Nếu bạn có API công khai, có thể mong muốn các giá trị đi vào và ra khỏi API có thể đọc được. Nó giúp mọi người sử dụng API của bạn để tìm hiểu và gỡ lỗi dễ dàng hơn.

Vấn đề thực sự với mã của bạn nằm ở việc lặp lại giá trị.

Đừng làm điều này:

if (direction == 'left') {
  goLeft();
}

Nếu bạn mắc lỗi đánh máy ở một trong những điều kiện này hoặc cách sử dụng "trái" khác ở đâu đó, bạn có thể không thể thực hiện một cách hiệu quả một tìm kiếm trên toàn bộ cơ sở mã cho nó, vì bạn đã nhầm nó!

Thay vào đó, mã theo bảng liệt kê hoặc hằng số - tùy thuộc vào loại nào hỗ trợ loại dữ liệu bạn cần trong (các) ngôn ngữ bạn đang sử dụng.

Điều đó có nghĩa là LEFTnên được chỉ định một lầnchỉ một lần trong ứng dụng của bạn. Và, phần còn lại của mã của bạn sẽ tận dụng các enum hoặc hằng số đó:

const string LEFT = "left";

if (direction == LEFT) {
  goLeft();
}

Điều này cũng cho phép bạn dễ dàng thay đổi loại dữ liệu bạn sử dụng cho chỉ đường của mình sau này, nếu bạn chọn.


1

Có phải là thực hành xấu?

Có thể là có, nhưng nó phụ thuộc vào chính xác những gì bạn đang làm, và cũng phụ thuộc vào ngôn ngữ lập trình bạn đang sử dụng.

Trong ví dụ của bạn, chúng ta có thể suy ra rằng có một tập hợp nhỏ các giá trị cố định mà một hướng có thể thực hiện. Trong trường hợp này, không có lợi thế trong việc sử dụng một chuỗi và một số nhược điểm nhất định. Ví dụ này:

  • Một enumloại sẽ thích hợp hơn, nếu ngôn ngữ lập trình của bạn hỗ trợ điều này.
  • Mặt khác, các hằng số nguyên được đặt tên là con đường để đi, với một định nghĩa duy nhất cho các hằng số.

Nhưng câu trả lời có thể khác nếu tập hợp các giá trị có thể thay đổi động hoặc cần phát triển theo thời gian. Trong hầu hết các ngôn ngữ, việc thay đổi một enumloại (ví dụ để thêm hoặc xóa giá trị) yêu cầu biên dịch lại hoàn chỉnh. (Ví dụ: loại bỏ một giá trị enum trong Java sẽ phá vỡ tính tương thích nhị phân.) Và áp dụng tương tự cho các hằng số nguyên được đặt tên.

Trong các tình huống như thế này, một giải pháp khác có thể được yêu cầu và sử dụng chuỗi là một trong các tùy chọn. (Và bạn có thể kết hợp chuỗi ký tự chuỗi và hằng số được đặt tên ... nếu kịch bản đòi hỏi một lõi cố định với các phần mở rộng động.)


Nếu bạn đang triển khai trong Java, character.direction == "left"thì thực tế không tốt vì một lý do khác. Bạn không nên sử dụng ==để so sánh các chuỗi, bởi vì nó đang kiểm tra danh tính đối tượng hơn là bình đẳng chuỗi. (Nó sẽ hoạt động nếu bạn có thể đảm bảo rằng tất cả các phiên bản của "left"chuỗi đã được thực tập, nhưng điều đó đòi hỏi phải phân tích toàn bộ ứng dụng.)


1
Những gì có vẻ như cố định bao giờ thường hóa ra là không cố định chút nào. Bốn hướng chẳng hạn có thể trở thành tám.
Jimmy T.

@JimmyT. - Điều đó phụ thuộc vào bối cảnh. Ví dụ, trong một trò chơi, việc thay đổi số hướng mà nhân vật có thể di chuyển từ 4 thành 8 sẽ đòi hỏi phải sửa chữa hoàn toàn các quy tắc của trò chơi và mã thực hiện các quy tắc. Sử dụng Stringđể thể hiện các hướng sẽ tạo ra sự khác biệt gần như bằng không với số lượng công việc liên quan để thực hiện các thay đổi. Một cách tiếp cận hợp lý hơn là giả định rằng số lượng hướng sẽ không thay đổi và nhận ra rằng bạn sẽ có rất nhiều việc phải làm theo cách đó nếu luật chơi thay đổi một cách cơ bản.
Stephen C

@JimmyT - Hoàn toàn đồng ý Tôi đã thấy điều này xảy ra nhiều lần, bạn cũng khiến các nhà phát triển rút ngắn việc xác định lại chuỗi, ví dụ, sản phẩm = "Tùy chọn" trở thành sản phẩm = "Option_or_Future" hoàn toàn làm mất ý nghĩa của mã.
Chris Milburn

-3

Theo đề xuất, bạn nên sử dụng Enums. Tuy nhiên, chỉ sử dụng Enum để thay thế cho Strigns là không đủ IMHO. Trong ví dụ particualr của bạn, vận tốc của người chơi thực sự phải là một vectơ bằng phương pháp vật lý:

class Vector {
  final double x;
  final double y;
}

Vectơ này sau đó nên được sử dụng để cập nhật vị trí người chơi mỗi tích tắc.

Sau đó, bạn có thể kết hợp điều đó với một enum để dễ đọc hơn:

enum Direction {
  UP(new Vector(0, 1)),
  RIGHT(new Vector(1, 0)),
  DOWN(new Vector(0, -1)),
  LEFT(new Vector(-1, 0));

  private Vector direction;

  Direction(Vector v) {
    this.direction = v;
  }

  public Vector getVector() { return this.direction; }
}

4
-1 Bạn sẽ đi quá xa vào ví dụ anh ấy đưa ra.
Pieter B
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.