Là thông tin ẩn nhiều hơn một quy ước?


20

Trong Java, C # và nhiều ngôn ngữ được gõ mạnh, được kiểm tra tĩnh, chúng tôi được sử dụng để viết mã như thế này:

public void m1() { ... }
protected void m2() { ... }
private void m2() { ... }
void m2() { ... }

Một số ngôn ngữ được kiểm tra động không cung cấp từ khóa để thể hiện mức độ "riêng tư" của một thành viên lớp nhất định và thay vào đó dựa vào các quy ước mã hóa. Python ví dụ tiền tố thành viên riêng với dấu gạch dưới:

_m(self): pass

Có thể lập luận rằng việc cung cấp các từ khóa như vậy trong các ngôn ngữ được kiểm tra động sẽ thêm ít sử dụng vì nó chỉ được kiểm tra khi chạy.

Tuy nhiên, tôi cũng không thể tìm thấy một lý do chính đáng để cung cấp các từ khóa này bằng các ngôn ngữ được kiểm tra tĩnh. Tôi thấy yêu cầu phải điền mã của mình bằng các từ khóa khá dài dòng như protectedvừa khó chịu vừa mất tập trung. Cho đến nay, tôi đã không ở trong một tình huống mà một lỗi trình biên dịch gây ra bởi các từ khóa này sẽ cứu tôi khỏi một lỗi. Hoàn toàn ngược lại, tôi đã ở trong tình huống đặt nhầm chỗ protectedkhiến tôi không thể sử dụng thư viện.

Với ý nghĩ này, câu hỏi của tôi là:

Là thông tin ẩn nhiều hơn một quy ước giữa các lập trình viên được sử dụng để xác định đâu là một phần của giao diện chính thức của một lớp?

Nó có thể được sử dụng để bảo đảm trạng thái bí mật của một lớp khỏi bị tấn công không? Phản xạ có thể ghi đè lên cơ chế này? Điều gì sẽ làm cho nó đáng giá cho trình biên dịch để thực thi ẩn thông tin?


10
Xin lỗi, Eric Lippert đang trong kỳ nghỉ :)
Stewol

Câu hỏi của bạn hơi khó hiểu. Về cơ bản, bạn chỉ hỏi nếu ai đó có thể truy cập một thành viên tư nhân từ bên ngoài?
Morgan Herlocker


@ironcode: Tôi có thể nói rằng bằng cách nào đó tôi không thể hiện bản thân đủ rõ ràng từ một số câu trả lời. Bạn có thể nói những gì cụ thể làm cho nó khó hiểu? Tôi sẽ cố gắng làm rõ.
blubb

2
"Thật vô nghĩa khi cung cấp thông tin vào thời gian biên dịch và sau đó chỉ kiểm tra nó khi chạy" - thật sao? Tốt hơn nên nói điều đó với Microsoft bởi vì đó là một đoạn đáng kể của khung .NET hiện tại dựa trên: chọn lọc chọn lọc hoặc từ chối gõ tĩnh. Tôi không nghĩ rằng đây thực sự là những gì bạn có ý nghĩa; câu hỏi liên tục nói về kiểm tra "tĩnh" so với "động", nhưng đó là những đặc điểm của hệ thống loại; những gì thực sự có vấn đề ở đây là thời gian biên dịch so với ràng buộc thời gian chạy .
Aaronaught

Câu trả lời:


4

Tôi đang nghiên cứu chứng nhận Java và toàn bộ vấn đề liên quan đến vấn đề đó là làm thế nào để quản lý các công cụ sửa đổi truy cập. Và chúng có ý nghĩa và chúng nên được sử dụng đúng cách.

Tôi đã làm việc với python và, trong hành trình học hỏi của mình, tôi đã nghe nói rằng trong python, quy ước là có bởi vì mọi người, những người làm việc với nó, nên biết ý nghĩa của nó và cách áp dụng nó. Điều đó nói rằng, ở _m(self): passphần gạch dưới sẽ cảnh báo tôi không được lộn xộn với lĩnh vực đó. Nhưng mọi người sẽ theo quy ước đó chứ? Tôi làm việc với javascript và tôi phải nói rằng, họ không làm thế . Đôi khi tôi cần kiểm tra một vấn đề và lý do là người đó đang làm gì đó mà anh ta không phải làm ...

đọc cuộc thảo luận này liên quan đến con trăn hàng đầu

