Tài liệu trong OOP nên tránh chỉ định liệu một get getter có hay không thực hiện bất kỳ tính toán nào?


39

Chương trình CS của trường tôi tránh mọi đề cập đến lập trình hướng đối tượng, vì vậy tôi đã tự mình đọc một số thứ để bổ sung cho nó - cụ thể là Xây dựng phần mềm hướng đối tượng của Bertrand Meyer.

Meyer đưa ra quan điểm nhiều lần rằng các lớp nên che giấu càng nhiều thông tin về việc thực hiện chúng càng tốt, điều này có ý nghĩa. Cụ thể, ông lập luận nhiều lần rằng các thuộc tính (nghĩa là các thuộc tính tĩnh, không tính toán của các lớp) và các thường trình (các thuộc tính của các lớp tương ứng với các lệnh gọi hàm / thủ tục) không thể phân biệt được với nhau.

Ví dụ, nếu một lớp Personcó thuộc tính age, anh ta khẳng định rằng không thể biết, từ ký hiệu, cho dù Person.agetương ứng bên trong với một cái gì đó giống như return current_year - self.birth_datehoặc đơn giản return self.age, nơi self.ageđã được định nghĩa là một thuộc tính không đổi. Điều này có ý nghĩa với tôi. Tuy nhiên, ông tiếp tục yêu cầu như sau:

Tài liệu khách hàng tiêu chuẩn cho một lớp, được gọi là dạng ngắn của lớp, sẽ được phát minh để không tiết lộ liệu một tính năng nhất định là thuộc tính hay hàm (trong trường hợp có thể là một trong hai).

tức là, ông tuyên bố rằng ngay cả tài liệu cho lớp cũng nên tránh chỉ định liệu "getter" có thực hiện bất kỳ tính toán nào hay không.

Cái này, tôi không làm theo. Không phải tài liệu là nơi quan trọng để thông báo cho người dùng về sự khác biệt này sao? Nếu tôi thiết kế một cơ sở dữ liệu chứa đầy Personcác đối tượng, điều quan trọng là phải biết liệu đó có phải Person.agelà một cuộc gọi đắt tiền hay không , vì vậy tôi có thể quyết định có thực hiện một số loại bộ đệm cho nó không? Tôi đã hiểu sai những gì anh ấy nói, hay anh ấy chỉ là một ví dụ đặc biệt cực đoan về triết lý thiết kế OOP?


1
Câu hỏi thú vị. Tôi đã hỏi về một cái gì đó rất giống nhau gần đây: Làm thế nào tôi có thể thiết kế một giao diện sao cho rõ ràng thuộc tính nào có thể thay đổi giá trị của chúng và giá trị nào sẽ không đổi? . Và tôi đã nhận được một câu trả lời tốt chỉ vào tài liệu, tức là chính xác những gì Bertrand Meyer xuất hiện để tranh luận.
stakx

Tôi chưa đọc cuốn sách này. Meyer có đưa ra bất kỳ ví dụ nào về phong cách tài liệu mà ông đề xuất không? Tôi thấy khó mà tưởng tượng những gì bạn mô tả làm việc cho bất kỳ ngôn ngữ nào .
dùng16764

1
@PatrickCollins Tôi đề nghị bạn đọc 'thực hiện trong vương quốc của danh từ' và nhận được đằng sau khái niệm động từ và danh từ ở đây. Thứ hai, OOP KHÔNG phải về getters và setters, tôi đề nghị Alan Kay (nhà phát minh của OOP): lập trình và quy mô
AndreasScheinert

@AndreasScheinert - bạn đang đề cập đến điều này ? Tôi cười thầm "tất cả chỉ vì muốn có móng ngựa", nhưng dường như nó là một lời ca ngợi về những tệ nạn của lập trình hướng đối tượng.
Patrick Collins

