Những thứ ngay lập tức rung chuông báo động khi nhìn vào mã? [đóng cửa]


98

Tôi đã tham dự một sự kiện thủ công phần mềm vài tuần trước và một trong những ý kiến ​​được đưa ra là "Tôi chắc chắn tất cả chúng ta đều nhận ra mã xấu khi nhìn thấy nó" và mọi người gật đầu lúng túng mà không cần thảo luận thêm.

Điều này luôn làm tôi lo lắng vì có sự thật rằng mọi người đều nghĩ rằng họ là một tài xế trên trung bình. Mặc dù tôi nghĩ rằng tôi có thể nhận ra mã xấu Tôi muốn tìm hiểu thêm về những gì người khác cho là mã có mùi vì nó hiếm khi được thảo luận chi tiết trên blog của mọi người và chỉ trong một số ít sách. Đặc biệt tôi nghĩ sẽ rất thú vị khi nghe về bất cứ thứ gì có mùi mã trong một ngôn ngữ chứ không phải ngôn ngữ khác.

Tôi sẽ bắt đầu với một điều dễ dàng:

Mã trong kiểm soát nguồn có tỷ lệ cao của mã nhận xét - tại sao nó lại ở đó? nó có nghĩa là bị xóa? nó là một nửa hoàn thành công việc? có lẽ nó không nên được bình luận và chỉ được thực hiện khi ai đó đang thử nghiệm cái gì đó? Cá nhân tôi thấy loại điều này thực sự gây phiền nhiễu ngay cả khi nó chỉ là dòng lẻ ở đây và đó, nhưng khi bạn thấy các khối lớn xen kẽ với phần còn lại của mã thì hoàn toàn không thể chấp nhận được. Nó cũng thường là một dấu hiệu cho thấy phần còn lại của mã có khả năng cũng có chất lượng đáng ngờ.


61
Thỉnh thoảng tôi bắt gặp những người bình luận mã, đăng ký và nói "Tôi có thể cần lại nó trong tương lai - nếu tôi xóa nó bây giờ tôi sẽ mất nó". Tôi phải chống lại "Er, ... nhưng đó là kiểm soát nguồn là gì".
Talonx

6
Đôi khi, đặc biệt là khi tối ưu hóa, thật tiện lợi khi để lại mã cũ làm nhận xét, để bạn biết mã tối ưu hóa tối nghĩa nào đang thay thế. Hãy nghĩ đến việc để lại một hoán đổi 3 dòng với temp tại chỗ khi thay thế nó bằng một hoán đổi một dòng bit. (Mặc dù, tôi thấy không cần sử dụng hoán đổi một dòng - EVER, trừ khi quy mô chương trình có tầm quan trọng quan trọng.)
Chris Cudmore

4
Tôi đang duy trì / dọn dẹp mã được viết bởi một trong những kỹ sư của chúng tôi, người đã mã hóa một số điều hữu ích nhưng thừa nhận anh ta không phải là lập trình viên. Khi tôi hợp nhất các công cụ, tôi sẽ nhận xét mã cũ của anh ấy và sau đó chúng tôi sẽ tiến hành thay đổi và tôi chỉ cho anh ấy cách tôi thay thế anh ấy bằng một cái gì đó nhỏ hơn / hiệu quả hơn / dễ hiểu hơn. Sau đó tôi loại bỏ các khối đó sau đó tôi kiểm tra nó. Có mã cũ có lợi ích vì anh ấy thấy cách mọi thứ có thể được thực hiện đơn giản hơn và tôi có thể nhớ tại sao tôi thay đổi mọi thứ khi chúng tôi nói chuyện.
Tin Man

8
Tôi để lại những thứ "có thể được sử dụng" trong 1 lần cam kết, sau đó nếu mọi thứ không bị hỏng hoặc không tìm thấy nhu cầu, nó sẽ bị xóa trong lần xác nhận tiếp theo.
Paul Nathan

24
Hừm. printf("%c", 7)thường là chuông báo thức cho tôi. ;)

Câu trả lời:


128
/* Fuck this error */

Thường được tìm thấy bên trong một try..catchkhối vô nghĩa , nó có xu hướng thu hút sự chú ý của tôi. Cũng gần như là /* Not sure what this does, but removing it breaks the build */.

Một vài điều nữa:

  • Nhiều ifcâu lệnh phức tạp lồng nhau
  • Các khối thử bắt được sử dụng để xác định luồng logic một cách thường xuyên
  • Chức năng với tên chung process, data, change, rework,modify
  • Sáu hoặc bảy kiểu giằng khác nhau trong 100 dòng

Một cái tôi vừa tìm thấy:

/* Stupid database */
$conn = null;
while(!$conn) {
    $conn = mysql_connect("localhost", "root", "[pass removed]");
}
/* Finally! */
echo("Connected successfully.");

Đúng, bởi vì phải vũ phu buộc các kết nối MySQL của bạn là cách làm đúng đắn. Hóa ra cơ sở dữ liệu đã có vấn đề với số lượng kết nối để họ liên tục hết thời gian. Thay vì gỡ lỗi này, họ chỉ cần cố gắng lặp đi lặp lại cho đến khi nó hoạt động.


19
Giá như tôi có thể nâng cấp 6 lần này! Tất cả các ví dụ tốt. Tôi cũng không thích những bình luận kiêu ngạo / hài hước (đặc biệt nếu chúng bao gồm chửi thề) - có thể hơi buồn cười khi lần đầu tiên bạn đọc chúng nhưng lại rất già (và do đó gây mất tập trung) rất nhanh.
FinnNk

5
Tôi thích ví dụ của bạn, mặc dù tôi sẽ nói rằng trong một bối cảnh nhất định, nhiều câu lồng nhau nếu các câu lệnh là không thể tránh khỏi. Với rất nhiều logic nghiệp vụ, mã có thể hơi khó hiểu, nhưng nếu bản thân doanh nghiệp khó hiểu, để đơn giản hóa mã sẽ là mô hình hóa quá trình ít chính xác hơn. Như Einstein đã nói: "Mọi thứ nên đơn giản nhất có thể và không đơn giản hơn một chút."
Morgan Herlocker