Là thông tin ẩn nhiều hơn một quy ước giữa các lập trình viên được sử dụng để xác định đâu là một phần của giao diện chính thức của một lớp?

Từ những gì tôi nói, Có.

Nó có thể được sử dụng để bảo đảm trạng thái bí mật của một lớp khỏi bị tấn công không? Phản xạ có thể ghi đè lên cơ chế này? Có bất kỳ lợi thế nào khác được cung cấp bởi một cơ chế chính thức hơn được thực thi bởi trình biên dịch không?

Có, nó có thể được sử dụng để bảo vệ trạng thái bí mật của một lớp và nó không chỉ được sử dụng cho điều đó mà còn để ngăn người dùng làm rối với trạng thái đối tượng để thay đổi hành vi của họ thành một thứ mà họ nghĩ nên là cách mà đối tượng nên có đã làm việc Bạn, với tư cách là một nhà phát triển, nên lập kế hoạch và suy nghĩ về nó và thiết kế lớp của bạn theo cách có ý nghĩa, theo cách mà hành vi của nó sẽ không bị can thiệp. Và trình biên dịch, như một người bạn tốt, sẽ giúp bạn giữ mã theo cách bạn thiết kế để thực thi các chính sách truy cập.

EDITED Có, phản ánh có thể, kiểm tra các ý kiến

Câu hỏi cuối cùng là một câu hỏi thú vị và tôi sẵn sàng đọc các câu trả lời liên quan đến nó.


3
Sự phản chiếu thường có thể bỏ qua việc bảo vệ truy cập. Đối với Java và C #, cả hai đều có thể truy cập dữ liệu riêng tư.
Đánh dấu H

Cảm ơn! Các câu trả lời ở đây: stackoverflow.com/questions/1565734/ từ giải thích nó!
wleao

1
Văn hóa Javascript rất khác với văn hóa python. Python có pep-08 và hầu hết tất cả các nhà phát triển python đều tuân theo các quy ước này. Vi phạm PEP-08 thường được coi là một lỗi. Vì vậy, tôi nghĩ rằng kinh nghiệm javascript / php / perl / ruby ​​chuyển rất ít sang trải nghiệm python ở khía cạnh này: _private hoạt động rất tốt trong python.
Mike Korobov

1
Trong một số (nhưng không phải tất cả) ngôn ngữ lập trình, ẩn thông tin có thể được sử dụng để bảo vệ chống lại mã thù địch. Ẩn thông tin không thể được sử dụng để bảo vệ chống lại người dùng thù địch.
Brian

2
@Mike nếu vi phạm PEP-08 thường "bị coi là lỗi" và không có nhà phát triển Python nào mơ ước không tuân theo các quy ước, thì bạn cũng có thể thực thi chúng bằng ngôn ngữ! Tại sao menial hoạt động khi ngôn ngữ có thể làm điều đó tự động?
Andres F.

27

Trình xác định truy cập "riêng tư" không phải là về lỗi trình biên dịch mà nó tạo ra lần đầu tiên bạn nhìn thấy. Trong thực tế, đó là về việc ngăn bạn truy cập vào thứ gì đó vẫn có thể thay đổi khi việc triển khai lớp giữ thành viên riêng thay đổi.

Nói cách khác, không cho phép bạn sử dụng nó khi nó vẫn hoạt động sẽ ngăn bạn vô tình vẫn sử dụng nó khi nó không còn hoạt động.

Như Del Nam đã nhận xét bên dưới quy ước tiền tố không khuyến khích việc sử dụng ngẫu nhiên các thành viên có thể thay đổi miễn là quy ước được tuân theo và hiểu đúng. Đối với người dùng độc hại (hoặc không biết gì), không có gì ngăn họ truy cập thành viên đó với tất cả các hậu quả có thể xảy ra. Trong các ngôn ngữ có hỗ trợ tích hợp cho các bộ chỉ định truy cập, điều này không xảy ra trong sự thiếu hiểu biết (lỗi trình biên dịch) và nổi bật như ngón tay cái đau khi độc hại (các cấu trúc lạ để đến với thành viên riêng).

