Một số ma thuật là gì?
Tại sao nên tránh?
Có trường hợp nào phù hợp không?
Một số ma thuật là gì?
Tại sao nên tránh?
Có trường hợp nào phù hợp không?
Câu trả lời:
Số ma thuật là cách sử dụng trực tiếp một số trong mã.
Ví dụ: nếu bạn có (bằng Java):
public class Foo {
public void setPassword(String password) {
// don't do this
if (password.length() > 7) {
throw new InvalidArgumentException("password");
}
}
}
Điều này nên được tái cấu trúc để:
public class Foo {
public static final int MAX_PASSWORD_SIZE = 7;
public void setPassword(String password) {
if (password.length() > MAX_PASSWORD_SIZE) {
throw new InvalidArgumentException("password");
}
}
}
Nó cải thiện khả năng đọc mã và dễ bảo trì hơn. Hãy tưởng tượng trường hợp tôi đặt kích thước của trường mật khẩu trong GUI. Nếu tôi sử dụng số ma thuật, bất cứ khi nào kích thước tối đa thay đổi, tôi phải thay đổi ở hai vị trí mã. Nếu tôi quên một, điều này sẽ dẫn đến sự không nhất quán.
JDK là đầy đủ các ví dụ như trong Integer
, Character
và Math
các lớp học.
PS: Các công cụ phân tích tĩnh như FindBugs và PMD phát hiện việc sử dụng các số ma thuật trong mã của bạn và đề xuất tái cấu trúc.
TRUE
/ FALSE
)
Số ma thuật là một giá trị được mã hóa cứng có thể thay đổi ở giai đoạn sau, nhưng do đó khó có thể cập nhật.
Ví dụ: giả sử bạn có một Trang hiển thị 50 Đơn hàng cuối cùng trong Trang Tổng quan về "Đơn hàng của bạn". 50 là Số ma thuật ở đây, vì nó không được đặt theo tiêu chuẩn hoặc quy ước, đó là một số mà bạn tạo ra vì những lý do được nêu trong thông số kỹ thuật.
Bây giờ, những gì bạn làm là bạn có 50 ở những nơi khác nhau - tập lệnh SQL ( SELECT TOP 50 * FROM orders
), Trang web của bạn (50 đơn hàng cuối cùng của bạn), đăng nhập đơn hàng của bạn ( for (i = 0; i < 50; i++)
) và có thể nhiều nơi khác.
Bây giờ, điều gì xảy ra khi ai đó quyết định thay đổi 50 thành 25? hay 75? hay 153? Bây giờ bạn phải thay thế 50 ở tất cả các nơi, và bạn rất có thể bỏ lỡ nó. Tìm / Thay thế có thể không hoạt động, vì 50 có thể được sử dụng cho những thứ khác và thay thế một cách mù quáng 50 bằng 25 có thể có một số tác dụng phụ xấu khác (ví dụ như Session.Timeout = 50
cuộc gọi của bạn , cũng được đặt thành 25 và người dùng bắt đầu báo cáo quá thời gian chờ quá thường xuyên).
Ngoài ra, mã có thể khó hiểu, tức là " if a < 50 then bla
" - nếu bạn gặp phải ở giữa một hàm phức tạp, các nhà phát triển khác không quen thuộc với mã có thể tự hỏi "WTF là 50 ???"
Đó là lý do tại sao tốt nhất là có các số mơ hồ và tùy ý như vậy ở đúng 1 vị trí - " const int NumOrdersToDisplay = 50
", vì điều đó làm cho mã dễ đọc hơn (" if a < NumOrdersToDisplay
", điều đó cũng có nghĩa là bạn chỉ cần thay đổi nó ở 1 vị trí được xác định rõ.
Những nơi có Số ma thuật phù hợp là mọi thứ được xác định thông qua một tiêu chuẩn, nghĩa là SmtpClient.DefaultPort = 25
hoặc TCPPacketSize = whatever
(không chắc chắn nếu đó là tiêu chuẩn). Ngoài ra, mọi thứ chỉ được xác định trong 1 hàm có thể được chấp nhận, nhưng điều đó phụ thuộc vào Ngữ cảnh.
SmtpClient.DefaultPort = 25
có thể rõ ràng er hơn SmtpClient.DefaultPort = DEFAULT_SMTP_PORT
.
25
tất cả các ứng dụng và đảm bảo rằng bạn chỉ thay đổi các lần xuất hiện của 25
Cổng SMTP, không phải là 25, ví dụ như chiều rộng của cột bảng hoặc số hồ sơ để hiển thị trên một trang.
IANA
.
Bạn đã xem mục Wikipedia cho số ma thuật chưa?
Nó đi vào một chút chi tiết về tất cả các cách tham chiếu số ma thuật được thực hiện. Đây là một trích dẫn về số ma thuật như một thực hành lập trình xấu
Thuật ngữ số ma thuật cũng đề cập đến thực tiễn lập trình xấu về việc sử dụng số trực tiếp trong mã nguồn mà không cần giải thích. Trong hầu hết các trường hợp, điều này làm cho các chương trình khó đọc, hiểu và duy trì hơn. Mặc dù hầu hết các hướng dẫn tạo một ngoại lệ cho các số 0 và một, nhưng nên xác định tất cả các số khác trong mã là các hằng số được đặt tên.
Phép thuật: Không biết ngữ nghĩa
Hằng số tượng trưng -> Cung cấp cả ngữ cảnh chính xác và ngữ cảnh chính xác để sử dụng
Semantic: Ý nghĩa hoặc mục đích của một điều.
"Tạo một hằng số, đặt tên theo ý nghĩa và thay thế số đó bằng nó." - Martin Fowler
Đầu tiên, số ma thuật không chỉ là số. Bất kỳ giá trị cơ bản có thể là "ma thuật". Các giá trị cơ bản là các thực thể rõ ràng như số nguyên, số thực, số nhân, số float, ngày, chuỗi, booleans, ký tự, v.v. Vấn đề không phải là kiểu dữ liệu, mà là khía cạnh "ma thuật" của giá trị như nó xuất hiện trong văn bản mã của chúng tôi.
"Ma thuật" nghĩa là gì? Nói chính xác: Bằng "phép thuật", chúng tôi dự định chỉ ra ngữ nghĩa (ý nghĩa hoặc mục đích) của giá trị trong ngữ cảnh mã của chúng tôi; rằng nó không được biết, không thể biết, không rõ ràng hoặc khó hiểu. Đây là khái niệm "ma thuật". Giá trị cơ bản không phải là phép thuật khi ý nghĩa ngữ nghĩa hoặc mục đích tồn tại của nó nhanh chóng và dễ dàng được biết, rõ ràng và được hiểu (không gây nhầm lẫn) từ bối cảnh xung quanh mà không có từ trợ giúp đặc biệt (ví dụ: hằng số biểu tượng).
Do đó, chúng tôi xác định các số ma thuật bằng cách đo khả năng của người đọc mã để biết, rõ ràng và hiểu ý nghĩa và mục đích của một giá trị cơ bản từ bối cảnh xung quanh. Người đọc càng ít biết đến, ít rõ ràng và càng bối rối thì giá trị cơ bản càng "kỳ diệu".
Chúng tôi có hai kịch bản cho các giá trị cơ bản kỳ diệu của chúng tôi. Chỉ thứ hai là quan trọng chính cho các lập trình viên và mã:
Một sự phụ thuộc bao trùm của "ma thuật" là cách giá trị cơ bản đơn độc (ví dụ số) không có ngữ nghĩa thường được biết đến (như Pi), nhưng có một ngữ nghĩa được biết đến cục bộ (ví dụ chương trình của bạn), không hoàn toàn rõ ràng từ ngữ cảnh hoặc có thể bị lạm dụng trong bối cảnh tốt hay xấu.
Các ngữ nghĩa của hầu hết các ngôn ngữ lập trình sẽ không cho phép chúng ta sử dụng các giá trị cơ bản đơn độc, ngoại trừ (có lẽ) làm dữ liệu (tức là các bảng dữ liệu). Khi chúng ta gặp "số ma thuật", chúng ta thường làm như vậy trong một bối cảnh. Do đó, câu trả lời cho
"Tôi có thay thế số ma thuật này bằng hằng số tượng trưng không?"
Là:
"Làm thế nào nhanh chóng bạn có thể đánh giá và hiểu ý nghĩa ngữ nghĩa của số (mục đích của nó là ở đó) trong bối cảnh của nó?"
Với suy nghĩ này, chúng ta có thể nhanh chóng thấy một số như Pi (3.14159) không phải là "số ma thuật" khi được đặt trong bối cảnh thích hợp (ví dụ: 2 x 3.14159 x bán kính hoặc 2 * Pi * r). Ở đây, số 3.14159 được Pi nhận biết về mặt tinh thần mà không cần định danh hằng số tượng trưng.
Tuy nhiên, chúng tôi thường thay thế 3.14159 bằng một định danh hằng số tượng trưng như Pi vì độ dài và độ phức tạp của số. Các khía cạnh về độ dài và độ phức tạp của Pi (kết hợp với nhu cầu về độ chính xác) thường có nghĩa là định danh hoặc hằng số tượng trưng ít bị lỗi hơn. Công nhận "Pi" như một cái tên đơn giản là một phần thưởng tiện lợi, nhưng không phải là lý do chính để có hằng số.
Đặt các hằng số phổ biến như Pi, hãy tập trung chủ yếu vào các số có ý nghĩa đặc biệt, nhưng những ý nghĩa đó bị hạn chế trong vũ trụ của hệ thống phần mềm của chúng tôi. Một số như vậy có thể là "2" (dưới dạng giá trị nguyên cơ bản).
Nếu tôi tự sử dụng số 2, câu hỏi đầu tiên của tôi có thể là: "2" nghĩa là gì? Ý nghĩa của "2" tự nó là không xác định và không thể biết được nếu không có ngữ cảnh, khiến cho việc sử dụng nó không rõ ràng và khó hiểu. Mặc dù chỉ có "2" trong phần mềm của chúng tôi sẽ không xảy ra do ngữ nghĩa ngôn ngữ, chúng tôi muốn thấy rằng "2" tự nó không mang ngữ nghĩa đặc biệt hoặc mục đích rõ ràng là một mình.
Hãy đặt "2" đơn độc của chúng tôi trong bối cảnh : padding := 2
, trong đó bối cảnh là "GUI Container". Trong ngữ cảnh này, ý nghĩa của 2 (dưới dạng pixel hoặc đơn vị đồ họa khác) cung cấp cho chúng tôi đoán nhanh về ngữ nghĩa của nó (ý nghĩa và mục đích). Chúng ta có thể dừng lại ở đây và nói rằng 2 là ổn trong bối cảnh này và không có gì khác chúng ta cần biết. Tuy nhiên, có lẽ trong vũ trụ phần mềm của chúng tôi, đây không phải là toàn bộ câu chuyện. Có nhiều hơn thế, nhưng "padding = 2" như một bối cảnh không thể tiết lộ nó.
Hãy giả vờ thêm rằng 2 là phần đệm pixel trong chương trình của chúng tôi thuộc loại "default_padding" trong toàn hệ thống của chúng tôi. Do đó, viết hướng dẫn padding = 2
là không đủ tốt. Khái niệm "mặc định" không được tiết lộ. Chỉ khi tôi viết: padding = default_padding
như một bối cảnh và sau đó ở nơi khác: default_padding = 2
tôi mới nhận ra đầy đủ ý nghĩa tốt hơn và đầy đủ hơn (ngữ nghĩa và mục đích) của 2 trong hệ thống của chúng tôi.
Ví dụ trên là khá tốt vì bản thân "2" có thể là bất cứ thứ gì. Chỉ khi chúng tôi giới hạn phạm vi và phạm vi hiểu biết đối với "chương trình của tôi" trong đó 2 là default_padding
phần GUI UX của "chương trình của tôi", cuối cùng chúng ta mới hiểu "2" trong ngữ cảnh phù hợp. Ở đây "2" là một số "ma thuật", được xác định là hằng số tượng trưng default_padding
trong ngữ cảnh GUI UX của "chương trình của tôi" để làm cho nó được sử dụng default_padding
nhanh chóng trong bối cảnh lớn hơn của mã kèm theo.
Do đó, bất kỳ giá trị cơ bản nào, có ý nghĩa (ngữ nghĩa và mục đích) không thể được hiểu một cách đầy đủ và nhanh chóng là một ứng cử viên tốt cho hằng số biểu tượng thay cho giá trị cơ bản (ví dụ số ma thuật).
Các số trên thang đo cũng có thể có ngữ nghĩa. Ví dụ, giả vờ rằng chúng tôi đang tạo ra một trò chơi D & D, nơi chúng tôi có khái niệm về một con quái vật. Đối tượng quái vật của chúng ta có một tính năng được gọi life_force
là số nguyên. Các con số có ý nghĩa không thể biết hoặc rõ ràng mà không có từ để cung cấp ý nghĩa. Vì vậy, chúng tôi bắt đầu bằng cách tùy tiện nói:
Từ các hằng số tượng trưng ở trên, chúng ta bắt đầu có được một bức tranh tinh thần về sự sống, sự chết và "sự bất tử" (và sự phân nhánh hoặc hậu quả có thể xảy ra) cho quái vật của chúng ta trong trò chơi D & D của chúng ta. Không có những từ này (hằng số tượng trưng), chúng ta chỉ còn lại các số từ -10 .. 10
. Chỉ phạm vi không có từ ngữ khiến chúng ta rơi vào tình trạng có thể gây nhầm lẫn lớn và có khả năng xảy ra lỗi trong trò chơi của chúng ta nếu các phần khác nhau của trò chơi có sự phụ thuộc vào phạm vi số đó có nghĩa gì đối với các hoạt động khác nhau như attack_elves
hoặc seek_magic_healing_potion
.
Do đó, khi tìm kiếm và xem xét thay thế "số ma thuật", chúng tôi muốn hỏi những câu hỏi rất có mục đích về các con số trong bối cảnh phần mềm của chúng tôi và thậm chí cả cách các con số tương tác với nhau.
Hãy xem lại những câu hỏi chúng ta nên hỏi:
Bạn có thể có một số ma thuật nếu ...
Kiểm tra các giá trị cơ bản không đổi của bảng kê khai độc lập trong văn bản mã của bạn. Hỏi từng câu hỏi một cách chậm rãi và chu đáo về từng trường hợp của một giá trị như vậy. Hãy xem xét sức mạnh của câu trả lời của bạn. Nhiều lần, câu trả lời không phải là đen và trắng, nhưng có sắc thái hiểu sai về mục đích và mục đích, tốc độ học tập và tốc độ hiểu. Cũng cần phải xem cách nó kết nối với máy phần mềm xung quanh nó.
Cuối cùng, câu trả lời cho sự thay thế là trả lời thước đo (trong suy nghĩ của bạn) về điểm mạnh hay điểm yếu của người đọc để tạo kết nối (ví dụ: "lấy nó"). Họ càng hiểu nhanh ý nghĩa và mục đích, bạn càng có ít "phép thuật".
KẾT LUẬN: Chỉ thay thế các giá trị cơ bản bằng các hằng số tượng trưng khi phép thuật đủ lớn để gây khó phát hiện lỗi phát sinh từ sự nhầm lẫn.
Số ma thuật là một chuỗi các ký tự khi bắt đầu định dạng tệp hoặc trao đổi giao thức. Con số này phục vụ như một kiểm tra vệ sinh.
Ví dụ: Mở bất kỳ tệp GIF nào, bạn sẽ thấy ngay khi bắt đầu: GIF89. "GIF89" là con số kỳ diệu.
Các chương trình khác có thể đọc một vài ký tự đầu tiên của tệp và xác định đúng GIF.
Điều nguy hiểm là dữ liệu nhị phân ngẫu nhiên có thể chứa các ký tự tương tự. Nhưng nó rất khó xảy ra.
Đối với trao đổi giao thức, bạn có thể sử dụng nó để nhanh chóng xác định rằng 'thông điệp' hiện tại đang được truyền cho bạn bị hỏng hoặc không hợp lệ.
Số ma thuật vẫn hữu ích.
Trong lập trình, "số ma thuật" là một giá trị nên được đặt tên tượng trưng, nhưng thay vào đó lại được đưa vào mã dưới dạng chữ, thường ở nhiều nơi.
Thật tệ vì cùng một lý do SPOT (Điểm duy nhất) là tốt: Nếu bạn muốn thay đổi hằng số này sau đó, bạn sẽ phải tìm kiếm mã của mình để tìm mọi trường hợp. Nó cũng tệ bởi vì nó có thể không rõ ràng với các lập trình viên khác những gì con số này đại diện, do đó là "ma thuật".
Mọi người đôi khi thực hiện loại bỏ số ma thuật hơn nữa, bằng cách di chuyển các hằng số này vào các tệp riêng biệt để hoạt động như cấu hình. Điều này đôi khi hữu ích, nhưng cũng có thể tạo ra sự phức tạp hơn giá trị của nó.
(foo[i]+foo[i+1]+foo[i+2]+1)/3
có thể được đánh giá nhanh hơn nhiều so với vòng lặp. Nếu một người thay thế 3
mà không viết lại mã thành một vòng lặp, một người nào đó ITEMS_TO_AVERAGE
được xác định là 3
có thể hình họ có thể thay đổi nó 5
và có mã trung bình nhiều mục hơn. Ngược lại, ai đó nhìn vào biểu thức với nghĩa đen 3
sẽ nhận ra rằng 3
đại diện cho số lượng vật phẩm được tổng hợp lại với nhau.
Một số ma thuật cũng có thể là một số với ngữ nghĩa đặc biệt, mã hóa cứng. Ví dụ, tôi đã từng thấy một hệ thống trong đó ID hồ sơ> 0 được xử lý bình thường, 0 chính là "bản ghi mới", -1 là "đây là gốc" và -99 là "điều này được tạo ra trong thư mục gốc". 0 và -99 sẽ khiến WebService cung cấp ID mới.
Điều tệ ở đây là bạn đang sử dụng lại một khoảng trắng (số nguyên đã ký cho ID hồ sơ) cho các khả năng đặc biệt. Có thể bạn sẽ không bao giờ muốn tạo một bản ghi với ID 0 hoặc với ID âm, nhưng ngay cả khi không, mọi người nhìn vào mã hoặc tại cơ sở dữ liệu có thể vấp phải điều này và lúc đầu bị nhầm lẫn. Không cần phải nói những giá trị đặc biệt đó không được ghi chép lại.
Có thể cho rằng, 22, 7, -12 và 620 cũng được tính là số ma thuật. ;-)
Một vấn đề chưa được đề cập khi sử dụng số ma thuật ...
Nếu bạn có rất nhiều trong số chúng, tỷ lệ cược khá tốt là bạn có hai mục đích khác nhau mà bạn đang sử dụng số ma thuật, trong đó các giá trị xảy ra là như nhau.
Và sau đó, chắc chắn, bạn cần thay đổi giá trị ... chỉ với một mục đích.
Tôi cho rằng đây là một câu trả lời cho câu trả lời của tôi cho câu hỏi trước đó của bạn. Trong lập trình, một số ma thuật là một hằng số được nhúng xuất hiện mà không cần giải thích. Nếu nó xuất hiện ở hai vị trí riêng biệt, nó có thể dẫn đến trường hợp một trường hợp bị thay đổi và không phải là một trường hợp khác. Vì cả hai lý do này, điều quan trọng là cô lập và xác định các hằng số bên ngoài những nơi chúng được sử dụng.
Tôi đã luôn sử dụng thuật ngữ "số ma thuật" khác nhau, như một giá trị tối nghĩa được lưu trữ trong cấu trúc dữ liệu có thể được xác minh là kiểm tra tính hợp lệ nhanh chóng. Ví dụ: các tệp gzip chứa 0x1f8b08 là ba byte đầu tiên của chúng, các tệp lớp Java bắt đầu bằng 0xcafebabe, v.v.
Bạn thường thấy các số ma thuật được nhúng trong các định dạng tệp, bởi vì các tệp có thể được gửi xung quanh khá bừa bãi và mất bất kỳ siêu dữ liệu nào về cách chúng được tạo. Tuy nhiên, số ma thuật đôi khi cũng được sử dụng cho các cấu trúc dữ liệu trong bộ nhớ, như các cuộc gọi ioctl ().
Việc kiểm tra nhanh số ma thuật trước khi xử lý tệp hoặc cấu trúc dữ liệu cho phép người ta báo hiệu sớm các lỗi, thay vì xử lý tất cả các cách thông qua quá trình xử lý có khả năng kéo dài để thông báo rằng đầu vào đã hoàn thành balderdash.
Điều đáng chú ý là đôi khi bạn không muốn các số "mã hóa cứng" không thể định cấu hình trong mã của mình. Có một số cái nổi tiếng bao gồm 0x5F3759DF được sử dụng trong thuật toán căn bậc hai được tối ưu hóa.
Trong những trường hợp hiếm hoi mà tôi thấy cần phải sử dụng Số ma thuật như vậy, tôi đặt chúng dưới dạng const trong mã của mình và ghi lại lý do tại sao chúng được sử dụng, cách chúng hoạt động và chúng đến từ đâu.
Điều gì về việc khởi tạo một biến ở đầu lớp với giá trị mặc định? Ví dụ:
public class SomeClass {
private int maxRows = 15000;
...
// Inside another method
for (int i = 0; i < maxRows; i++) {
// Do something
}
public void setMaxRows(int maxRows) {
this.maxRows = maxRows;
}
public int getMaxRows() {
return this.maxRows;
}
Trong trường hợp này, 15000 là một con số ma thuật (theo CheckStyles). Đối với tôi, thiết lập một giá trị mặc định là được. Tôi không muốn phải làm:
private static final int DEFAULT_MAX_ROWS = 15000;
private int maxRows = DEFAULT_MAX_ROWS;
Điều đó làm cho nó khó đọc hơn? Tôi chưa bao giờ xem xét điều này cho đến khi tôi cài đặt CheckStyles.
static final
hằng số là quá mức khi bạn sử dụng chúng trong một phương thức. Một final
biến được khai báo ở đầu phương thức là IMHO dễ đọc hơn.
@ eed3si9n: Tôi thậm chí còn đề xuất rằng '1' là một con số kỳ diệu. :-)
Một nguyên tắc liên quan đến số ma thuật là mọi thực tế mã của bạn phải được khai báo chính xác một lần. Nếu bạn sử dụng số ma thuật trong mã của mình (chẳng hạn như ví dụ về độ dài mật khẩu mà @marcio đưa ra, bạn có thể dễ dàng kết thúc việc sao chép thực tế đó và khi bạn hiểu về thực tế đó thay đổi, bạn đã gặp vấn đề về bảo trì.
factorial n = if n == BASE_CASE then BASE_VALUE else n * factorial (n - RECURSION_INPUT_CHANGE); RECURSION_INPUT_CHANGE = 1; BASE_CASE = 0; BASE_VALUE = 1
Còn các biến trả về thì sao?
Tôi đặc biệt thấy nó thách thức khi thực hiện các thủ tục được lưu trữ .
Hãy tưởng tượng thủ tục được lưu trữ tiếp theo (tôi biết cú pháp sai, chỉ để hiển thị một ví dụ):
int procGetIdCompanyByName(string companyName);
Nó trả về Id của công ty nếu nó tồn tại trong một bảng cụ thể. Nếu không, nó trả về -1. Bằng cách nào đó nó là một con số kỳ diệu. Một số khuyến nghị tôi đã đọc cho đến nay nói rằng tôi thực sự sẽ phải thiết kế một cái gì đó như thế:
int procGetIdCompanyByName(string companyName, bool existsCompany);
Nhân tiện, nó sẽ trở lại cái gì nếu công ty không tồn tại? Ok: nó sẽ đặt tồn tại Công ty là sai , nhưng cũng sẽ trả về -1.
Tùy chọn Antoher là tạo hai chức năng riêng biệt:
bool procCompanyExists(string companyName);
int procGetIdCompanyByName(string companyName);
Vì vậy, một điều kiện tiên quyết cho thủ tục lưu trữ thứ hai là công ty đó tồn tại.
Nhưng tôi sợ đồng thời, bởi vì trong hệ thống này, một công ty có thể được tạo bởi một người dùng khác.
Điểm mấu chốt là: bạn nghĩ gì về việc sử dụng loại "số ma thuật" tương đối nổi tiếng và an toàn để nói rằng một cái gì đó không thành công hoặc một cái gì đó không tồn tại?
Một lợi thế khác của việc trích xuất một số ma thuật là một hằng số cho khả năng ghi lại thông tin kinh doanh rõ ràng.
public class Foo {
/**
* Max age in year to get child rate for airline tickets
*
* The value of the constant is {@value}
*/
public static final int MAX_AGE_FOR_CHILD_RATE = 2;
public void computeRate() {
if (person.getAge() < MAX_AGE_FOR_CHILD_RATE) {
applyChildRate();
}
}
}
const myNum = 22; const number = myNum / 11;
ngay bây giờ 11 của tôi có thể là người hoặc chai bia hoặc một cái gì đó thay vì tôi sẽ thay đổi 11 thành hằng số chẳng hạn như cư dân.