2
@Prof Plum - Bạn có thể đưa ra ví dụ nào? Thông thường, phương án thay thế cho nhiều lồng nhau nếu là chia nó thành (nhiều) phương thức. Các nhà phát triển trẻ có xu hướng tránh điều này như thể nó ít mong muốn hơn so với nếu; nhưng thông thường khi nhấn họ nói "nếu làm điều đó trong ít dòng hơn". Phải có ai đó tự tin vào OOP để bước vào và nhắc nhở họ rằng ít dòng hơn! = Mã tốt hơn.
STW

2
@STW Đó là một điểm tốt, tuy nhiên, tôi sẽ nói rằng nó phụ thuộc vào độ sâu của tổ. Tôi chắc chắn sẽ đồng ý rằng bất cứ điều gì nhiều hơn ba tổ sâu thường cần tái cấu trúc, bởi vì nó sẽ có được lông đẹp. Tuy nhiên, báo giá bảo hiểm là một ví dụ điển hình trong đó đa lồng thực sự có thể mô hình hóa thế giới thực khá tốt. Ngoại trừ một số mức giá / phí bảo hiểm nhất định, hướng dẫn sẽ đọc theo nghĩa đen như "nếu đây là một tài sản và nếu nó có tỷ lệ tối thiểu dưới 5,6 và nếu nó ở NC và nếu nó có thuyền trong khuôn viên, thì hãy làm như vậy và như là." với nhiều lựa chọn khác.
Morgan Herlocker

4
@josh, nếu "họ" là đồng nghiệp thì tôi sẽ suy nghĩ về lý do tại sao bạn không nói "chúng tôi" ...

104

Cờ đỏ lớn đối với tôi là các khối mã trùng lặp, bởi vì nó cho thấy rằng người đó không hiểu các nguyên tắc lập trình cơ bản hoặc quá sợ hãi để thực hiện các thay đổi phù hợp cho cơ sở mã hiện có.

Tôi cũng từng coi việc thiếu bình luận là một lá cờ đỏ, nhưng gần đây đã làm việc với rất nhiều mã rất tốt mà không có bình luận nào tôi đã giảm bớt về điều đó.


1
+1: Tôi thấy một số mã từ đồng nghiệp tự xưng là chuyên gia linux, người đã viết một ứng dụng lặp đơn giản như một hàm dài, toàn bộ mọi thứ lặp đi lặp lại trong hàm main (). Rất tiếc.
KFro

4
@KFro, đó là vòng lặp không kiểm soát. Đó là những gì trình biên dịch làm mọi lúc :) RẤT hiệu quả!

3
@Thorbjorn: Đôi khi, bạn phải giúp trình biên dịch một chút; Rốt cuộc, bạn là người thông minh và anh ta chỉ là một máy tính câm.
yatima2975

3
Một lý do khác mà tôi đã thấy: nhà tư vấn đã được trả tiền để triển khai tính năng này càng nhanh càng tốt (tất nhiên đó là lý do tại sao các bài kiểm tra và tài liệu bị thiếu). Sao chép / dán nhanh hơn suy nghĩ về cách làm đúng.
LennyProgrammer

4
Tránh sao chép mã có thể là một nỗi ám ảnh. Trong một ngôn ngữ như C ++, không phải lúc nào cũng dễ dàng như việc tạo ra các phần khác nhau nhưng vẫn có mã mạnh mẽ và hiệu quả. Đôi khi, một chút cắt và dán là lựa chọn thiết thực hơn. Ngoài ra, nguyên tắc tối ưu hóa có thể được áp dụng - cắt và dán có thể giúp bạn có một giải pháp nhanh chóng và dễ dàng mà bạn có thể tái cấu trúc sau này nếu cần. Bạn thể tiết kiệm đau đầu bảo trì cho lần sau, nhưng bạn biết chắc chắn rằng bạn đang tránh sự chậm trễ ngay bây giờ.
Steve314

74

Mã cố gắng thể hiện mức độ thông minh của lập trình viên, mặc dù thực tế là nó không có giá trị thực:

x ^= y ^= x ^= y;

12
Ồ, thật là dễ đọc hơn nhiềuswap(x, y);
JBRWilkinson

8
nếu x và y là con trỏ và việc chuyển nhượng này diễn ra trong một khoảng thời gian hợp lý, thì nó sẽ phá vỡ những người thu gom rác bảo thủ như Boehm GC.
SingleNegationElimination

12
Nhân tiện, mã này đã không được xác định trong C và C ++ vì nhiều thay đổi mà không có điểm trình tự can thiệp.
dòng chảy

9
Nhìn vào đó, điều duy nhất nảy ra trong đầu là những biểu tượng cảm xúc:^_^
Darien

62
  • 20.000 (cường điệu) chức năng dòng. Bất kỳ chức năng nào có nhiều hơn một vài màn hình đều cần bao thanh toán lại.

  • Dọc theo cùng một dòng, các tập tin lớp dường như đi mãi mãi. Có lẽ có một vài khái niệm có thể được trừu tượng hóa thành các lớp sẽ làm rõ mục đích và chức năng của lớp gốc và có lẽ nó đang được sử dụng, trừ khi chúng là tất cả các phương thức bên trong.

  • các biến không mô tả, không tầm thường hoặc quá nhiều biến không mô tả tầm thường. Những điều này làm cho suy luận những gì đang thực sự xảy ra một câu đố.


9
Tôi có xu hướng giới hạn các chức năng ở 1 màn hình bất cứ khi nào có thể.
Matt DiTrolio

20
1 màn hình thậm chí là một căng. Tôi bắt đầu cảm thấy bẩn sau 10 dòng.
Bryan Rowe

54
Được rồi, tôi sẽ nói lên những gì có thể là một ý kiến ​​không phổ biến. Tôi nói rằng mùi mã của nó để viết các hàm là các quá trình nguyên tử, từ trên xuống dưới được chia thành các chức năng riêng biệt bởi vì nhà phát triển đang bám vào một số "chức năng nên ngắn" văn hóa hàng hóa. Các chức năng nên được chia theo các dòng FUNCTIONAL, không chỉ bởi vì chúng phải là một số "đúng kích cỡ" huyền thoại. Đó là lý do tại sao chúng được gọi là CHỨC NĂNG.
Dan Ray