Trình xác định truy cập "được bảo vệ" là một câu chuyện khác - đừng nghĩ về điều này đơn giản là "không hoàn toàn công khai" hoặc "rất giống như riêng tư". "Được bảo vệ" có nghĩa là bạn có thể sẽ muốn sử dụng chức năng đó khi bạn xuất phát từ lớp có chứa thành viên được bảo vệ. Các thành viên được bảo vệ là một phần của "giao diện mở rộng" mà bạn sẽ sử dụng để thêm chức năng lên trên các lớp hiện có mà không thay đổi chính các lớp hiện có đó.

Vì vậy, tóm tắt ngắn gọn:

  • công khai: Sử dụng an toàn trong các trường hợp của lớp, mục đích của lớp, sẽ không thay đổi.
  • bảo vệ: Được sử dụng khi mở rộng (xuất phát từ) lớp - có thể thay đổi nếu việc triển khai phải thay đổi mạnh mẽ.
  • riêng tư: Đừng chạm vào! Có thể thay đổi theo ý muốn để cung cấp triển khai tốt hơn các giao diện dự kiến.

3
Quan trọng hơn, bất kỳ (dễ nhận biết, đó là trường hợp ví dụ như dấu gạch dưới hàng đầu) không được thực thi theo quy định về "chi tiết thực hiện, có thể thay đổi" cũng tránh việc sử dụng một thành viên tư nhân. Ví dụ, trong Python, lần duy nhất một lập trình viên lành mạnh viết mã bằng cách sử dụng một thành viên riêng từ bên ngoài (và thậm chí sau đó nó cau mày) là khi gần như không thể tránh khỏi, anh ta biết chính xác những gì anh ta làm và chấp nhận sự phá vỡ tiềm năng trong các phiên bản trong tương lai . Đó là, một tình huống trong đó các lập trình viên Java sẽ sử dụng sự phản chiếu.

3
@Simon Stelling - không, tôi đang nói rằng người triển khai ban đầu của lớp đã tạo ra một cái gì đó riêng tư đang nói với bạn "đừng sử dụng cái này, mọi hành vi ở đây có thể thay đổi trong tương lai". Ví dụ, một biến thành viên riêng của một lớp trước tiên chứa khoảng cách giữa hai điểm sau đó có thể chứa khoảng cách giữa hai điểm bình phương vì lý do hiệu suất. Bất cứ điều gì có thể dựa vào hành vi cũ sẽ âm thầm phá vỡ.
Joris Timmermans

1
@MadKeithV: Vậy thì một từ khóa cấp ngôn ngữ nói "đừng chạm vào vì ..." khác với một quy ước có nghĩa giống nhau như thế nào? Chỉ trình biên dịch thực thi nó, nhưng sau đó chúng tôi quay lại thảo luận tĩnh so với thảo luận động.

7
@Simon Stelling (và Delnan) - với tôi đó là kiểu như hỏi "bộ giới hạn động cơ khác với biển báo giới hạn tốc độ như thế nào".
Joris Timmermans

1
Tôi nhận thức rõ về sự khác biệt và tôi chưa bao giờ nói nó không có ở đó. Tôi chỉ thấy rằng câu trả lời của bạn và hầu hết các nhận xét tiếp theo của bạn chỉ mô tả các loại chú thích cho các thành viên và phương pháp có thể được thực hiện bằng các quy ước hoặc từ khóa cấp ngôn ngữ và để lại sự khác biệt thực tế (sự hiện diện của việc thực thi cấp độ lan truyền) suy luận của người đọc.

11

Nếu bạn đang viết mã sẽ được người khác sử dụng, thì việc ẩn thông tin có thể cung cấp giao diện undertand dễ dàng hơn nhiều. "Người khác" có thể là một nhà phát triển khác trong nhóm của bạn, các nhà phát triển sử dụng API mà bạn đã viết thương mại hoặc thậm chí là người tương lai của bạn, người "chỉ không thể nhớ cách thức hoạt động của điều đó". Thật dễ dàng để làm việc với một lớp chỉ có 4 phương thức có sẵn hơn một phương thức có 40.


2
Tôi hoàn toàn đồng ý, nhưng điều này không trả lời câu hỏi của tôi. Cho dù tôi sử dụng _memberhay private membertrong lớp học của tôi là không đáng kể, miễn là các lập trình viên khác hiểu rằng anh ta chỉ nên nhìn vào publichoặc không có tiền tố thành viên.
blubb

6
Có, nhưng tài liệu công khai của một lớp được viết bằng ngôn ngữ động sẽ không ném 36 phần riêng tư đó vào bạn (ít nhất là không theo mặc định).