1
@PatrickCollins có này: steve-yegge.blogspot.com/2006/03/... ! Nó đưa ra một số điểm để suy ngẫm, những điểm khác là: bạn nên biến các đối tượng của mình thành cấu trúc dữ liệu bằng cách sử dụng setters.
AndreasScheinert

Câu trả lời:


58

Tôi không nghĩ quan điểm của Meyer là bạn không nên nói với người dùng khi bạn có một hoạt động đắt tiền. Nếu chức năng của bạn sẽ tấn công cơ sở dữ liệu hoặc yêu cầu máy chủ web và dành vài giờ để tính toán, mã khác sẽ cần phải biết điều đó.

Nhưng lập trình viên sử dụng lớp của bạn không cần biết bạn đã triển khai hay chưa:

return currentAge;

hoặc là:

return getCurrentYear() - yearBorn;

Các đặc tính hiệu suất giữa hai cách tiếp cận này rất ít nên không quan trọng. Lập trình viên sử dụng lớp của bạn thực sự không nên quan tâm bạn có gì. Đó là quan điểm của meyer.

Nhưng điều đó không phải lúc nào cũng đúng, ví dụ, giả sử bạn có một phương thức kích thước trên một container. Điều đó có thể được thực hiện:

return size;

hoặc là

return end_pointer - start_pointer;

hoặc có thể là:

count = 0
for(Node * node = firstNode; node; node = node->next)
{
    count++
}
return count

Sự khác biệt giữa hai cái đầu thực sự không nên quan trọng. Nhưng cái cuối cùng có thể có sự phân nhánh hiệu suất nghiêm trọng. Đó là lý do tại sao STL, ví dụ, nói rằng đó .size()O(1). Nó không ghi lại chính xác cách tính kích thước, nhưng nó mang lại cho tôi các đặc tính hiệu suất.

Vì vậy : vấn đề hiệu suất tài liệu. Không chi tiết thực hiện tài liệu. Tôi không quan tâm làm thế nào std :: sắp xếp công cụ của tôi, miễn là nó làm đúng và hiệu quả. Lớp của bạn cũng không nên ghi lại cách nó tính toán mọi thứ, nhưng nếu một cái gì đó có hồ sơ hiệu suất bất ngờ, hãy ghi lại điều đó.


4
Hơn nữa: độ phức tạp của thời gian / không gian tài liệu trước, sau đó đưa ra lời giải thích tại sao một hàm có các thuộc tính đó. Ví dụ:// O(n) Traverses the entire user list.
Jon Purdy