7
@Dan, Chức năng không nên ngắn vì mục đích ngắn, nhưng chỉ có rất nhiều thông tin bạn có thể giữ trong đầu một lúc. Có lẽ tôi đã có một bộ não nhỏ vì đối với tôi, giới hạn đó là một vài màn hình :). Việc chia các hàm thành nhiều chức năng khi chúng bắt đầu kiểm tra ranh giới đó là cần thiết để tránh nhầm lẫn. Một mặt nó cung cấp sự đóng gói để bạn có thể suy nghĩ ở cấp độ cao hơn và mặt khác nó che giấu những gì đang xảy ra nên làm cho nó khó khăn hơn để tìm ra cách thức hoạt động của chức năng. Tôi nghĩ rằng việc phá vỡ các chức năng nên được thực hiện để hỗ trợ khả năng đọc, không phù hợp với 'độ dài hoàn hảo'.
Dominique McDonnell

6
@Dominic, @ Péter, tôi nghĩ ba chúng tôi thực sự đang nói điều tương tự. Khi có một lý do chính đáng để đưa mã vào một hàm nhỏ hơn, tôi sẽ cho nó. Những gì tôi đang từ chối là sự ngắn gọn vì lợi ích của sự ngắn gọn trong thiết kế chức năng. Bạn biết đấy, một ngăn xếp cuộc gọi dài hơn ba lần so với yêu cầu, nhưng ít nhất các chức năng đó là ngắn. Tôi thà gỡ lỗi một hàm cao làm tốt một việc và rõ ràng hơn là đuổi theo đường dẫn thực thi thông qua hàng tá hàm được xâu chuỗi mà mỗi hàm chỉ được gọi từ hàm trước.
Dan Ray

61
{ Let it Leak, people have good enough computers to cope these days }

Điều tồi tệ hơn là từ một thư viện thương mại!


32
Đây không phải là chuông báo thức. Nó thực tế đá bạn giữa hai chân.
Steven Evers

15
Con gái là một người nghiện meth. Ai quan tâm, ít nhất cô sẽ không trở nên béo phì. :: thở dài ::
Evan Plaice

17
Khi tôi thấy mình gặp khó khăn, mẹ buntu đến với tôi. Nói những lời khôn ngoan, hãy để nó rò rỉ. HÃY CHO NÓ .. HÃY ĐỂ NÓ. HÃY NÓI LEAK oh HÃY ĐỂ NÓ. Nếu nó chỉ bị rò rỉ một lần, thì đó không phải là một sự cố. (nếu bạn đọc đến đây, +1). Tôi thực sự cần phải xem xét decaf.
Tim Post

13
"Khi thời hạn đến gần, LEAKS là tất cả những gì tôi có thể thấy, ở đâu đó có ai đó thì thầm, 'Viết bằng C ...... eeeeee !!!'"
chiurox

9
Ngày xửa ngày xưa có hai HĐH. Một cái bị rò rỉ và sụp đổ nếu nó chạy trong hơn 49 ngày, cái còn lại là hoàn hảo và sẽ chạy mãi mãi. Một chiếc đã được đưa ra vào năm 1995 cho một fanfair khổng lồ và được sử dụng bởi hàng triệu người - cái còn lại không bao giờ được vận chuyển vì họ vẫn đang kiểm tra xem nó không có lỗi. Có một sự khác biệt giữa triết học và kỹ thuật.
Martin Beckett

53

Nhận xét dài dòng đến mức nếu có trình biên dịch tiếng Anh, nó sẽ biên dịch và chạy hoàn hảo, nhưng không mô tả bất cứ điều gì mà mã không có.

//Copy the value of y to temp.
temp = y;
//Copy the value of x to y.
y = x;
//Copy the value of temp to x.
x = temp;

Ngoài ra, các nhận xét về mã có thể đã được loại bỏ với mã đã tuân thủ một số nguyên tắc cơ bản:

//Set the age of the user to 13.
a = 13;

15
Có, nó được gọi là COBOL :-)
Gaius

26
Trường hợp thứ hai không phải là tồi tệ nhất. Điều tồi tệ nhất là: / * Đặt tuổi của người dùng là 13 * / a = 18;
PhiLho

4
@PhiLho - không có, thậm chí tệ hơn là khi /từ */là mất tích, vì vậy tất cả các mã để kết thúc tiếp theo */được bỏ qua. May mắn thay, làm nổi bật cú pháp làm cho loại điều đó hiếm ngày nay.
Steve314

3
Tệ hơn nữa, ađối với user_age? Có thật không?
glasnt

2
Tôi đã sử dụng để duy trì một tài liệu tiêu chuẩn mã tại một nhà tuyển dụng trước đó, một phần trong đó là nhận xét thích hợp. Ví dụ yêu thích của tôi là từ MSDN:i = i + 1; //increment i
Michael Itzoe

42

Mã tạo cảnh báo khi biên dịch.


1
Có một ứng cử viên để thêm tùy chọn trình biên dịch 'Tất cả các cảnh báo là lỗi' vào tệp thực hiện / dự án.
JBRWilkinson

3
Tôi đoán điều đó có thể hữu ích nếu bạn tham gia vào một dự án có nhiều người mà bạn đơn giản không tin tưởng - mặc dù nếu tôi tham gia một dự án mà tùy chọn đó được đặt, thì chính điều đó sẽ khiến tôi lo lắng về khả năng của người khác lập trình viên là.
Rei Miyasaka

1
Tôi không đồng ý với điều này. Một số cảnh báo của trình biên dịch (như so sánh giữa ký và không dấu, khi bạn biết cả hai giá trị là không dấu, ngay cả khi các loại khác nhau) thích hợp hơn với việc xả mã với các phôi không cần thiết. Nếu tôi giảm mã bằng cách sử dụng số nguyên có chữ ký di động mà hàm chỉ sửa đổi nếu số nguyên có giá trị không dấu, tôi sẽ làm điều đó.
Tim Post

13
Tôi thà làm lộn xộn mã của mình với một thứ gần như không cần thiết (unsigned int)hơn là làm lộn xộn danh sách cảnh báo / lỗi của tôi với các cảnh báo lành tính. Tôi ghét danh sách cảnh báo sẽ trở thành một điểm mù. Nó cũng nhiều hơn nữa của một Pita giải thích với người khác khiến bạn đang bỏ qua một cảnh báo hơn là để giải thích lý do tại sao bạn đang làm một dàn diễn viên của tự nhiên intsđể unsigned ints.
Rei Miyasaka

Đôi khi, bạn phải làm việc với một API phát sinh lỗi bất cứ điều gì bạn làm. Các ví dụ cổ điển là nơi API được định nghĩa theo những thứ bị phá vỡ bởi thiết kế (một số hằng số ioctl () cũ giống như vậy và đôi khi các nhà phát triển hệ điều hành khăng khăng sử dụng loại sai trong tiêu đề của họ) hoặc ở đó họ đã từ chối thứ gì đó mà không có để lại một sự thay thế tốt (cảm ơn bạn, Apple).
Donal Fellows