3
@Simon Tuyên bố rằng "chỉ là một quy ước" là sai lệch, vì nó có lợi ích thực sự. "Chỉ là một quy ước" là một cái gì đó giống như dấu ngoặc nhọn trên dòng tiếp theo. Một quy ước với lợi ích là một cái gì đó giống như sử dụng tên có ý nghĩa, mà tôi muốn nói là hoàn toàn khác nhau. Việc thiết lập một cái gì đó để riêng tư ngăn ai đó nhìn thấy bạn "phương pháp bí mật"? Không, không phải nếu họ quyết tâm.
Morgan Herlocker

1
@ Simon- cũng vậy, chỉ đưa ra các phương thức an toàn để sử dụng sẽ làm tăng khả năng người dùng sẽ không phá vỡ thứ gì đó khi cố gắng sử dụng lớp. Có thể dễ dàng các phương thức có thể đánh sập một máy chủ web nếu được gọi một ngàn lần. Bạn không muốn người tiêu dùng API của bạn có thể làm điều đó. Trừu tượng không chỉ là một quy ước.
Morgan Herlocker

4

Tôi muốn đề nghị rằng đối với nền, bạn bắt đầu bằng cách đọc về bất biến lớp .

Một bất biến là, để làm cho một câu chuyện dài trở nên ngắn gọn, một giả định về tình trạng của một lớp được cho là vẫn đúng trong suốt vòng đời của một lớp.

Hãy sử dụng một ví dụ C # thực sự đơn giản:

public class EmailAlert
{
    private readonly List<string> addresses = new List<string>();

    public void AddRecipient(string address)
    {
        if (!string.IsNullOrEmpty(address))
            addresses.Add(address);
    }

    public void Send(string message)
    {
        foreach (string address in addresses)
            SendTo(address, message);
    }

    // Details of SendTo not shown
}

Những gì đang xảy ra ở đây?

  • Các thành viên addressesđược khởi tạo về xây dựng lớp.
  • Nó là riêng tư, vì vậy không có gì từ bên ngoài có thể chạm vào nó.
  • Chúng tôi cũng đã tạo ra nó readonly, vì vậy không có gì từ bên trong có thể chạm vào nó sau khi xây dựng (điều này không phải lúc nào cũng đúng / cần thiết, nhưng nó hữu ích ở đây).
  • Do đó, Sendphương pháp có thể đưa ra một giả định addressessẽ không bao giờ xảy ra null. Nó không phải thực hiện kiểm tra đó bởi vì không có cách nào có thể thay đổi giá trị.

Nếu các lớp khác được phép ghi vào addressestrường (tức là nếu có public), thì giả định này sẽ không còn hiệu lực. Mỗi một phương thức khác trong lớp phụ thuộc vào trường đó sẽ phải bắt đầu thực hiện kiểm tra null rõ ràng hoặc có nguy cơ làm hỏng chương trình.

Vì vậy, có, nó không chỉ là một "quy ước"; tất cả các công cụ sửa đổi truy cập trên các thành viên lớp cùng nhau tạo thành một tập hợp các giả định về thời điểm và cách thức trạng thái đó có thể được thay đổi. Những giả định đó sau đó được kết hợp thành các thành viên và lớp học phụ thuộc và phụ thuộc lẫn nhau để các lập trình viên không phải suy luận về toàn bộ trạng thái của chương trình cùng một lúc. Khả năng đưa ra các giả định là một yếu tố quan trọng để quản lý sự phức tạp trong phần mềm.

Đối với những câu hỏi khác:

Nó có thể được sử dụng để bảo đảm trạng thái bí mật của một lớp khỏi bị tấn công không?

Có và không. Mã bảo mật, giống như hầu hết các mã, sẽ dựa vào một số bất biến nhất định. Công cụ sửa đổi truy cập chắc chắn hữu ích như các biển chỉ dẫn cho những người gọi đáng tin cậy mà họ không cần phải gây rối với nó. Mã độc hại sẽ không quan tâm, nhưng mã độc cũng không phải thông qua trình biên dịch.

Phản xạ có thể ghi đè lên cơ chế này?

Tất nhiên là có thể. Nhưng sự phản ánh đòi hỏi mã gọi để có mức đặc quyền / tin cậy đó. Nếu bạn đang chạy mã độc với sự tin tưởng hoàn toàn và / hoặc đặc quyền quản trị, thì bạn đã thua trận chiến đó.