2
= (Một cái gì đó tầm thường như Python lenkhông làm được điều này ... (Trong ít nhất một số tình huống, O(n)như chúng ta đã học trong một dự án ở trường đại học khi tôi đề nghị lưu trữ độ dài thay vì tính toán lại mỗi lần lặp lại)
Izkata

@Izkata, tò mò. Bạn có nhớ cấu trúc là O(n)gì không?
Winston Ewert

@WinstonEwert Thật không may. Đó là hơn 4 năm trước trong một dự án Khai thác dữ liệu và tôi chỉ gợi ý cho bạn tôi một cách linh hoạt vì tôi đã làm việc với C trong một lớp khác ..
Izkata

1
@JonPurdy Tôi sẽ thêm rằng trong mã doanh nghiệp thông thường, có thể không có ý nghĩa gì khi chỉ định độ phức tạp big-O. Ví dụ, truy cập cơ sở dữ liệu O (1) rất có thể sẽ chậm hơn nhiều so với truy cập danh sách trong bộ nhớ O (n), vì vậy hãy ghi lại những gì quan trọng. Nhưng chắc chắn có những trường hợp trong đó độ phức tạp của tài liệu là rất quan trọng (các bộ sưu tập hoặc mã nặng thuật toán khác).
Svick

16

Từ quan điểm thuần túy học thuật hoặc CS, tất nhiên không thể mô tả trong tài liệu bất cứ điều gì về nội bộ của việc thực hiện một tính năng. Đó là bởi vì người dùng của một lớp lý tưởng không nên đưa ra bất kỳ giả định nào về việc triển khai nội bộ của lớp. Nếu việc triển khai thay đổi, lý tưởng là sẽ không có người dùng nào nhận thấy điều đó - tính năng tạo ra sự trừu tượng và các phần bên trong sẽ được ẩn hoàn toàn.

Tuy nhiên, hầu hết các chương trình thực tế bị Joel Spolsky`s "Luật của trừu tượng rò rỉ" , mà nói

"Tất cả các khái niệm trừu tượng không tầm thường, ở một mức độ nào đó, đều bị rò rỉ."

Điều đó có nghĩa là, hầu như không thể tạo ra sự trừu tượng hóa hộp đen đầy đủ các tính năng phức tạp. Và một triệu chứng điển hình của điều này là vấn đề hiệu suất. Vì vậy, đối với các chương trình trong thế giới thực, nó có thể trở nên rất quan trọng, cuộc gọi nào đắt và cuộc gọi nào không, và một tài liệu tốt nên bao gồm thông tin đó (hoặc nên nói nơi người dùng của một lớp được phép đưa ra các giả định về hiệu suất, và ở đâu không ).

Vì vậy, lời khuyên của tôi là: bao gồm thông tin về các cuộc gọi đắt tiền tiềm năng nếu bạn viết tài liệu cho một chương trình trong thế giới thực và loại trừ nó cho một chương trình mà bạn chỉ viết cho mục đích giáo dục của khóa học CS của bạn, cho rằng nên giữ mọi cân nhắc về hiệu suất cố ý ra khỏi phạm vi.


+1, cộng với hầu hết các tài liệu được tạo là để lập trình viên tiếp theo duy trì dự án của bạn chứ không phải lập trình viên tiếp theo sử dụng nó.
jmoreno

12

Bạn có thể viết nếu một cuộc gọi nhất định có đắt hay không. Tốt hơn, sử dụng quy ước đặt tên như getAgeđể truy cập nhanh và loadAgehoặc fetchAgetìm kiếm đắt tiền. Bạn chắc chắn muốn thông báo cho người dùng nếu phương thức đang thực hiện bất kỳ IO nào.

Mỗi chi tiết bạn cung cấp trong tài liệu giống như một hợp đồng phải được cả lớp tôn vinh. Nó sẽ thông báo về hành vi quan trọng. Thông thường, bạn sẽ thấy dấu hiệu phức tạp với ký hiệu O lớn. Nhưng bạn thường muốn ngắn gọn và đi vào vấn đề.


1
+1 để đề cập rằng tài liệu này là một phần của hợp đồng của một lớp như giao diện của nó.
Bart van Ingen Schenau

Tôi ủng hộ điều này. Nói chung, cố gắng giảm thiểu sự cần thiết của getters bằng cách cung cấp các phương thức với hành vi.
bảy lực lượng

9

Nếu tôi thiết kế một cơ sở dữ liệu chứa đầy các đối tượng Person, điều quan trọng không phải là liệu Person.age có phải là một cuộc gọi đắt tiền hay không?

Vâng.

Đây là lý do tại sao đôi khi tôi sử dụng các Find()hàm để chỉ ra rằng việc gọi nó có thể mất một lúc. Đây là một quy ước nhiều hơn bất cứ điều gì khác. Thời gian cần cho một chức năng hoặc thuộc tính để trở lại làm cho không có sự khác biệt cho chương trình (mặc dù nó có thể cho người sử dụng), mặc dù giữa các lập trình viên có một kỳ vọng rằng, nếu nó được khai báo là một thuộc tính, chi phí để gọi nó nên thấp

Trong mọi trường hợp, cần có đủ thông tin trong chính mã để suy luận xem một cái gì đó là hàm hay thuộc tính, vì vậy tôi không thực sự thấy cần phải nói điều đó trong tài liệu.


4
+1: quy ước đó là thành ngữ ở một vài nơi. Hơn nữa, tài liệu nên được thực hiện ở cấp giao diện - tại thời điểm đó bạn không biết cách Person.Age được triển khai.
Telastyn

@Telastyn: Tôi chưa bao giờ nghĩ về tài liệu theo cách này; đó là, nó nên được thực hiện ở cấp độ giao diện. Có vẻ như bây giờ rõ ràng. +1 cho nhận xét có giá trị đó.
stakx

Tôi thích câu trả lời này rất nhiều. Một ví dụ hoàn hảo về những gì bạn mô tả rằng hiệu suất không phải là mối quan tâm đối với chính chương trình sẽ là nếu Person là một thực thể được lấy từ dịch vụ RESTful. GET là cố hữu nhưng không rõ ràng nếu điều này sẽ rẻ hoặc đắt. Điều này tất nhiên không nhất thiết là OOP nhưng quan điểm là như nhau.
maple_shaft

+1 để sử dụng Getcác phương thức trên các thuộc tính để biểu thị thao tác nặng hơn. Tôi đã thấy đủ mã trong đó các nhà phát triển giả định một thuộc tính chỉ là một trình truy cập và sử dụng nó nhiều lần thay vì lưu giá trị vào một biến cục bộ và do đó thực hiện một thuật toán rất phức tạp hơn một lần. Nếu không có quy ước để không thực hiện các thuộc tính như vậy và tài liệu không gợi ý về sự phức tạp, thì tôi mong muốn bất cứ ai phải duy trì một ứng dụng như vậy là may mắn.
enzi

Hội nghị này đến từ đâu? Nghĩ về Java tôi sẽ mong đợi nó theo cách khác: getphương thức tương đương với quyền truy cập thuộc tính và do đó không tốn kém.
bảy lực lượng

3

Điều quan trọng cần lưu ý là phiên bản đầu tiên của cuốn sách này được viết vào năm 1988, vào những ngày đầu của OOP. Những người này đã làm việc với các ngôn ngữ hướng đối tượng thuần túy hơn được sử dụng rộng rãi ngày nay. Các ngôn ngữ OO phổ biến nhất của chúng tôi hiện nay - C ++, C # & Java - có một số khác biệt khá đáng kể so với cách mà các ngôn ngữ OO ban đầu, thuần túy hơn hoạt động.

Trong một ngôn ngữ như C ++ và Java, bạn phải phân biệt giữa việc truy cập một thuộc tính và một cuộc gọi phương thức. Có một thế giới khác biệt giữa instance.getter_methodinstance.getter_method(). Một cái thực sự có được giá trị của bạn và cái kia thì không.

Khi làm việc với ngôn ngữ OO thuần túy hơn, về sự thuyết phục của Smalltalk hoặc Ruby (có vẻ như ngôn ngữ Eiffel được sử dụng trong cuốn sách này), nó trở thành lời khuyên hoàn toàn hợp lệ. Những ngôn ngữ này sẽ ngầm gọi các phương thức cho bạn. Không có sự khác biệt giữa instance.attributeinstance.getter_method.

Tôi sẽ không đổ mồ hôi điểm này hoặc quá coi trọng giáo điều. Mục đích là tốt - bạn không muốn người dùng trong lớp của mình lo lắng về các chi tiết triển khai không liên quan - nhưng nó không dịch rõ ràng theo cú pháp của nhiều ngôn ngữ hiện đại.


1
Điểm rất quan trọng về việc xem xét năm mà đề xuất được đưa ra. Nit: Smalltalk và Simula có từ những năm 60 và 70, vì vậy 88 hầu như không phải là "những ngày đầu".
luser droog

2

Là người dùng, bạn không cần phải biết cách thực hiện.

Nếu hiệu suất là một vấn đề, một cái gì đó phải được thực hiện bên trong lớp thực hiện, chứ không phải xung quanh nó. Do đó, hành động chính xác là sửa lỗi triển khai lớp hoặc gửi lỗi cho người bảo trì.


3
Có phải luôn luôn là một phương pháp đắt tiền tính toán là một lỗi, mặc dù? Như một ví dụ tầm thường, giả sử rằng tôi quan tâm đến việc tổng hợp độ dài của một chuỗi các chuỗi. Trong nội bộ, tôi không biết các chuỗi trong ngôn ngữ của mình là kiểu Pascal hay kiểu C. Trong trường hợp trước, vì các chuỗi "biết" độ dài của chúng, tôi có thể mong đợi vòng lặp tổng chiều dài của mình mất thời gian tuyến tính phụ thuộc vào số lượng chuỗi. Tôi cũng nên biết rằng các hoạt động thay đổi độ dài của chuỗi sẽ có chi phí liên kết với chúng, vì string.lengthsẽ được tính toán lại mỗi khi nó thay đổi.
Patrick Collins

3
Trong trường hợp sau, do chuỗi không "biết" độ dài của nó, tôi có thể mong đợi vòng lặp tổng chiều dài của mình mất thời gian bậc hai (điều này phụ thuộc cả vào số lượng chuỗi và độ dài của chúng), nhưng các hoạt động thay đổi độ dài của chuỗi sẽ rẻ hơn. Cả hai cách triển khai này đều sai và cũng không đáng để báo cáo lỗi, nhưng họ kêu gọi các kiểu mã hóa hơi khác nhau để tránh những trục trặc bất ngờ. Sẽ không dễ dàng hơn nếu người dùng có ít nhất một ý tưởng mơ hồ về những gì đang diễn ra?
Patrick Collins

Vì vậy, nếu bạn biết rằng lớp chuỗi thực hiện kiểu C, bạn sẽ chọn một cách mã hóa có tính đến thực tế đó. Nhưng nếu phiên bản tiếp theo của lớp chuỗi thực hiện biểu diễn kiểu Foo mới thì sao? Bạn sẽ thay đổi mã của mình cho phù hợp hay bạn sẽ chấp nhận mất hiệu suất do các giả định sai trong mã của bạn?
mouviciel

Tôi hiểu rồi. Vì vậy, phản hồi của OO đối với "Làm cách nào tôi có thể loại bỏ một số hiệu suất bổ sung ra khỏi mã của mình, dựa vào việc triển khai cụ thể?" là "Bạn không thể." Và câu trả lời cho "Mã của tôi chậm hơn tôi mong đợi, tại sao?" là "Nó cần phải được viết lại." Đó là nhiều hay ít ý tưởng?
Patrick Collins

2
@PatrickCollins Phản hồi OO dựa trên các giao diện không triển khai. Không sử dụng giao diện không bao gồm các đảm bảo hiệu suất như một phần của định nghĩa giao diện (như ví dụ về C ++ 11 List.size được bảo đảm O (1)). Nó không yêu cầu bao gồm các chi tiết thực hiện trong định nghĩa giao diện. Nếu mã của bạn chậm hơn bạn muốn thì có câu trả lời nào khác ngoài việc bạn sẽ phải thay đổi nó để nhanh hơn (sau khi định hình nó để xác định tắc nghẽn) không?
ném đá

2

Bất kỳ phần tài liệu hướng đến lập trình viên nào không thông báo cho các lập trình viên về chi phí phức tạp của các thói quen / phương pháp là thiếu sót.

  • Chúng tôi đang tìm cách để sản xuất các phương pháp không có tác dụng phụ.

  • Nếu việc thực thi một phương thức có độ phức tạp về thời gian và / hoặc độ phức tạp của bộ nhớ khác O(1), trong các môi trường bị hạn chế về bộ nhớ hoặc thời gian, nó có thể được coi là có tác dụng phụ .

  • Các nguyên tắc nhất bất ngờ bị vi phạm nếu một phương pháp làm điều gì đó hoàn toàn bất ngờ - trong trường hợp này, hogging nhớ hoặc lãng phí thời gian CPU.


1

Tôi nghĩ rằng bạn hiểu anh ấy một cách chính xác, nhưng tôi cũng nghĩ rằng bạn có một điểm tốt. nếu Person.ageđược thực hiện với một phép tính đắt tiền, thì tôi nghĩ tôi cũng muốn thấy điều đó trong tài liệu. Nó có thể tạo ra sự khác biệt giữa việc gọi nó nhiều lần (nếu đó là một hoạt động không tốn kém) hoặc gọi nó một lần và lưu trữ giá trị (nếu nó đắt tiền). Tôi không biết chắc chắn, nhưng tôi nghĩ trong trường hợp này Meyer có thể đồng ý rằng nên đưa vào cảnh báo trong tài liệu.

Một cách khác để xử lý điều này có thể là giới thiệu một thuộc tính mới có tên ngụ ý rằng một phép tính dài có thể xảy ra (chẳng hạn như Person.ageCalculatedFromDB) và sau đó Person.agetrả về một giá trị được lưu trong bộ nhớ cache, nhưng điều này có thể không phải lúc nào cũng phù hợp và dường như quá phức tạp những điều, theo ý kiến ​​của tôi.


3
Người ta cũng có thể đưa ra lập luận rằng nếu bạn cần biết agevề a Person, bạn nên gọi phương thức để lấy nó bất kể. Nếu người gọi bắt đầu làm những việc quá thông minh để tránh phải tính toán, họ sẽ gặp rủi ro khi việc triển khai của họ không hoạt động chính xác vì họ đã vượt qua ranh giới sinh nhật. Việc triển khai tốn kém trong lớp sẽ biểu hiện như các vấn đề về hiệu năng có thể được bắt nguồn từ hồ sơ và các cải tiến như bộ nhớ đệm có thể được thực hiện trong lớp, trong đó tất cả người gọi sẽ thấy lợi ích (và kết quả chính xác).
Blrfl

1
@Blrfl: vâng, vâng, bộ nhớ đệm nên được thực hiện trong Personlớp, nhưng tôi nghĩ rằng câu hỏi được dự định là chung chung hơn và đó Person.agechỉ là một ví dụ. Có thể có một số trường hợp sẽ có ý nghĩa hơn cho người gọi lựa chọn - có thể callee có hai thuật toán khác nhau để tính cùng một giá trị: một nhanh nhưng không chính xác, một chậm hơn nhưng chính xác hơn (hiển thị 3D xuất hiện trong một vị trí nơi mà điều đó có thể xảy ra), và tài liệu nên đề cập đến điều này.
Thất vọngWithFormsDesigner

Hai phương pháp cung cấp kết quả khác nhau là trường hợp sử dụng khác nhau so với khi bạn mong đợi cùng một câu trả lời mỗi lần.
Blrfl

0

Tài liệu cho các lớp hướng đối tượng thường liên quan đến sự đánh đổi giữa việc cho những người duy trì tính linh hoạt của lớp thay đổi thiết kế của nó, so với việc cho phép người tiêu dùng của lớp sử dụng toàn bộ tiềm năng của nó. Nếu một lớp không thay đổi sẽ có một số thuộc tính mà sẽ có một số chính xác mối quan hệ với nhau (ví dụ như Left, Right, vàWidthcác thuộc tính của một hình chữ nhật lưới tọa độ nguyên), người ta có thể thiết kế lớp để lưu trữ bất kỳ sự kết hợp nào của hai thuộc tính và tính toán thứ ba, hoặc người ta có thể thiết kế nó để lưu trữ cả ba. Nếu không có gì về giao diện làm rõ các thuộc tính nào được lưu trữ, lập trình viên của lớp có thể thay đổi thiết kế trong trường hợp làm như vậy sẽ chứng minh hữu ích vì một số lý do. Ngược lại, nếu ví dụ hai trong số các thuộc tính được hiển thị dưới dạng finalcác trường và thứ ba thì không, thì các phiên bản tương lai của lớp sẽ luôn phải sử dụng hai thuộc tính giống như là "cơ sở".

Nếu các thuộc tính không có mối quan hệ chính xác (ví dụ vì chúng floathoặc doublehơn thế int), thì có thể cần phải ghi lại các thuộc tính nào "xác định" giá trị của một lớp. Ví dụ, mặc dù Leftcộng Widthđược cho là bằng nhau Right, toán học dấu phẩy động thường không chính xác. Ví dụ, giả sử a Rectanglesử dụng kiểu Floatchấp nhận LeftWidthlàm tham số hàm tạo được xây dựng với giá Lefttrị as 1234567fWidthas 1.1f. floatĐại diện tốt nhất của tổng là 1234568.125 [có thể hiển thị là 1234568.13]; cái nhỏ hơn tiếp theo floatsẽ là 1234568.0. Nếu lớp thực sự lưu trữ LeftWidth, nó có thể báo cáo giá trị chiều rộng như được chỉ định. Tuy nhiên, nếu các nhà xây dựng tính toán Rightdựa trên các thông qua trong LeftWidth, và sau đó tính toán Widthdựa trên LeftRight, nó sẽ báo cáo chiều rộng như 1.25fchứ không phải là thông qua trong 1.1f.

Với các lớp có thể thay đổi, mọi thứ thậm chí còn thú vị hơn, vì một thay đổi đối với một trong các giá trị liên quan đến nhau sẽ ngụ ý thay đổi ít nhất một giá trị khác, nhưng có thể không phải lúc nào cũng rõ ràng. Trong một số trường hợp, nó có thể là tốt nhất để tránh việc các phương pháp mà "set" một tài sản duy nhất như vậy, nhưng thay vào đó hoặc có phương pháp để ví dụ SetLeftAndWidthhoặc SetLeftAndRight, nếu không làm rõ những gì thuộc tính đang được xác định và được thay đổi (ví dụ MoveRightEdgeToSetWidth, ChangeWidthToSetLeftEdgehay MoveShapeToSetRightEdge) .

Đôi khi có thể hữu ích khi có một lớp theo dõi các giá trị thuộc tính nào đã được chỉ định và được tính toán từ các lớp khác. Ví dụ: lớp "khoảnh khắc trong thời gian" có thể bao gồm thời gian tuyệt đối, giờ địa phương và độ lệch múi giờ. Cũng như nhiều loại như vậy, với hai thông tin bất kỳ, người ta có thể tính toán thứ ba. Biết cái nàophần thông tin đã được tính toán, tuy nhiên, đôi khi có thể quan trọng. Ví dụ: giả sử rằng một sự kiện được ghi lại là đã xảy ra vào "17:00 UTC, múi giờ -5, giờ địa phương 12:00 chiều" và một lần sau đó phát hiện ra rằng múi giờ phải là -6. Nếu ai đó biết rằng UTC đã được ghi lại trên máy chủ, thì bản ghi sẽ được sửa thành "18:00 UTC, múi giờ -6, giờ địa phương 12:00 chiều"; nếu ai đó bấm giờ trong giờ địa phương thì đồng hồ sẽ là "17:00 UTC, múi giờ -6, giờ địa phương 11:00 sáng". Tuy nhiên, không biết thời gian toàn cầu hay địa phương nên được coi là "đáng tin hơn", tuy nhiên, không thể biết nên áp dụng chỉnh sửa nào. Tuy nhiên, nếu bản ghi theo dõi thời gian được chỉ định, các thay đổi về múi giờ có thể để lại một mình trong khi thay đổi thời gian khác.


0

Tất cả các quy tắc này về cách ẩn thông tin trong các lớp có ý nghĩa hoàn hảo về giả định cần phải bảo vệ chống lại ai đó trong số những người dùng của lớp sẽ phạm sai lầm khi tạo ra sự phụ thuộc vào việc thực hiện nội bộ.

Thật tốt khi xây dựng sự bảo vệ như vậy, nếu lớp có đối tượng như vậy. Nhưng khi người dùng viết một cuộc gọi đến một chức năng trong lớp của bạn, họ sẽ tin tưởng bạn với tài khoản ngân hàng thời gian thực hiện của họ.

Đây là loại điều tôi thấy rất nhiều:

  1. Các đối tượng có một bit "được sửa đổi" nói rằng, theo một cách nào đó, chúng đã lỗi thời. Đủ đơn giản, nhưng sau đó chúng có các đối tượng cấp dưới, do đó, thật đơn giản để cho "sửa đổi" là một hàm tổng hợp trên tất cả các đối tượng cấp dưới. Sau đó, nếu có nhiều lớp đối tượng phụ (đôi khi chia sẻ cùng một đối tượng nhiều lần), đơn giản "Nhận" thuộc tính "đã sửa đổi" có thể sẽ mất một phần thời gian thực hiện lành mạnh.

  2. Khi một đối tượng theo một cách nào đó được sửa đổi, người ta cho rằng các đối tượng khác nằm rải rác xung quanh phần mềm cần phải được "thông báo". Điều này có thể diễn ra trên nhiều lớp cấu trúc dữ liệu, cửa sổ, v.v. được viết bởi các lập trình viên khác nhau và đôi khi lặp lại trong các lần truy cập vô hạn cần được bảo vệ chống lại. Ngay cả khi tất cả các nhà văn của những người xử lý thông báo đó cẩn thận một cách hợp lý để không lãng phí thời gian, toàn bộ tương tác tổng hợp có thể kết thúc bằng cách sử dụng một phần lớn thời gian thực hiện không dự đoán được và cực kỳ khó khăn, và giả định rằng nó chỉ đơn giản là "cần thiết".

VÌ VẬY, tôi muốn thấy các lớp trình bày một giao diện trừu tượng sạch đẹp với thế giới bên ngoài, nhưng tôi muốn có một số khái niệm về cách chúng hoạt động, nếu chỉ để hiểu những gì chúng đang cứu tôi. Nhưng ngoài ra, tôi có xu hướng cảm thấy rằng "ít hơn là nhiều hơn". Mọi người rất say mê cấu trúc dữ liệu đến mức họ nghĩ nhiều hơn là tốt hơn và khi tôi thực hiện điều chỉnh hiệu suất, lý do lớn cho các vấn đề về hiệu suất là sự tuân thủ chặt chẽ với các cấu trúc dữ liệu cồng kềnh được xây dựng theo cách mọi người được dạy.

Vì vậy, đi con số.


0

Việc thêm các chi tiết triển khai như "tính toán hay không" hoặc "thông tin hiệu suất" làm cho nó trở nên phổ biến hơn để giữ cho mã và tài liệu được đồng bộ hóa .

Thí dụ:

Nếu bạn có một phương thức "đắt tiền hiệu năng", bạn có muốn ghi lại "đắt" cho tất cả các lớp sử dụng phương thức đó không? Điều gì sẽ xảy ra nếu bạn thay đổi việc thực hiện để không còn tốn kém nữa. Bạn có muốn cập nhật thông tin này cho tất cả người tiêu dùng không?

Tất nhiên, thật tốt cho người bảo trì mã để có được tất cả các thông tin quan trọng từ tài liệu mã, nhưng tôi không thích tài liệu tuyên bố điều gì đó không còn hợp lệ nữa (không đồng bộ với mã)


0

Khi câu trả lời được chấp nhận đi đến kết luận:

Vì vậy: vấn đề hiệu suất tài liệu.

mã tự ghi được coi là tốt hơn tài liệu theo sau tên của phương thức sẽ nêu bất kỳ kết quả hoạt động bất thường nào.

Vì vậy, vẫn Person.agecho return current_year - self.birth_datenhưng nếu phương thức sử dụng một vòng lặp để tính tuổi (có):Person.calculateAge()

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.