36

Các hàm có số trong tên thay vì có tên mô tả , như:

void doSomething()
{
}

void doSomething2()
{
}

Xin vui lòng, làm cho tên hàm có ý nghĩa gì đó! Nếu doS Something và doS Something2 làm những việc tương tự, hãy sử dụng tên hàm để phân biệt sự khác biệt. Nếu doS Something2 là một sự đột phá về chức năng từ doS Something, hãy đặt tên cho chức năng của nó.


Tương tự như vậy @Parm cho SQL
Dave

2
+1 - Enter mshtml- nó phá vỡ mắt tôi :(
Kyle Rozendo

3
Ngoại lệ cho điều này sẽ là mã GUI. Chẳng hạn, nếu một mẫu thư có hai địa chỉ; address1 và address2 hợp lý hơn địa chỉ và AlternateAddress. Nhãn giả chỉ tĩnh cũng là một ngoại lệ hợp lý.
Evan Plaice

@Evan - đủ công bằng, mặc dù tôi đã tạo ra sự khác biệt trong chức năng.
Wonko the Sane

1
+1 - Tôi thậm chí đã thấy điều này được sử dụng như phương pháp kiểm soát phiên bản giả.
EZ Hart

36

Số ma thuật hoặc Chuỗi ma thuật.

   if (accountBalance>200) { sendInvoice(...); }

   salesPrice *= 0.9;   //apply discount    

   if (invoiceStatus=="Overdue") { reportToCreditAgency(); }

4
Không thực sự tệ với hai phần hai, ít nhất là giảm giá được giải thích và "Quá hạn" là trực quan. Mặt 200khác, ...
Tarka

9
@Slokun - Trực quan không phải là vấn đề nhiều như khả năng duy trì và mong manh. Ví dụ, hãy xem xét những gì xảy ra khi số tiền chiết khấu thay đổi và 0,9 được mã hóa cứng ở sáu nơi khác nhau. Ngoài ra, sử dụng chuỗi thay vì hằng / enums là yêu cầu sự cố trong các ngôn ngữ có chuỗi phân biệt chữ hoa chữ thường.
JohnFx

+1 Tôi vừa dành quá nhiều thời gian để gỡ lỗi một vấn đề hóa ra là do một dòng 'timeout = 15;' chôn cất trong một chương trình.
aubreyrhodes

Tôi nghĩ rằng cái cuối cùng đôi khi vẫn ổn, tùy thuộc vào nơi dữ liệu cho hóa đơnStatus đến từ đâu. Nếu nó xuất phát từ một api công khai trả về JSON vừa được giải mã, thì việc kiểm tra hằng số Chuỗi được mã hóa cứng có lẽ là ổn. Đồng ý rằng mặc dù "200" và "0,9" chỉ là hằng số ma thuật và không nên mã hóa theo cách này. Ngay cả khi chúng chỉ được sử dụng ở một nơi này, việc bảo trì sẽ dễ dàng hơn nếu bạn xác định chúng trong một phần cấu hình riêng biệt thay vì xen kẽ chúng trong mã logic. Và nếu chúng được sử dụng ở nhiều nơi, việc bảo trì trở nên dễ dàng hơn nhiều .
Ben Lee

36
  • Có thể không phải là tồi tệ nhất nhưng rõ ràng cho thấy mức độ người thực hiện:

    if(something == true) 
  • Nếu một ngôn ngữ có cấu trúc vòng lặp for hoặc iterator, thì việc sử dụng vòng lặp while cũng thể hiện mức độ hiểu biết về ngôn ngữ của người triển khai:

    count = 0; 
    while(count < items.size()){
       do stuff
       count ++; 
    }
    
    for(i = 0; i < items.size(); i++){
      do stuff 
    }
    //Sure this is not terrible but use the language the way it was meant to be used.
  • Chính tả / ngữ pháp kém trong tài liệu / nhận xét ăn vào của tôi gần như bằng chính mã. Lý do cho điều này là vì mã có nghĩa là cho con người đọc và máy móc để chạy. Đây là lý do tại sao chúng tôi sử dụng các ngôn ngữ cấp cao, nếu tài liệu của bạn khó vượt qua nó khiến tôi hình thành ý kiến ​​tiêu cực về codebase mà không cần nhìn vào nó.


29

Cái tôi nhận thấy ngay lập tức là tần số của các khối mã được lồng sâu (nếu là, trong khi, v.v.). Nếu mã thường xuyên đi sâu hơn hai hoặc ba cấp, đó là dấu hiệu của vấn đề thiết kế / logic. Và nếu nó đi sâu như 8 tổ, tốt hơn hết là một lý do chính đáng để nó không bị phá vỡ.


6
Tôi biết một số người đã học được rằng mọi phương pháp chỉ nên có một returncâu lệnh, nhưng khi nó gây ra hơn 6 cấp độ if / then lồng nhau, tôi nghĩ rằng nó làm hại nhiều hơn là tốt.
Darien

28

Khi chấm điểm chương trình của học sinh, đôi khi tôi có thể nói trong một khoảnh khắc kiểu "nháy mắt". Đây là những manh mối tức thì:

  • Định dạng kém hoặc không nhất quán
  • Nhiều hơn hai dòng trống liên tiếp
  • Quy ước đặt tên không chuẩn hoặc không nhất quán
  • Mã lặp lại, lặp lại nguyên văn càng nhiều, cảnh báo càng mạnh
  • Điều gì phải là một đoạn mã đơn giản là quá phức tạp (ví dụ: kiểm tra các đối số được truyền đến chính theo cách phức tạp)

Hiếm khi những ấn tượng đầu tiên của tôi không chính xác, và những tiếng chuông cảnh báo này đúng khoảng 95% . Đối với một ngoại lệ, một sinh viên mới sử dụng ngôn ngữ đã sử dụng một phong cách từ một ngôn ngữ lập trình khác. Đi sâu vào và đọc lại phong cách của họ trong thành ngữ của ngôn ngữ khác đã gỡ bỏ tiếng chuông báo thức cho tôi, và sau đó sinh viên đã nhận được tín dụng đầy đủ. Nhưng ngoại lệ như vậy là rất hiếm.

Khi xem xét mã nâng cao hơn, đây là những cảnh báo khác của tôi:

  • Sự hiện diện của nhiều lớp Java chỉ là "cấu trúc" để chứa dữ liệu. Không thành vấn đề nếu các trường là công khai hoặc riêng tư và sử dụng getters / setters, nó vẫn không phải là một phần của một thiết kế chu đáo.
  • Các lớp có tên kém, chẳng hạn như chỉ là một không gian tên và không có sự gắn kết thực sự trong mã
  • Tham chiếu đến các mẫu thiết kế thậm chí không được sử dụng trong mã
  • Xử lý ngoại lệ trống mà không giải thích
  • Khi tôi kéo mã lên trong Eclipse, hàng trăm "cảnh báo" màu vàng sẽ mã dòng, chủ yếu là do nhập hoặc các biến không được sử dụng

Về phong cách, tôi thường không muốn thấy:

  • Javadoc bình luận chỉ lặp lại mã

Đây chỉmanh mối cho mã xấu. Đôi khi những gì có vẻ như mã xấu thực sự không phải là vì bạn không biết ý định của lập trình viên. Ví dụ, có thể có một lý do chính đáng rằng một cái gì đó có vẻ quá phức tạp - có thể có một sự xem xét khác khi chơi.


Tôi không thấy cách sử dụng kiểu từ ngôn ngữ này sang ngôn ngữ khác là một lỗi nghiêm trọng (2, 4, 8 khoảng cách cho mỗi lần thụt lề ...). Nếu học sinh có ít phong cách khác để làm theo, không có gì sai khi tự lập. Là học sinh lớp bạn thấy một tỷ chương trình, vì vậy bạn ở phía đối diện của quang phổ đó, nhưng đó không phải là lý do để loại bỏ hoàn toàn một phong cách khác (nhưng nhất quán).
Nick T

18
Tôi thấy không có gì sai với các lớp chỉ đơn giản là tổng hợp dữ liệu - tức là các cấu trúc. Đó là những gì Đối tượng truyền dữ liệu (DTO) là và có thể làm cho mã dễ đọc hơn nhiều so với nói, ví dụ: truyền 20 tham số cho một phương thức.
Nemi

1
Nhận xét của bạn về structs là tại chỗ. Sẽ ổn khi dữ liệu ở dạng raxi và sẽ không bị sửa đổi theo bất kỳ cách nào. Nhưng 95% thời gian bạn nên có một số chức năng trong lớp để định dạng / vận hành trên dữ liệu. Tôi chỉ nhớ một số mã của tôi về cơ bản sử dụng mẫu đó và phải được cải thiện.
DisgruntledGoat

2
+1 cho kiểu không nhất quán và ngắt dòng thêm (Tôi đã thấy các vết lõm ngẫu nhiên, không thụt lề, quy ước đặt tên ngẫu nhiên và thay đổi và hơn thế nữa - và điều này trong mã sản xuất!). Nếu bạn thậm chí không thể bận tâm để có được điều đó, thì bạn không thể làm gì khác?
Dean Harding

1
Tôi tìm dòng khai báo của một phương thức được thụt quá xa so với cơ thể. Đây là một dấu hiệu họ sao chép và dán từ tệp của người khác.
Barry Brown

25

Yêu thích cá nhân / peeve vật nuôi: IDE tạo tên được nhận. Nếu TextBox1 là một biến số quan trọng và quan trọng trong hệ thống của bạn, thì bạn đã có một điều nữa sắp tới là xem xét mã.


25

Các biến hoàn toàn không được sử dụng , đặc biệt là khi biến có tên tương tự với tên biến được sử dụng.


21

Rất nhiều người đã đề cập:

//TODO: [something not implemented]

Trong khi tôi ước điều đó được thực hiện, ít nhất họ đã ghi chú lại. Những gì tôi nghĩ là tồi tệ hơn là:

//TODO: [something that is already implemented]

Todo là vô giá trị và khó hiểu nếu bạn không bao giờ bận tâm để loại bỏ chúng!


1
Tôi vừa trải qua bài tập về việc phải tạo một báo cáo về tất cả các TODO trong sản phẩm phát hành của chúng tôi, cộng với một kế hoạch xử lý chúng. Gần một nửa hóa ra là lỗi thời.
HỎI

1
-1 Nhận xét TODO được sử dụng trong MS Visual Studio để theo dõi các phần của dự án vẫn cần làm việc. IE, IDE giữ một danh sách theo dõi các TODO để bạn có thể dễ dàng nhấp vào chúng và được đưa trực tiếp đến dòng mà TODO tồn tại. Tôi thà nhìn thấy các TODO được đặt rõ ràng để xem những gì công việc vẫn cần phải được thực hiện. Xem dotnetperls.com/todo .
Evan Plaice

3
@Evan Plaice: Wow, ý bạn là VS IDE nhận ra khi bạn thực hiện điều gì đó và xóa //TODOnhận xét? Tuyệt vời!
JBRWilkinson

4
@Prof Plum Tại sao không chỉ tạo một chính sách trong đó người chịu trách nhiệm về TODO đặt tên của họ trong bình luận. Theo cách đó, nếu có một số thức ăn thừa
Evan Plaice

3
Kế hoạch tốt hơn // TODO , sử dụng trình theo dõi lỗi của bạn, đó là những gì nó làm!
Độc thân Tăng tốc

20

Một phương pháp đòi hỏi tôi phải cuộn xuống để đọc tất cả.


14
Hmm .. điều này phụ thuộc vào những gì đang được thực hiện. Sẽ không có lý khi điều này xảy ra khi thực hiện một số thuật toán phức tạp, vì đó là cách chúng được định nghĩa. Tất nhiên, nếu phần lớn các phương thức là như vậy, đó là một lá cờ đỏ.
Billy ONeal

9
Như một tuyên bố mền sau đó tôi không đồng ý, lãng phí thời gian liên tục tái cấu trúc để mọi thứ phù hợp với loại quy tắc tự áp đặt này thực sự làm tăng chi phí chung của dự án.
Loại ẩn danh

1
Tôi không đồng ý rằng quy tắc này có thể làm tăng chi phí chung của một dự án nhưng tôi đoán đó là chủ quan vì nó sẽ phụ thuộc vào (các) nhà phát triển. Nếu bạn tuân thủ nguyên tắc chung là "tách các mối quan tâm" trong khi phát triển thì việc bao thanh toán lại sẽ ít có nhiệm vụ hơn nếu người ta chọn thực hiện nó. Một cái gì đó để xem xét là nó sẽ tốn bao nhiêu tiền trong ba năm khi các nhà phát triển không viết mã cho dự án ban đầu đang cố gắng sửa lỗi một loạt các phương thức với hơn 300 dòng mã? Cái đó giá bao nhiêu?
BradB

18
Tôi khó chịu hơn khi cuộn sang phải hơn là cuộn xuống. Khoảng trắng là "miễn phí."
Wonko the Sane

4
Tôi thà cuộn hơn là phải bỏ qua tất cả các tệp (hoặc nhiều tệp) để tìm hiểu xem mã đang làm gì.
TMN

20

Các kết hợp trong tên phương thức:

public void addEmployeeAndUpdatePayrate(...) 


public int getSalaryOrHourlyPay(int Employee) ....

Làm rõ: Lý do chuông này báo động là vì nó chỉ ra phương pháp có khả năng vi phạm nguyên tắc trách nhiệm duy nhất .


Hmmm ... nếu tên hàm xác định chính xác chức năng làm gì thì tôi không đồng ý. Nếu phương thức thực hiện hai điều riêng biệt sẽ tốt hơn để tách, thì tôi có thể đồng ý, tùy thuộc vào ngữ cảnh.
Wonko the Sane

2
Đó là điểm. Sự kết hợp ngụ ý rằng nó rất có khả năng nó làm nhiều hơn một điều. Theo câu hỏi, nó chỉ là một cái gì đó nâng cao nhận thức của tôi rằng một cái gì đó có thể sai.
JohnFx

3
Bây giờ nếu bạn phải thêm một nhân viên và cập nhật mức lương của họ ở một số nơi thì sao? Nếu phương thức đó chứa hai lệnh gọi phương thức ( addEmployee(); updatePayrate();), thì tôi không nghĩ đó là vấn đề.
Matt Grande

13

Liên kết rõ ràng mã nguồn GPL'd thành một chương trình nguồn đóng, thương mại.

Nó không chỉ tạo ra một vấn đề pháp lý ngay lập tức, mà theo kinh nghiệm của tôi, nó thường chỉ ra sự bất cẩn hoặc không quan tâm được phản ánh ở nơi khác trong mã.


6
Trong khi quan điểm của bạn là tuyệt vời, giai điệu của bạn là không cần thiết.
JBRWilkinson

@JBRWilkinson: Cảm ơn, bạn đã đúng. Tôi xin lỗi tất cả.
Bob Murphy

Tôi nghĩ rằng tiêu đề của bạn cần viết lại. Cả liên kết tĩnh và liên kết động với mã nguồn của GPL đều vi phạm GPL ...
Gavin Coates

Điểm tốt. Tôi đã viết lại toàn bộ bài viết. Cảm ơn tất cả.
Bob Murphy

9

Ngôn ngữ bất khả tri:

  • TODO: not implemented
  • int function(...) { return -1; } (giống như "không được thực hiện")
  • Ném một ngoại lệ cho một lý do không đặc biệt.
  • Lạm dụng hoặc sử dụng không phù hợp của 0, -1hoặc nullgiá trị trả lại là ngoại lệ.
  • Khẳng định mà không có một bình luận thuyết phục nói tại sao nó không bao giờ nên thất bại.

Ngôn ngữ cụ thể (C ++):

  • Macro C ++ viết thường.
  • Biến C ++ tĩnh hoặc toàn cầu.
  • Các biến không được khởi tạo hoặc không sử dụng.
  • Bất kỳ array newđiều đó rõ ràng là không an toàn RAII.
  • Bất kỳ việc sử dụng mảng hoặc con trỏ rõ ràng là không an toàn. Điều này bao gồm printf.
  • Bất kỳ việc sử dụng phần chưa khởi tạo của một mảng.

Microsoft C ++ cụ thể:

  • Bất kỳ tên định danh nào va chạm với macro đã được xác định bởi bất kỳ tệp tiêu đề SDK nào của Microsoft.
  • Nói chung, bất kỳ việc sử dụng API Win32 là một nguồn chuông báo động lớn. Luôn mở MSDN và tra cứu các định nghĩa giá trị đối số / trả về bất cứ khi nào nghi ngờ. (Đã chỉnh sửa)

C ++ / OOP cụ thể:

  • Kế thừa thực hiện (lớp cụ thể) trong đó lớp cha có cả phương thức ảo và không ảo, không có sự phân biệt khái niệm rõ ràng rõ ràng giữa những gì nên / không nên là ảo.

18
// TODO: Nhận xét về câu trả lời này
johnc

8
Tôi đoán "ngôn ngữ bất khả tri" có nghĩa là "C / C ++ / Java" bây giờ?
Inaimathi

+1 "Ném một ngoại lệ vì một lý do không đặc biệt" không thể đồng ý nhiều hơn!
billy.bob

2
....
Rei Miyasaka

Tôi tin rằng các macro tiền xử lý C trong trường hợp thấp hơn ổn, nhưng chỉ khi chúng đánh giá các đối số của chúng chỉ một lần và chỉ dẫn đến một câu lệnh duy nhất.
Joe D

8

Phong cách thụt đầu dòng kỳ lạ.

Có một vài phong cách rất phổ biến, và mọi người sẽ đưa cuộc tranh luận đó xuống mồ. Nhưng đôi khi tôi thấy ai đó sử dụng một kiểu thụt đầu dòng thực sự hiếm, hoặc thậm chí là trồng tại nhà. Đây là một dấu hiệu cho thấy họ có thể đã không được mã hóa với bất kỳ ai khác ngoài chính họ.


2
hoặc chỉ là một dấu hiệu cho thấy họ là một tài năng cá nhân được đánh giá cao đã không bị cuốn vào mạng lưới các hoạt động mã hóa đồng nhất không liên quan đến "các thực tiễn tốt nhất".
Loại ẩn danh

1
Tôi hy vọng bạn đang mỉa mai. Nếu ai đó đang mã hóa theo cách bất thường như vậy, điều đó sẽ gióng lên hồi chuông cảnh báo. Họ có thể nghệ thuật như họ muốn, nhưng vẫn ... chuông báo thức.
Ken

2
Có một phong cách hơi không phổ biến (tôi tin rằng nó được gọi là phong cách Utrecht) mà tôi thấy khá hữu ích mặc dù cực kỳ không phổ biến bên ngoài Haskell / ML / F #. Cuộn xuống "Hình dạng mô-đun": learnyouahaskell.com/making-our-own-types-and-typec Cầu . Điều tuyệt vời ở phong cách này là bạn không phải sửa đổi dấu phân cách trên dòng trước để thêm một kiểu mới - điều mà tôi thường quên làm.
Rei Miyasaka

@ReiMiyasaka Bảy năm muộn, nhưng ... Phong cách Utrecht thực sự làm tôi khó chịu. Tôi tin rằng đó là một lỗi trong đặc tả Haskell khi không áp đặt một "quy tắc bố cục" khác cho các danh sách được sắp xếp theo chiều dọc. Bằng cách đó, trình phân tích cú pháp có thể phát hiện một mục nhập danh sách mới chỉ bằng cách kiểm tra thụt lề, đó là cách mọi người tổ chức danh sách của họ bằng mọi cách.
Ryan Reich

@RyanReich Lạ, bảy năm sau, và tôi vẫn thích nó. Tôi đồng ý, mặc dù; đối với tất cả sự lúng túng và thất bại cú pháp của nó, F # cũng cho phép các mục được phân định chỉ bằng một dòng mới và thụt lề (trong hầu hết các trường hợp), tạo ra mã gọn gàng.
Rei Miyasaka

8

Sử dụng nhiều khối văn bản thay vì enum hoặc các biến được xác định toàn cầu.

Không tốt:

if (itemType == "Student") { ... }

Tốt hơn:

private enum itemTypeEnum {
  Student,
  Teacher
}
if (itemType == itemTypeEnum.Student.ToString()) { ... }

Tốt:

private itemTypeEnum itemType;
...
if (itemType == itemTypeEnum.Student) { ... }

Hoặc tốt nhất: Sử dụng đa hình.
Lstor

8

Nhập tham số yếu hoặc trả về giá trị trên các phương thức.

public DataTable GetEmployees() {...}

public DateTime getHireDate(DataTable EmployeeInfo) {...}

public void FireEmployee(Object EmployeeObjectOrEmployeeID) {...}

2
+1: Tôi phải làm việc với một số dịch vụ web REST REST mà trả về mọi thứ dưới dạng bảng giả HTML, ngay cả khi bạn vượt qua những thứ có lỗi cú pháp rõ ràng. Không được ủy quyền? Nhập liệu đầy đủ? Máy chủ vượt quá dung lượng? 200 (cộng với một tin nhắn ở định dạng khủng khiếp trong một cột, một bảng hàng). Ôi trờiaaaaa!
Donal Fellows

7
  • Nhiều toán tử ternary nối với nhau, vì vậy thay vì giống một if...elsekhối, nó trở thành một if...else if...[...]...elsekhối
  • Tên biến dài không có dấu gạch dưới hoặc camelCasing. Ví dụ từ một số mã tôi đã kéo lên:$lesseeloginaccountservice
  • Hàng trăm dòng mã trong một tệp, có rất ít hoặc không có nhận xét và mã này rất không rõ ràng
  • ifBáo cáo quá phức tạp . Ví dụ từ mã: if (!($lessee_obj instanceof Lessee && $lessee_obj != NULL))mà tôi đã nhai xuốngif ($lessee_obj == null)

7

Mùi mã: không tuân theo các thực hành tốt nhất

Điều này luôn làm tôi lo lắng vì có sự thật rằng mọi người đều nghĩ rằng họ là một tài xế trên trung bình.

Đây là một tin tức chớp nhoáng cho bạn: 50% dân số thế giới dưới mức thông minh trung bình. Ok, vì vậy một số người sẽ có trí thông minh chính xác trung bình, nhưng chúng ta đừng kén chọn. Ngoài ra, một trong những tác động phụ của sự ngu ngốc là bạn không thể nhận ra sự ngu ngốc của chính mình! Mọi thứ sẽ không tốt nếu bạn kết hợp những tuyên bố này.

Những thứ ngay lập tức rung chuông báo động khi nhìn vào mã?

Nhiều điều tốt đã được đề cập, và nói chung có vẻ như không tuân theo các thực tiễn tốt nhất là mùi mã.

Thực hành tốt nhất thường không được phát minh ngẫu nhiên, và thường có lý do. Nhiều khi nó có thể chủ quan, nhưng theo kinh nghiệm của tôi thì hầu hết là hợp lý. Thực hiện theo các thực tiễn tốt nhất không phải là một vấn đề và nếu bạn đang tự hỏi tại sao chúng lại như vậy, hãy nghiên cứu nó thay vì phớt lờ và / hoặc phàn nàn về nó - có thể nó hợp lý, có thể không phải vậy.

Một ví dụ về cách thực hành tốt nhất có thể là sử dụng curlies với mọi khối if, ngay cả khi nó chỉ chứa một dòng:

if (something) {
    // whatever
}

Bạn có thể không nghĩ rằng nó cần thiết, nhưng gần đây tôi đã đọc rằng đó là một nguồn chính của lỗi. Luôn luôn sử dụng dấu ngoặc cũng đã được thảo luận về Stack Overflow và kiểm tra xem các câu lệnh if có dấu ngoặc cũng là một quy tắc trong PMD , một công cụ phân tích mã tĩnh cho Java.

Hãy nhớ rằng: "Bởi vì đó là cách thực hành tốt nhất" không bao giờ là câu trả lời chấp nhận được cho câu hỏi "tại sao bạn lại làm điều này?" Nếu bạn không thể nói rõ tại sao một cái gì đó là một thực tiễn tốt nhất, thì đó không phải là một thực tiễn tốt nhất, đó là một sự mê tín.


2
Điều này có thể là phạm vi, nhưng tôi nghĩ nó quan trọng với mức trung bình bạn chọn. Theo tôi hiểu, 50% dân số thế giới nằm dưới mức thông minh trung bình (theo định nghĩa); nhưng các mức trung bình khác không hoạt động theo cùng một cách. Ví dụ: lấy dân số (1, 1, 1, 5) có trung bình số học là 2.
flamingpenguin

Ừm, bạn đã trích dẫn bài đăng "what-superstitions-do-lập trình viên-có" trong đó người dùng đưa ra tuyên bố táo bạo về niềng răng xoăn không có nguồn. Tôi không thấy đó là một ví dụ tốt về thực hành tốt nhất.
Evan Plaice

@Evan: vâng, bạn nói đúng. Tôi đã thêm một chút về điều đó, hy vọng điều này sẽ giúp.
Vetle

4
Điểm nổi bật của vấn đề này là những người mù quáng tuân theo "các thực hành tốt nhất" mà không có bất kỳ suy nghĩ phê phán nào về lý do tại sao một cái gì đó là "thực hành tốt nhất". Đây là lý do tại sao tôi cực kỳ không thích thuật ngữ "thực hành tốt nhất", bởi vì đối với một số người, đó là một cái cớ để ngừng suy nghĩ và đi theo bầy đàn. "Bởi vì đó là cách thực hành tốt nhất" không bao giờ là câu trả lời chấp nhận được cho câu hỏi "tại sao bạn lại làm điều này?" Nếu bạn không thể nói rõ tại sao một cái gì đó là một thực tiễn tốt nhất, thì đó không phải là một thực tiễn tốt nhất, đó là một sự mê tín.
Dan Dyer

Nhận xét rất tốt, Dan! Tôi đã thêm hai dòng cuối cùng vào câu trả lời.
Vetle

6

Những bình luận nói rằng "điều này là do thiết kế của hệ thống con froz hoàn toàn bị bó buộc."

Điều đó diễn ra trên toàn bộ một đoạn.

Họ giải thích rằng các cấu trúc lại sau đây cần phải xảy ra.

Nhưng đã không làm điều đó.

Bây giờ, họ có thể đã được thông báo rằng họ không thể thay đổi nó bởi ông chủ của họ vào thời điểm đó, vì vấn đề thời gian hoặc năng lực, nhưng có lẽ đó là do mọi người nhỏ mọn.

Nếu một giám sát viên nghĩ rằng j.random. lập trình viên không thể thực hiện tái cấu trúc, sau đó người giám sát nên thực hiện.

Dù sao, điều này xảy ra, tôi biết mã được viết bởi một nhóm bị chia rẽ, với chính trị quyền lực có thể, và họ đã không tái cấu trúc các thiết kế hệ thống con.

Câu chuyện có thật. Nó có thể xảy ra với bạn.


6

Bất cứ ai cũng có thể nghĩ về một ví dụ trong đó mã nên tham chiếu hợp pháp một tệp theo đường dẫn tuyệt đối?


1
Lược đồ XML tính?
Nick T

1
Tập tin cấu hình hệ thống. Thông thường nên được đặt theo ./mình, nhưng ngay cả điều đó cũng cần một giá trị mặc định ở đâu đó.
eswald

4
/dev/nullvà bạn bè đều ổn. Nhưng ngay cả những thứ như /bin/bashlà nghi ngờ - nếu bạn là một hệ thống kooky nào đó thì /usr/bin/bashsao?
Tom Anderson

1
Mã máy khách dịch vụ web được tạo bởi các công cụ JAX-WS (ít nhất là JBossWS và Metro) chứa một đường dẫn tuyệt đối được gắn kết đến tệp WSDL (hai lần!). Mà có lẽ là một cái gì đó cực kỳ không phù hợp như /home/tom/dev/randomhacking/thing.wsdl. Điều điên rồ nhất là đây là hành vi mặc định.
Tom Anderson

4
about /dev/null: Tôi có thói quen, khi phát triển trên windows để giữ các ứng dụng và lib c:\dev. Bằng cách nào đó, một thư mục nullluôn được tạo tự động bên trong thư mục đó. Tôi thề tôi không biết ai làm điều đó. (Một trong những lỗi / tính năng yêu thích của tôi)
Sean Patrick Floyd

6

Nắm bắt các ngoại lệ chung:

try {

 ...

} catch {
}

hoặc là

try {

 ...

} catch (Exception ex) {
}

Khu vực lạm dụng

Thông thường, sử dụng quá nhiều vùng cho tôi biết rằng các lớp học của bạn quá lớn. Đó là một lá cờ cảnh báo báo hiệu rằng tôi nên điều tra thêm về đoạn mã đó.


Nắm bắt các ngoại lệ chung chỉ là một vấn đề nếu bạn nghĩ lại chúng mà không làm gì cả. Thực sự đối với hầu hết mọi thứ, một lớp ngoại lệ sẽ đủ. Tôi có xu hướng sử dụng runtime_error.
CashCow

+1 cho ví dụ 'bắt và vứt bỏ ngoại lệ'. Nếu bạn không làm gì với ngoại lệ, đừng bắt nó. Ít nhất, đăng nhập nó. Ít nhất, rất ít đưa ra một nhận xét giải thích lý do tại sao nên nắm bắt tất cả các ngoại lệ và tiếp tục tại thời điểm đó trong mã.
EZ Hart

5

Các quy ước đặt tên lớp thể hiện sự hiểu biết kém về sự trừu tượng mà họ đang cố gắng tạo ra. Hoặc điều đó không định nghĩa một sự trừu tượng nào cả.

Một ví dụ cực đoan xuất hiện trong lớp VB tôi đã thấy một lần có tiêu đề Datavà dài hơn 30.000 dòng ... trong tệp đầu tiên . Đó là một lớp một phần được chia thành ít nhất nửa tá các tệp khác. Hầu hết các phương thức là các hàm bao quanh các procs được lưu trữ với tên như thế nào FindXByYWithZ().

Ngay cả với những ví dụ ít kịch tính hơn, tôi chắc chắn rằng tất cả chúng ta chỉ 'đổ' logic vào một lớp học được hình thành kém, đặt cho nó một tiêu đề hoàn toàn chung chung và hối hận về sau.


5

Các chức năng thực hiện lại chức năng cơ bản của ngôn ngữ. Ví dụ: nếu bạn từng thấy phương thức "getStringL wavel ()" trong JavaScript thay vì gọi đến thuộc tính ".length" của chuỗi, bạn biết bạn đang gặp rắc rối.


5
#define ...
#define ...
#define ...
#define ...
#define ...
#define ...
#define ...
#define ...
#define ...
#define ...
#define ...
#define ...
#define ...
#define ...
#define ...

Tất nhiên không có bất kỳ loại giấy tờ và thỉnh thoảng lồng #defines


Tôi đã thấy chính xác "mẫu" này ngày hôm qua ... trong mã sản xuất ... và thậm chí tệ hơn ... trong mã sản xuất C ++: - /
Oliver Weiler
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.