Điều gì sẽ làm cho nó đáng giá cho trình biên dịch để thực thi ẩn thông tin?

Trình biên dịch đã không thực thi nó. Thời gian chạy trong .NET, Java và các môi trường khác như vậy - opcode được sử dụng để gọi một phương thức sẽ không thành công nếu phương thức này là riêng tư. Các cách duy nhất xung quanh hạn chế đó yêu cầu mã đáng tin cậy / nâng cao và mã nâng cao luôn có thể chỉ cần ghi trực tiếp vào bộ nhớ của chương trình. Nó được thi hành nhiều như nó có thể được thi hành mà không yêu cầu một hệ điều hành tùy chỉnh.


1
@Simon: Chúng ta cần làm rõ "sự thiếu hiểu biết" ở đây. Tiền tố một phương thức với _các hợp đồng, nhưng không thực thi nó. Điều đó có thể khiến bạn khó có thể không biết gì về hợp đồng, nhưng nó không làm cho việc phá vỡ hợp đồng trở nên khó khăn , đặc biệt là khi so sánh với các phương pháp tẻ nhạt và thường bị hạn chế như Reflection. Một quy ước nói, "bạn không nên phá vỡ điều này". Công cụ sửa đổi truy cập cho biết, "bạn không thể phá vỡ điều này". Tôi không cố nói rằng một cách tiếp cận tốt hơn phương pháp kia - cả hai đều tốt tùy thuộc vào triết lý của bạn, nhưng chúng vẫn là những cách tiếp cận rất khác nhau.
Aaron

2
Làm thế nào về một sự tương tự: A private/ protectedmodifier truy cập giống như một bao cao su. Bạn không cần phải sử dụng một cái, nhưng nếu bạn không muốn, thì tốt hơn hết là bạn nên chắc chắn về thời gian của mình và (các) đối tác của bạn. Nó sẽ không ngăn chặn một hành động độc hại và thậm chí có thể không hoạt động mọi lúc, nhưng nó chắc chắn sẽ giảm rủi ro do bất cẩn, và làm như vậy hiệu quả hơn nhiều so với việc nói "làm ơn cẩn thận". Ồ, và nếu bạn có một cái, nhưng đừng sử dụng nó, thì đừng mong tôi có cảm tình với tôi.
Aaronaught

3

Việc che giấu thông tin không chỉ là một quy ước; cố gắng đi xung quanh nó thực sự có thể phá vỡ chức năng của lớp trong nhiều trường hợp. Chẳng hạn, việc lưu trữ một giá trị trong một privatebiến là khá phổ biến, phơi bày nó bằng cách sử dụng một protectedhoặc publicthuộc tính cùng thời gian và trong getter, kiểm tra null và thực hiện bất kỳ khởi tạo nào cần thiết (ví dụ, lười tải). Hoặc lưu trữ một cái gì đó trong một privatebiến, phơi bày nó bằng cách sử dụng một thuộc tính và trong setter, kiểm tra xem giá trị thay đổi và cháy PropertyChanging/ PropertyChangedsự kiện. Nhưng không nhìn thấy việc thực hiện nội bộ, bạn sẽ không bao giờ biết tất cả những gì đang diễn ra đằng sau hậu trường.


3

Ẩn thông tin phát triển từ một triết lý thiết kế từ trên xuống. Python đã được gọi là ngôn ngữ từ dưới lên .

Việc che giấu thông tin được thi hành tốt ở cấp độ lớp trong Java, C ++ và C #, vì vậy đây không thực sự là một quy ước ở cấp độ này. Thật dễ dàng để biến một lớp thành một "hộp đen", với các giao diện công cộng và các chi tiết ẩn (riêng tư).

Như bạn đã chỉ ra, trong Python, các lập trình viên phải tuân theo quy ước không sử dụng những gì được dự định ẩn, vì mọi thứ đều hiển thị.

Ngay cả với Java, C ++ hoặc C #, tại một số điểm, việc ẩn thông tin trở thành một quy ước. Không có kiểm soát truy cập ở mức độ trừu tượng cao nhất liên quan đến các kiến ​​trúc phần mềm phức tạp hơn. Ví dụ: trong Java, bạn có thể tìm thấy việc sử dụng ".iternal." tên gói. Đây hoàn toàn là một quy ước đặt tên vì không dễ để loại thông tin ẩn này được thực thi chỉ thông qua khả năng truy cập gói.

Một ngôn ngữ cố gắng xác định quyền truy cập chính thức là Eiffel. Bài viết này chỉ ra một số điểm yếu che giấu thông tin khác của các ngôn ngữ như Java.

Bối cảnh: Việc che giấu thông tin được đề xuất vào năm 1971 bởi David Parnas . Ông chỉ ra trong bài báo rằng việc sử dụng thông tin về các mô-đun khác có thể "làm tăng đáng kể khả năng kết nối của cấu trúc hệ thống". Theo ý tưởng này, việc thiếu thông tin ẩn có thể dẫn đến các hệ thống được liên kết chặt chẽ, khó bảo trì. Anh tiếp tục với:

Chúng tôi mong muốn cấu trúc hệ thống được xác định rõ ràng bởi các nhà thiết kế trước khi bắt đầu lập trình, thay vì vô tình sử dụng thông tin của lập trình viên.


1
+1 cho trích dẫn "sử dụng thông tin của lập trình viên", đó là chính xác (mặc dù bạn có thêm dấu phẩy ở cuối).
jmoreno

2

Ruby và PHP có nó và thực thi nó trong thời gian chạy.

Điểm ẩn thông tin thực sự là thông tin hiển thị. Bằng cách "che giấu" các chi tiết nội bộ, mục đích trở nên rõ ràng từ góc nhìn bên ngoài. Có những ngôn ngữ, trong đó nắm lấy điều này. Trong Java, truy cập mặc định để gói nội bộ, trong haXe để được bảo vệ. Bạn tuyên bố rõ ràng công khai để lộ chúng.

Toàn bộ vấn đề này là làm cho các lớp của bạn dễ sử dụng bằng cách chỉ hiển thị một giao diện rất mạch lạc. Bạn muốn phần còn lại được bảo vệ, để không có anh chàng thông minh nào đến và gây rối với trạng thái nội bộ của bạn để lừa lớp bạn làm những gì anh ta muốn.

Ngoài ra, khi các công cụ sửa đổi truy cập được thi hành trong thời gian chạy, bạn có thể sử dụng chúng để thực thi một mức bảo mật nhất định, nhưng tôi không nghĩ đây là một giải pháp đặc biệt tốt.


1
Nếu thư viện của bạn đang được sử dụng trong cùng một công ty nơi bạn đang làm việc, bạn sẽ quan tâm ... Nếu anh ta làm hỏng ứng dụng của mình, bạn sẽ gặp khó khăn khi sửa nó.
wleao

1
Tại sao phải giả vờ đeo đai an toàn khi bạn có thể đeo? Tôi muốn viết lại câu hỏi và hỏi, có lý do nào để trình biên dịch không thực thi nó không, nếu tất cả thông tin có sẵn cho nó. Bạn không muốn đợi cho đến khi gặp sự cố để tìm hiểu xem chiếc đai an toàn đó có đáng đeo hay không.
Đánh dấu

1
@Simon: tại sao làm lộn xộn một giao diện với những thứ bạn không muốn mọi người sử dụng. Tôi ghét rằng C ++ yêu cầu mọi thành viên phải có mặt trong định nghĩa lớp trong tệp tiêu đề (tôi hiểu tại sao). Giao diện dành cho người khác và không nên vứt rác với những thứ họ không thể sử dụng.
phkahler

1
@phkahler: pydocxóa _prefixedMemberskhỏi giao diện. Các giao diện lộn xộn không liên quan gì đến việc lựa chọn từ khóa được kiểm tra bởi trình biên dịch.
blubb

2

Công cụ sửa đổi Acces chắc chắn có thể làm một cái gì đó mà quy ước không thể.

Ví dụ: trong Java, bạn không thể truy cập thành viên / trường riêng trừ khi bạn sử dụng sự phản chiếu.

Do đó, nếu tôi viết giao diện cho một plugin và từ chối chính xác các quyền để sửa đổi các trường riêng thông qua sự phản chiếu (và để đặt trình quản lý bảo mật :)), tôi có thể gửi một số đối tượng đến các chức năng được thực hiện bởi bất kỳ ai và biết rằng anh ta không thể truy cập vào các trường riêng tư của nó.

Tất nhiên có thể có một số lỗi bảo mật cho phép anh ta khắc phục điều này, nhưng điều này không quan trọng về mặt triết học (nhưng trong thực tế thì chắc chắn là như vậy).

Nếu người dùng chạy giao diện của tôi trong môi trường của anh ta, anh ta có quyền kiểm soát và do đó có thể phá vỡ các sửa đổi truy cập.


2

Tôi đã không ở trong một tình huống mà một lỗi biên dịch gây ra bởi các từ khóa này sẽ cứu tôi khỏi một lỗi.

Sẽ không quá nhiều để cứu các tác giả ứng dụng khỏi các lỗi khi để các tác giả thư viện quyết định phần nào trong quá trình thực hiện của họ mà họ cam kết duy trì.

Nếu tôi có một thư viện

class C {
  public void foo() { ... }

  private void fooHelper() { /* lots of complex code */ }
}

Tôi có thể muốn có thể thay thế fooviệc thực hiện và có thể thay đổi fooHelpertheo những cách triệt để. Nếu một nhóm người đã quyết định sử dụng fooHelperbất chấp tất cả các cảnh báo trong tài liệu của tôi thì tôi có thể không làm được điều đó.

privatecho phép các tác giả thư viện chia các thư viện thành các phương thức có kích thước có thể quản lý (và privatecác lớp trợ giúp) mà không sợ rằng họ sẽ bị buộc phải duy trì các chi tiết nội bộ đó trong nhiều năm.


Điều gì sẽ làm cho nó đáng giá cho trình biên dịch để thực thi ẩn thông tin?

Mặt khác, trong Java privatekhông phải do trình biên dịch thực thi, mà bởi trình xác minh mã byte của Java .


Phản xạ có thể ghi đè lên cơ chế này? Điều gì sẽ làm cho nó đáng giá cho trình biên dịch để thực thi ẩn thông tin?

Trong Java, không chỉ sự phản chiếu có thể ghi đè lên cơ chế này. Có hai loại privatetrong Java. Loại privatengăn không cho một lớp bên ngoài truy cập vào privatecác thành viên của lớp bên ngoài khác được kiểm tra bởi trình xác minh mã byte, nhưng cũng privateđược sử dụng bởi một lớp bên trong thông qua phương thức truy cập tổng hợp riêng tư gói như trong

 public class C {
   private int i = 42;

   public class B {
     public void incr() { ++i; }
   }
 }

Do lớp B(thực sự được đặt tên C$B) sử dụng i, trình biên dịch tạo ra một phương thức Btruy cập tổng hợp cho phép truy cập C.itheo cách vượt qua trình xác minh mã byte. Thật không may, vì ClassLoadercho phép bạn tạo một lớp từ một byte[], khá đơn giản để có được các phần riêng tư Cđã tiếp xúc với các lớp bên trong bằng cách tạo một lớp mới trongC gói có thể nếu Cbình không được niêm phong.

Việc privatethực thi đúng cách đòi hỏi sự phối hợp giữa các trình nạp lớp, trình xác minh mã byte và chính sách bảo mật có thể ngăn chặn truy cập phản chiếu đến các vùng riêng.


Nó có thể được sử dụng để bảo đảm trạng thái bí mật của một lớp khỏi bị tấn công không?

Vâng. "Phân tách an toàn" có thể có khi các lập trình viên có thể cộng tác trong khi mỗi bảo toàn các thuộc tính bảo mật của các mô-đun của họ - Tôi không phải tin tưởng tác giả của một mô-đun mã khác không vi phạm các thuộc tính bảo mật của mô-đun của tôi.

Các ngôn ngữ Khả năng đối tượng như Joe-E sử dụng ẩn thông tin và các phương tiện khác để thực hiện phân tách an toàn:

Joe-E là một tập hợp con của ngôn ngữ lập trình Java được thiết kế để hỗ trợ lập trình an toàn theo nguyên tắc khả năng của đối tượng. Joe-E nhằm tạo điều kiện cho việc xây dựng các hệ thống an toàn, cũng như tạo điều kiện đánh giá bảo mật cho các hệ thống được xây dựng trong Joe-E.

Bài viết được liên kết từ trang đó đưa ra một ví dụ về cách privatethực thi giúp phân tách an toàn có thể.

Cung cấp đóng gói an toàn.

Hình 1. Một cơ sở ghi nhật ký chỉ phụ lục.

public final class Log {
  private final StringBuilder content;
  public Log() {
    content = new StringBuilder();
  }
  public void write(String s) {
    content.append(s);
  }
}

Xem xét hình 1, minh họa cách người ta có thể xây dựng một cơ sở nhật ký chỉ nối thêm. Với điều kiện phần còn lại của chương trình được viết bằng Joe-E, một người đánh giá mã có thể hiểu rằng các mục nhật ký chỉ có thể được thêm vào và không thể sửa đổi hoặc xóa. Đánh giá này là thực tế vì nó chỉ yêu cầu kiểm tra Log lớp và không yêu cầu xem lại bất kỳ mã nào khác. Do đó, việc xác minh tài sản này chỉ yêu cầu lý luận cục bộ về mã đăng nhập.


1

Ẩn thông tin là một trong những mối quan tâm chính của thiết kế phần mềm tốt. Kiểm tra bất kỳ giấy tờ nào của Dave Parnas từ cuối thập niên 70. Về cơ bản, nếu bạn không thể đảm bảo rằng trạng thái bên trong của mô-đun là nhất quán, bạn không thể đảm bảo bất cứ điều gì về hành vi của nó. Và cách duy nhất bạn có thể đảm bảo trạng thái bên trong của nó là giữ riêng tư và chỉ cho phép thay đổi nó bằng phương tiện bạn cung cấp.


1

Bằng cách làm cho việc bảo vệ trở thành một phần của ngôn ngữ, bạn có được một thứ: sự đảm bảo hợp lý.

Nếu tôi đặt một biến riêng tư, tôi có sự đảm bảo hợp lý rằng nó sẽ chỉ được chạm bằng mã trong lớp đó hoặc bạn bè được tuyên bố rõ ràng của lớp đó. Phạm vi của mã có thể chạm hợp lý vào giá trị đó được giới hạn và được xác định rõ ràng.

Bây giờ có cách nào để có được xung quanh bảo vệ cú pháp? Chắc chắn rồi; hầu hết các ngôn ngữ có chúng. Trong C ++, bạn luôn có thể chuyển lớp sang một số loại khác và chọc vào các bit của nó. Trong Java và C #, bạn có thể phản ánh chính mình vào nó. Và kể từ đó trở đi.

Tuy nhiên, làm điều này là khó khăn . Rõ ràng là bạn đang làm điều gì đó bạn không nên làm. Bạn không thể làm điều đó một cách tình cờ (bên ngoài viết hoang dã trong C ++). Bạn phải sẵn sàng nghĩ, "Tôi sẽ chạm vào thứ gì đó mà tôi đã nói không với trình biên dịch của tôi." Bạn phải sẵn sàng làm một cái gì đó không hợp lý .

Nếu không có sự bảo vệ cú pháp, một lập trình viên có thể vô tình làm hỏng mọi thứ. Bạn phải dạy cho người dùng một quy ước và họ phải tuân theo quy ước đó mỗi lần . Nếu họ không, thế giới trở nên rất không an toàn.

Không có sự bảo vệ cú pháp, trách nhiệm thuộc về những người sai: nhiều người sử dụng lớp học. Họ phải tuân theo quy ước hoặc tính xấu không xác định sẽ xảy ra. Cào rằng: xấu không xác định có thể xảy ra.

Không có gì tệ hơn API, nếu bạn làm sai, mọi thứ có thể vẫn hoạt động. Điều đó cung cấp sự trấn an sai lầm cho người dùng rằng họ đã làm đúng, mọi thứ đều ổn, v.v.

Và những gì của người dùng mới với ngôn ngữ đó? Họ không chỉ phải học và làm theo cú pháp thực tế (được thực thi bởi trình biên dịch), giờ đây họ phải tuân theo quy ước này (được thực thi bởi các đồng nghiệp của họ một cách tốt nhất). Và nếu họ không, thì không có gì xấu có thể xảy ra. Điều gì xảy ra nếu một lập trình viên không hiểu tại sao quy ước tồn tại? Điều gì xảy ra khi anh ấy nói "vặn nó" và chỉ chọc vào những người bạn của bạn? Và anh ấy nghĩ gì nếu mọi thứ tiếp tục hoạt động?

Ông nghĩ rằng quy ước là ngu ngốc. Và anh sẽ không theo dõi nó nữa. Và anh ấy sẽ nói với tất cả bạn bè của mình không làm phiền quá.

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.