Tại sao chúng ta cần các biến riêng tư?


210

Tại sao chúng ta cần các biến riêng tư trong các lớp?

Mỗi cuốn sách về lập trình tôi đã đọc đều nói đây là một biến riêng tư, đây là cách bạn định nghĩa nó nhưng dừng lại ở đó.

Từ ngữ của những lời giải thích này dường như luôn luôn đối với tôi như chúng ta thực sự có một cuộc khủng hoảng niềm tin trong nghề nghiệp của chúng ta. Những lời giải thích luôn luôn nghe giống như các lập trình viên khác đang làm rối tung mã của chúng tôi. Tuy nhiên, có nhiều ngôn ngữ lập trình không có biến riêng.

  1. Làm gì biến tư nhân giúp ngăn chặn?

  2. Làm thế nào để bạn quyết định nếu một tài sản cụ thể nên riêng tư hay không? Nếu theo mặc định, mọi trường NÊN ở chế độ riêng tư thì tại sao lại có các thành viên dữ liệu công khai trong một lớp?

  3. Trong những trường hợp nào một biến nên được công khai?


9
Câu hỏi của bạn nghe có vẻ không liên quan đến ngôn ngữ, nhưng bạn đã gắn thẻ nó là java và c ++. Nếu bạn quan tâm đến quan điểm từ bên ngoài hai ngôn ngữ đó, có lẽ bạn nên nói như vậy.
James

8
Đối với bản ghi, tạo một phương thức riêng không làm tăng "bảo mật". Các phần khác trong ứng dụng của bạn vẫn có thể truy cập các thành viên tư nhân bằng cách sử dụng sự phản chiếu.
kba

3
Việc sử dụng và sự khác biệt của các biến riêng tư và công khai, và tại sao chúng ta có chúng, chỉ có thể được hiểu trong bối cảnh của các nguyên tắc hướng đối tượng chung. Nó không thể được hiểu riêng. Bạn có thể cần phải nhìn nó từ quan điểm đó, mọi thứ có thể trở nên rõ ràng hơn.
Nazar Merza


3
Nếu bạn bắt gặp bất cứ ai sử dụng dây an toàn trong xe của họ, bạn nên mang theo bằng lái xe của họ, vì rõ ràng họ không tin tưởng vào khả năng lái xe của mình.
gnasher729

Câu trả lời:


307

Đó không phải là vấn đề đáng tin cậy, mà là quản lý sự phức tạp.

Một thành viên công cộng có thể được truy cập từ bên ngoài lớp học, điều này cho những cân nhắc thực tế có nghĩa là "có khả năng ở bất cứ đâu". Nếu có sự cố xảy ra với một lĩnh vực công cộng, thủ phạm có thể ở bất cứ đâu, và vì vậy để theo dõi lỗi, bạn có thể phải xem xét khá nhiều mã.

Ngược lại, một thành viên riêng chỉ có thể được truy cập từ bên trong cùng một lớp, vì vậy nếu có vấn đề xảy ra với điều đó, thường chỉ có một tệp nguồn để xem xét. Nếu bạn có một triệu dòng mã trong dự án của mình, nhưng các lớp của bạn được giữ ở mức nhỏ, điều này có thể làm giảm nỗ lực theo dõi lỗi của bạn xuống 1000 lần.

Một lợi thế khác có liên quan đến khái niệm 'khớp nối'. Một thành viên công cộng mcủa một lớp Ađược sử dụng bởi lớp khác Bgiới thiệu một phụ thuộc: nếu bạn thay đổi mtrong A, bạn cũng phải kiểm tra tập quán của mtrong B. Tệ hơn nữa, không có gì trong lớp Acho bạn biết nơi mđang được sử dụng, vì vậy một lần nữa bạn phải tìm kiếm trong toàn bộ cơ sở mã; nếu đó là thư viện bạn đang viết, bạn thậm chí phải đảm bảo mã bên ngoàidự án của bạn không bị phá vỡ vì sự thay đổi của bạn. Trong thực tế, các thư viện có xu hướng gắn bó với chữ ký phương thức ban đầu của họ càng lâu càng tốt, bất kể đau đớn, và sau đó giới thiệu một khối các thay đổi phá vỡ với một bản cập nhật phiên bản chính. Ngược lại, với các thành viên tư nhân, bạn có thể loại trừ các phụ thuộc ngay lập tức - chúng không thể được truy cập từ bên ngoài, vì vậy tất cả các phụ thuộc được chứa trong lớp.

Trong bối cảnh này, "các lập trình viên khác" bao gồm cả bản thân trong tương lai và quá khứ của bạn. Bây giờ bạn có thể biết rằng bạn không nên làm điều này X với biến Y của mình, nhưng bạn chắc chắn đã quên mất ba tháng khi một khách hàng khẩn cấp cần bạn thực hiện một số tính năng và bạn tự hỏi tại sao làm X bị hỏng Y theo những cách tối nghĩa.

Vì vậy, khi nào bạn nên đặt mọi thứ ở chế độ riêng tư: Tôi muốn nói mọi thứ ở chế độ riêng tư theo mặc định, và sau đó chỉ hiển thị những phần hoàn toàn phải công khai. Bạn càng có thể làm cho riêng tư, tốt hơn.


24
+1 để giải quyết vấn đề cốt lõi, mặc dù cá nhân tôi thấy mã của mình dễ bảo trì hơn khi tôi dừng việc chuyển đổi chỉ để đảm bảo rằng một biến được sử dụng thường xuyên ở chế độ riêng tư. Chắc chắn một lỗi có thể xuất hiện trong tương lai, nhưng đó là 60 dòng mã ít hơn để sàng lọc và ít biến và chức năng để khởi động hơn;)
Jeffrey Sweeney

48
Chén thánh của điện toán hiện đại đang giữ sự phức tạp.

1
Tôi luôn muốn nói rằng một phần của việc gỡ lỗi không chỉ là lái trực tiếp đến "điều gì đã xảy ra", mà là thực hiện quá trình vòng xoay để tìm ra "điều gì không sai", và tập trung điều tra vào những gì còn lại. Nếu tôi có thể nhờ trình biên dịch hỗ trợ tôi với các công cụ như biến riêng để xác định "điều gì có thể xảy ra", điều đó giúp tôi tiết kiệm được rất nhiều thời gian. Chỉ trong những trường hợp hiếm hoi mà tôi chứng minh rằng những gì đã xảy ra không thể xảy ra, tôi mới phải đi sâu vào những giả định đáng sợ hơn, như có lẽ một bộ đệm tràn ngập dữ liệu riêng tư của tôi.
Cort Ammon

2
Chính xác đó. Khi mọi người nghe thấy "ẩn thông tin", họ nghĩ rằng họ nên sử dụng các biến riêng tư cho những thứ như khóa mã hóa, thông tin cơ sở dữ liệu, v.v.
Wayne Werner

2
@AdamZerner xem "Nói, đừng hỏi" ( martinfowler.com/bliki/TellDontAsk.html ) về việc có getters / setters ở vị trí đầu tiên - nhưng nếu không thì có, vì bạn nên tự do thay đổi biểu diễn bên trong của giá trị bất cứ lúc nào Bạn ước. Như mọi khi, vẫn có trường hợp ngoại lệ ... (ví dụ: DTOs)
doubleYou

111

Các biến riêng tư giúp ngăn mọi người phụ thuộc vào một số phần nhất định trong mã của bạn. Ví dụ, giả sử bạn muốn thực hiện một số cấu trúc dữ liệu. Bạn muốn người dùng cấu trúc dữ liệu của bạn không quan tâm đến cách bạn triển khai nó, mà chỉ sử dụng triển khai thông qua giao diện được xác định rõ. Lý do là nếu không có ai phụ thuộc vào việc thực hiện của bạn, bạn có thể thay đổi nó bất cứ khi nào bạn muốn. Ví dụ: bạn có thể thay đổi triển khai back-end để cải thiện hiệu suất. Bất kỳ nhà phát triển nào khác phụ thuộc vào việc triển khai của bạn sẽ bị hỏng, trong khi người dùng giao diện sẽ ổn. Có sự linh hoạt để thay đổi việc triển khai mà không ảnh hưởng đến người dùng lớp là một lợi ích rất lớn mà việc sử dụng các biến riêng tư (và rộng hơn là đóng gói ) mang lại cho bạn.

Ngoài ra, nó không thực sự là một "cuộc khủng hoảng niềm tin". Nếu bạn đặt một phần dữ liệu công khai, bạn không thể đảm bảo rằng không có ai phụ thuộc vào dữ liệu đó. Nó thường rất thuận tiện để phụ thuộc vào một số biến cụ thể thực hiện, thay vì đi qua giao diện công cộng, đặc biệt là trong thời hạn cuối cùng. Hơn nữa, các nhà phát triển sẽ không luôn nhận ra rằng họ phụ thuộc vào thứ gì đó có thể thay đổi.

Vì vậy, loại câu trả lời câu hỏi khác của bạn, tôi hy vọng. Tất cả các chi tiết triển khai của bạn phải ở chế độ riêng tư và phần công khai phải là một giao diện nhỏ, ngắn gọn, được xác định rõ ràng để sử dụng lớp của bạn.


24
IMO cũng là về giao tiếp giữa tác giả lib và người tiêu dùng lib. Giao diện công cộng giống như "sử dụng cái này" và các tư nhân giống như "không sử dụng cái đó" ngoại trừ trong Java, bạn thực sự không thể sử dụng nó một cách tình cờ nếu đặt nó ở chế độ riêng tư để an toàn và rõ ràng hơn theo cách đó.
chakrit

5
@chakrit và những người khác: Lưu ý rằng những người thực sự sử dụng các lĩnh vực và phương thức riêng tư của bạn có thể làm như vậy (thông qua sự phản ánh). privateít hơn "không chủ yếu dành cho bảo mật", nó không cung cấp "bảo mật" nào. Đó chỉ là một gợi ý mạnh mẽ (được gửi qua trình biên dịch) để không truy cập nó.

@delnan lưu ý cụm từ "tình cờ" vâng có lẽ tôi nên nói rõ hơn về điều đó.
chakrit

@chakrit Vâng, tôi không có ý ám chỉ bạn đã sai. Tôi chỉ muốn phát huy điểm đó.

1
@delnan: Các lĩnh vực tư nhân, trong một số ngôn ngữ, cung cấp các hình thức bảo mật nhất định. Ví dụ: xem câu trả lời của Eric Lippert về các cấp truy cập và công cụ sửa đổi (riêng tư, niêm phong, v.v.) có phục vụ mục đích bảo mật trong C # không? .
Brian

25

Từ khóa ở đây là Encapsulation . Trong OOP, bạn muốn sử dụng các biến riêng tư để thực thi đóng gói đúng đối tượng / lớp của mình.

Trong khi các lập trình viên khác không ra ngoài để có được bạn, họ sẽ tương tác với mã của bạn. Nếu bạn không đặt một biến riêng tư, họ cũng có thể tham chiếu nó trong mã riêng của họ mà không muốn làm hại gì cả. Tuy nhiên, nếu bạn cần quay lại lớp học và thay đổi thứ gì đó, bạn không còn biết ai sử dụng biến nào và ở đâu. Mục tiêu của việc đóng gói là làm cho giao diện bên ngoài của lớp rõ ràng để bạn chỉ biết các phương thức (thông thường) này có thể được sử dụng bởi những người khác.

  1. Do đó, các biến riêng đảm bảo rằng biến tương ứng chỉ còn lại trong lớp định nghĩa. Nếu bạn cần thay đổi nó, thay đổi là cục bộ của chính lớp đó.

  2. Trong các đường truyền thống như C ++ hoặc Java, bạn thường đặt mọi thứ ở chế độ riêng tư và chỉ có thể truy cập bằng các getters và setters tương ứng. Không cần phải quyết định nhiều thực sự.

  3. Đôi khi, f.ex. trong cấu trúc C ++, bạn chỉ cần một lớp như một cách để nhóm nhiều thứ lại với nhau. Ví dụ, hãy xem xét một lớp Vector chỉ có xmột ythuộc tính. Trong những trường hợp đó, bạn có thể cho phép truy cập trực tiếp vào các thuộc tính này bằng cách khai báo chúng công khai. Cụ thể, bạn không cần quan tâm nếu một số mã bên ngoài sửa đổi một đối tượng của lớp bằng cách viết trực tiếp các giá trị mới vào xhoặc y.

Là một điểm bổ sung, lưu ý rằng vấn đề này được coi là hơi khác nhau trong các ngôn ngữ khác. Ví dụ, các ngôn ngữ bắt nguồn mạnh mẽ trong lập trình chức năng nhấn mạnh đến tính bất biến của dữ liệu, tức là các giá trị dữ liệu hoàn toàn không thể thay đổi. Trong những trường hợp như vậy, bạn không cần phải quan tâm những gì lập trình viên khác (hay đúng hơn là mã của họ) làm gì với dữ liệu của bạn. Họ chỉ đơn giản là không thể làm bất cứ điều gì có thể ảnh hưởng đến mã của bạn, bởi vì tất cả dữ liệu là bất biến.

Do đó, trong các ngôn ngữ đó, bạn có được một thứ gọi là nguyên tắc truy cập thống nhất , trong đó người ta cố tình không phân biệt các phương thức như getters và setters, nhưng cung cấp quyền truy cập trực tiếp vào biến / hàm. Vì vậy, bạn có thể nói rằng các khai báo riêng tư khá phổ biến hơn trong các kịch bản hướng đối tượng.

Điều này cũng cho thấy việc mở rộng kiến ​​thức của bạn để bao gồm các lĩnh vực khác khiến bạn xem các khái niệm hiện có theo một cách hoàn toàn mới.


1
+1 "Trong khi các lập trình viên khác không sẵn sàng nhận bạn, họ sẽ tương tác với mã của bạn." Các lập trình viên sử dụng mã của bạn không phải là xấu. Bằng cách sử dụng các biến riêng tư, bạn chắc chắn rằng chúng (và bạn, trong tương lai) sẽ không bị tổn thương khi sử dụng mã. Nó cũng giúp bạn thiết kế một API tốt hơn và ghi lại nó.
szalski

7

Các biến riêng tư đảm bảo rằng các tham chiếu sau này nằm ngoài phạm vi của một đối tượng hoặc hàm sẽ vô tình ảnh hưởng đến các biến khác. Đối với các dự án lập trình lớn, điều này có thể giúp tránh được rất nhiều vấn đề thú vị (thường không được trình biên dịch bắt gặp).

Ví dụ: các ngôn ngữ nguyên mẫu như Javascript có thể cho phép người dùng dán vào các biến khi họ thấy phù hợp:

function SomeObject() {
    this.a = 2; //Public (well, actually protected) variable

    this.showA = function() {
        alert(this.a);
    }
}

//Some lines down...

var obj = new SomeObject();
obj.a = 3;
obj.showA(); //Will alert 3. Uh oh!

Nếu biến đó là riêng tư, nó sẽ không thể thay đổi từ bên ngoài:

function SomeObject() {
    var a = 2; //Private variable

    this.showA = function() {
        alert(a);
    }
}

//Some lines down...

var obj = new SomeObject();
obj.a = 3;
obj.showA(); //Will alert 2

Điều đó nói rằng, không phải tất cả các biến cần phải riêng tư và việc sử dụng quá mức các biến riêng tư thực sự có thể làm cho mã trở nên cồng kềnh hơn. Các trường tầm thường không ảnh hưởng nghiêm trọng đến một đối tượng không cần get()set()phương pháp.

Các biến riêng tư cũng giúp mở rộng mã dễ dàng hơn trong tương lai, mà không cần dựa vào tài liệu nặng như những gì nhiều biến công khai làm. Có ít mối đe dọa về việc vô tình phá vỡ chức năng nếu ít biến số có thể bị ghi đè.

Các biến công khai nên được sử dụng khi một đối tượng / hàm sẽ truy cập vào các đối tượng / hàm khác, vì theo quy tắc, các biến riêng không thể làm điều này. Bạn có thể có bất cứ nơi nào từ một vài đến một vài biến công khai và việc sử dụng chúng không phải là một điều xấu nếu chúng được sử dụng đúng cách.


Không rõ tại sao cảnh báo 3 sẽ là "uh oh!" trong trường hợp này. Có lẽ đó là những gì lập trình viên muốn xảy ra.
Immibis

@immibis Có lẽ, và có lẽ tôi nên nói rõ hơn trong câu trả lời. Nhưng quyền truy cập vào các thuộc tính có thể mở ra cơ hội vi phạm một số nguyên tắc OOP như nguyên tắc đóng mở hoặc LoD, vì bạn có khả năng tiết lộ chi tiết triển khai. Tất nhiên, theo quan điểm của bạn, nó phụ thuộc vào chính xác tài sản là gì. Hầu hết các ngôn ngữ sử dụng các đối tượng làm tài liệu tham khảo và khi các tham chiếu của các đối tượng được truyền qua và các nhà phát triển đồng nghiệp lặng lẽ thay đổi các thuộc tính, nó có thể thực sự khó gỡ lỗi. IMO thật dễ dàng hơn và có thể mở rộng hơn để làm việc với các phương thức nếu bạn có một thể hiện của lớp.
Jeffrey Sweeney

7

Có một số câu trả lời hay trích dẫn lợi ích cho những người duy trì và người dùng trong tương lai. Tuy nhiên, cũng có những lợi ích cho thiết kế ban đầu. Nó cung cấp một điểm phân định rõ ràng giữa khi mục tiêu của bạn là làm cho mọi thứ dễ dàng hơn với chính mình và khi mục tiêu của bạn là làm cho mọi thứ dễ dàng hơn với người dùng lớp. Khi bạn chọn giữa công khai và riêng tư, nó báo hiệu cho bộ não của bạn để chuyển sang chế độ này hay chế độ khác và bạn kết thúc với một API tốt hơn.

Không phải ngôn ngữ mà không có sự riêng tư làm cho thiết kế như vậy là không thể, chỉ là ít có khả năng. Thay vì được nhắc viết một cái gì đó như foo.getBar(), mọi người có xu hướng nghĩ rằng một getter là không cần thiết, bởi vì nó dễ viết hơn foo.bar, nhưng sau đó quyết định đó không được xem xét lại khi bạn kết thúc với những điều quái dị dài hơn obj.container[baz].foo.bar. Các lập trình viên chưa bao giờ làm việc trong một ngôn ngữ chặt chẽ hơn thậm chí có thể không thấy điều gì sai với điều đó.

Đó là lý do tại sao trong các ngôn ngữ không có quyền riêng tư, mọi người sẽ thường áp dụng các tiêu chuẩn đặt tên mô phỏng nó, chẳng hạn như đặt trước một dấu gạch dưới cho tất cả các thành viên "riêng tư". Đó là một tín hiệu hữu ích ngay cả khi ngôn ngữ không thi hành nó.


3

Tất cả các biến phải ở chế độ riêng tư trừ khi chúng thực sự cần phải công khai (điều này gần như không bao giờ, bạn nên sử dụng các thuộc tính / getters và setters).

Các biến chủ yếu cung cấp trạng thái của đối tượng và các biến riêng tư ngăn người khác đi vào và thay đổi trạng thái của đối tượng.


5
Một cách nhìn quá hẹp về vấn đề này. Có những trường hợp truy cập trường công cộng được bảo hành, thậm chí được ưa thích
Roland Tepp

Một người chắc chắn có thể thiết kế các đối tượng mà trạng thái không thể thay đổi (đối tượng bất biến), nhưng đây không phải là quy tắc chung được áp dụng cho tất cả các lớp. Theo kinh nghiệm của tôi, hầu hết các đối tượng làm cho người khác rất dễ dàng thay đổi trạng thái của đối tượng.
David K

1
@DavidK Có lẽ điều mà bbb muốn nói là "các biến riêng tư ngăn người khác đi vào và thay đổi một cách bừa bãi trạng thái của đối tượng." Các phương thức công khai có thể thay đổi trạng thái riêng tư của đối tượng, nhưng chỉ theo những cách cần thiết và nhất quán cho hoạt động của đối tượng.
Max Nanasy

@Max Nanasy: Có, người ta có thể kiểm soát cách thay đổi trạng thái khi chỉ cung cấp các phương thức trong giao diện công cộng. Ví dụ, có thể có một mối quan hệ giữa các thành viên phải được thỏa mãn để đối tượng "tự đồng nhất" và bạn có thể cho phép trạng thái tự đồng nhất chỉ được thay đổi sang trạng thái tự đồng nhất khác. Nếu tất cả các biến đó là công khai thì bạn không thể thực thi điều này. Nhưng cũng có thể có những đối tượng mà bạn không muốn cho phép bất kỳ thay đổi nào , vì vậy điều quan trọng là phải làm rõ những điều chúng ta đang nói đến.
David K

2
  • Làm gì biến tư nhân giúp ngăn chặn?

Đó là về việc làm rõ các thuộc tính và phương thức nào là giao diện và những thuộc tính nào thực sự là chức năng cốt lõi. Các phương thức / thuộc tính công khai là về mã khác thường là mã của nhà phát triển khác sử dụng các đối tượng của bạn. Tôi chưa bao giờ làm việc với các nhóm trên 100 người trong cùng một dự án, nhưng theo kinh nghiệm của tôi về các nhóm 3-5 có tới 20 nhà phát triển khác sử dụng những thứ mà tôi đã viết, những mối quan tâm khác có vẻ ngớ ngẩn.

Lưu ý: Tôi chủ yếu là một nhà phát triển JavaScript. Tôi thường không lo lắng về việc các nhà phát triển khác xem mã của tôi và tôi hoạt động trong nhận thức đầy đủ rằng họ có thể chỉ cần xác định lại công cụ của tôi bất cứ lúc nào. Tôi cho rằng họ đủ năng lực để biết họ đang làm gì trước tiên. Nếu không, tôi không thể làm việc với một nhóm không sử dụng kiểm soát nguồn.

  • Làm thế nào để bạn quyết định nếu một tập hợp các thuộc tính cụ thể nên riêng tư hay không? Nếu theo mặc định, mọi trường NÊN ở chế độ riêng tư thì tại sao lại có các thành viên dữ liệu công khai trong một lớp?

Tôi đã từng nghĩ thật ngớ ngẩn khi đặt getters và setters vào các thuộc tính riêng tư khi bạn không thực sự thực hiện bất kỳ loại xác nhận hoặc xử lý đặc biệt nào khác của tài sản sẽ được đặt. Tôi vẫn không nghĩ rằng nó luôn luôn cần thiết, nhưng khi bạn xử lý với quy mô lớn, độ phức tạp cao, nhưng quan trọng nhất là rất nhiều nhà phát triển khác dựa vào các đối tượng của bạn hành xử một cách nhất quán, có thể hữu ích để làm mọi việc trong một cách nhất quán và thống nhất. Khi mọi người thấy các phương thức được gọi getThatsetThat, họ biết chính xác ý định là gì và họ có thể viết các đối tượng của mình để tương tác với bạn với mong muốn họ sẽ luôn nhận được cà chua chứ không phải cà chua. Nếu họ không, họ biết đối tượng của bạn đang cung cấp cho họ thứ gì đó có lẽ không nên, điều đó có nghĩa là nó cho phép một số đối tượng khác làm điều gì đó với dữ liệu đó có lẽ không nên. Bạn có thể kiểm soát điều đó khi có nhu cầu.

Ưu điểm lớn khác là dễ dàng hơn để các đối tượng của bạn xử lý hoặc giải thích các giá trị khác với getter hoặc setter dựa trên một số loại bối cảnh trạng thái khác nhau. Vì họ đang truy cập công cụ của bạn bằng một phương thức, nên việc thay đổi cách đối tượng của bạn hoạt động sau này dễ dàng hơn nhiều mà không vi phạm mã khác phụ thuộc vào phương thức đó. Nguyên tắc quan trọng là chỉ có đối tượng của bạn thực sự thay đổi dữ liệu mà bạn đã chịu trách nhiệm. Điều này đặc biệt quan trọng để giữ cho mã của bạn được ghép lỏng lẻo, đó là một chiến thắng lớn về tính di động và dễ sửa đổi.

  • Trong những trường hợp nào một biến nên được công khai?

Với các hàm hạng nhất (bạn có thể truyền các hàm làm đối số), tôi không thể nghĩ ra nhiều lý do tuyệt vời khác ngoài việc không cần thiết phải làm như vậy đối với các dự án quy mô nhỏ hơn. Nếu không có nó, tôi cho rằng bạn có thể có các thành viên đang được xử lý thường xuyên và thường xuyên bởi các đối tượng khác ở mức độ có vẻ hơi khó chịu khi liên tục gọi get và đặt trên một đối tượng không thực sự chạm vào dữ liệu đó, nhưng đó là chính nó sẽ khiến tôi tự hỏi tại sao đối tượng chịu trách nhiệm về dữ liệu đó. Nói chung, tôi có xu hướng muốn kiểm tra lại kiến ​​trúc của mình để tìm ra sai sót trước khi quyết định một thuộc tính dữ liệu công cộng là cần thiết. IMO, không có gì sai với ngôn ngữ cho phép bạn làm điều gì đó thường là một ý tưởng tồi. Một số ngôn ngữ không đồng ý.


IMHO, không có gì đặc biệt sai khi có một lớp công khai với mục đích duy nhất là phơi bày các biến công khai. Trên thực tế, các JVM có một số class built-in cho mục đích đó: int[], double[], Object[], vv Một nên, tuy nhiên, phải rất cẩn thận về cách người ta thấy nhiều trường hợp của một lớp học như vậy. Ý nghĩa của việc void CopyLocationTo(Point p);[chấp nhận một Pointtừ người gọi] rõ ràng hơn Point location(), vì không rõ ảnh hưởng của Point pt = foo.location(); pt.x ++;nó đến vị trí của foo.
supercat

2

Xem bài đăng này của tôi cho một câu hỏi liên quan về SO .

Tóm lại, đó là phạm vi biến cho phép bạn hiển thị cho người tiêu dùng mã của bạn những gì họ nên và không nên gây rối. Một biến riêng có thể chứa dữ liệu đã được "hiệu đính" bằng cách sử dụng bộ thiết lập thuộc tính hoặc phương thức xử lý để đảm bảo toàn bộ đối tượng ở trạng thái nhất quán. Thay đổi trực tiếp giá trị của biến riêng có thể khiến đối tượng trở nên không nhất quán. Bằng cách đặt nó ở chế độ riêng tư, ai đó phải làm việc rất chăm chỉ và có quyền truy cập thời gian thực sự cao, để thay đổi nó.

Do đó, phạm vi thành viên thích hợp là chìa khóa trong mã tự ghi.


2

Tôi sẽ nói về giá trị của sự đóng gói từ một góc nhìn khác bằng một ví dụ thực tế. Cách đây khá lâu (đầu thập niên 80), một trò chơi đã được viết cho Máy tính màu Radio Shack có tên Dungeons of Daggorath. Vài năm trước, Richard Hunerlach đã chuyển nó từ một nhà lắp ráp giấy liệt kê sang C / C ++ ( http://mspencer.net/daggorath/dodpcp.html ). Một thời gian sau, tôi đã nhận được mã và bắt đầu tính lại nó để đóng gói tốt hơn, ( http://dbp-consulting.com/ ware / ).

Mã đã được đưa vào các đối tượng khác nhau để xử lý lập lịch, video, đầu vào của người dùng, tạo ngục tối, quái vật, v.v. nhưng bạn chắc chắn có thể nói rằng nó được chuyển từ trình biên dịch. Nhiệm vụ lớn nhất của tôi là tăng đóng gói. Điều đó có nghĩa là tôi có được các phần khác nhau của mã để loại bỏ mũi của họ ra khỏi doanh nghiệp của nhau. Ví dụ, có nhiều nơi sẽ thay đổi các biến video trực tiếp để có hiệu quả. Một số đã làm điều đó với việc kiểm tra lỗi, một số thì không, một số đã có những ý tưởng khác biệt tinh tế về việc thay đổi điều đó có nghĩa gì. Có nhiều sự trùng lặp của mã. Thay vào đó, phần video cần có giao diện để thực hiện các tác vụ mong muốn và mã để thực hiện có thể nằm ở một nơi, một ý tưởng, một nơi để gỡ lỗi. Đó là loại điều tràn lan.

Khi mã bắt đầu bị trêu chọc, các lỗi thậm chí không được chẩn đoán đã biến mất. Mã trở nên chất lượng cao hơn. Mỗi nhiệm vụ trở thành trách nhiệm của một nơi trong mã và chỉ có một nơi duy nhất để thực hiện đúng. (Tôi vẫn tìm thấy nhiều hơn mỗi khi tôi thực hiện một lần nữa thông qua mã.)

Tất cả mọi thứ có thể nhìn thấy về mã của bạn là giao diện của bạn. Cho dù bạn có ý nghĩa hay không. Nếu bạn giới hạn khả năng hiển thị càng nhiều càng tốt thì bạn sẽ không muốn đặt mã sai vị trí sau này. Nó làm cho rõ ràng phần nào của mã chịu trách nhiệm cho dữ liệu nào. Nó làm cho thiết kế tốt hơn, sạch hơn, đơn giản hơn, thanh lịch hơn.

Nếu bạn tạo một đối tượng chịu trách nhiệm về dữ liệu của chính nó và cung cấp giao diện cho mọi người khác cần điều gì đó xảy ra, mã của bạn sẽ đơn giản hơn rất nhiều. Nó chỉ rơi ra. Bạn có những thói quen nhỏ đơn giản chỉ làm một việc. Ít phức tạp hơn == ít lỗi hơn.


Sự cân bằng cứng giữa "quyền tự do của người dùng lib của chúng tôi" và "ít rắc rối hơn với chúng tôi". Tôi bắt đầu tin rằng việc đóng gói sẽ tốt hơn theo nghĩa là tôi sẽ ngăn chặn các lỗi trên mã hóa của chính mình và tôi cũng sẽ giúp người dùng cuối cũng ngăn chặn các lỗi. Nhưng sự thiếu tự do có thể chỉ đơn giản là đưa chúng đi, tìm kiếm các giải pháp thay thế ... Có thể mã hóa chưa được sử dụng nhưng các chức năng dự kiến ​​thông qua các phương pháp công cộng có thể giúp cân bằng điều đó, nhưng đòi hỏi nhiều thời gian hơn.
Sức mạnh Bảo Bình

Quá tệ, phiên bản được cấu trúc lại không có trên github ....
jmoreno

2

Nó không liên quan gì đến sự tin tưởng hay sợ hãi trước các cuộc tấn công, nó chỉ liên quan đến Đóng gói - không ép buộc thông tin không cần thiết về người dùng của lớp.

Xem xét các hằng số riêng - chúng không nên chứa các giá trị bí mật (chúng nên được lưu trữ ở nơi khác), chúng không thể thay đổi, chúng không cần phải được chuyển vào lớp của bạn (nếu không chúng sẽ phải được công khai). Việc sử dụng duy nhất có thể cho chúng là các hằng số trong các lớp KHÁC. Nhưng nếu bạn làm điều đó, các lớp đó phụ thuộc vào lớp của bạn, để thực hiện công việc không liên quan đến lớp của bạn. Nếu bạn thay đổi hằng số, các lớp khác có thể bị hỏng. Điều đó thật tệ từ cả hai phía - với tư cách là nhà văn của lớp bạn, bạn muốn tự do thay đổi nhiều nhất có thể và không muốn lo lắng về những điều ngoài tầm kiểm soát của mình. Một người tiêu dùng của lớp của bạn muốn có thể phụ thuộc vào các chi tiết được tiết lộ của lớp của bạn, mà không phải lo lắng về việc bạn thay đổi nó và phá vỡ mã của họ.

Một người tiêu dùng của lớp bạn muốn biết mọi thứ cần thiết để TƯƠNG TÁC với lớp của bạn và không muốn biết bất cứ điều gì về lớp của bạn mà không thay đổi cách họ làm như vậy: đó là những chuyện vặt vãnh vô ích. Nếu bạn đã sử dụng một ngôn ngữ với sự phản chiếu, bạn có thường xuyên sử dụng nó để học không phải cách một số lớp làm điều gì đó hoặc nơi nó ném một ngoại lệ bất ngờ, mà chỉ đơn giản là tên của các trường và phương thức riêng tư? Tôi đang cá cược không bao giờ. Bởi vì bạn không có sử dụng cho kiến ​​thức đó.


1

Khái niệm OOP có sự kế thừa có một trong các tính năng của nó (Java hoặc C ++). Vì vậy, nếu chúng ta sẽ kế thừa (có nghĩa là chúng ta sẽ truy cập vào các biến của lớp kế thừa), có khả năng ảnh hưởng đến các biến đó. Vì vậy, chúng ta phải quyết định xem các biến có thể được thay đổi hay không.

Chỉ dành cho mục đích này, chúng tôi đang sử dụng công cụ sửa đổi truy cập trong OOP. Một trong những sửa đổi là riêng tư có nghĩa là nó chỉ có thể được truy cập bởi lớp đó. Bất kỳ lớp nào khác không thể ảnh hưởng đến các biến đó.

Như chúng ta biết, được bảo vệ có nghĩa là nó có thể được truy cập bởi lớp sẽ kế thừa.

Tại sao có một khái niệm sửa đổi trong OOP, là do những lý do này (bất kỳ lớp nào cũng có thể truy cập các biến lớp khác bằng khái niệm OOP). Nếu không có khái niệm sửa đổi, điều đó có nghĩa là nó sẽ gặp khó khăn trong khi kế thừa các lớp hoặc sử dụng các khái niệm OOP khác.


1

Như đã nói bởi những người khác, các biến riêng tư là tốt để tránh các lỗi sử dụng dẫn đến đối tượng rơi vào tình trạng không nhất quán và khó theo dõi các lỗi và các ngoại lệ không lường trước được.

Nhưng mặt khác, những gì hầu hết bị người khác bỏ qua là về các lĩnh vực được bảo vệ.

Một lớp con mở rộng sẽ có quyền truy cập đầy đủ vào các trường được bảo vệ, làm cho đối tượng trở nên mỏng manh như thể các trường đó là công khai, nhưng sự mong manh đó bị giới hạn ở lớp mở rộng tự nó (trừ khi nó tiếp xúc với các trường như vậy nhiều hơn).

Vì vậy, các trường công khai khó được coi là tốt và cho đến nay, lý do duy nhất để sử dụng chúng là cho các lớp được sử dụng làm tham số cấu hình (một lớp rất đơn giản có nhiều trường và không có logic, do đó lớp được truyền dưới dạng tham số một mình một số phương pháp).

Nhưng mặt khác, các trường riêng làm giảm tính linh hoạt của mã của bạn đối với người dùng khác.

Linh hoạt so với Rắc rối, ưu và nhược điểm:

Các đối tượng được khởi tạo bởi mã của bạn trong lớp vanilla với các trường được bảo vệ là an toàn và là trách nhiệm của riêng bạn.

Mặt khác, các đối tượng mở rộng lớp của bạn với các trường được bảo vệ, được người dùng mã của bạn khởi tạo, là trách nhiệm của họ, không phải của bạn.

Vì vậy, các trường / phương thức được bảo vệ không được ghi chép tốt hoặc nếu người dùng không thực sự hiểu cách sử dụng các trường và phương pháp đó, có khả năng gây rắc rối không cần thiết cho chính họ và cho bạn.

Mặt khác, làm cho hầu hết mọi thứ riêng tư sẽ làm giảm tính linh hoạt của người dùng và thậm chí có thể khiến họ tìm kiếm các giải pháp thay thế được duy trì, vì họ có thể không muốn tạo và duy trì một ngã ba chỉ để mọi thứ diễn ra theo cách của họ.

Vì vậy, một sự cân bằng tốt giữa tư nhân, được bảo vệ và công cộng là điều thực sự quan trọng.

Bây giờ, để quyết định giữa riêng tư và được bảo vệ là vấn đề thực sự.

Khi nào sử dụng được bảo vệ?

Bất cứ khi nào bạn hiểu một lĩnh vực có thể rất linh hoạt, nó nên được mã hóa là được bảo vệ. Tính linh hoạt đó là: từ trở thành null (trong đó null luôn được kiểm tra và được công nhận là trạng thái hợp lệ không ném ngoại lệ), đến các ràng buộc trước khi được sử dụng bởi lớp cũ của bạn. > = 0, <100, v.v. và tự động sửa cho các giá trị vượt / chảy, ném tối đa một thông báo cảnh báo.

Vì vậy, đối với trường được bảo vệ như vậy, bạn có thể tạo một getter và chỉ sử dụng nó (thay vì sử dụng biến trường), trong khi những người dùng khác không thể sử dụng nó, trong trường hợp họ muốn linh hoạt hơn với mã cụ thể của họ, trong ví dụ của tôi có thể như : nếu họ muốn các giá trị âm hoạt động tốt ở lớp mở rộng của chúng.


-1

imo @tdammers không đúng và thực sự sai lệch.

Các biến riêng tồn tại để ẩn chi tiết thực hiện. Lớp học của bạn Acó thể đang sử dụng một arrayđể lưu trữ điểm số. Ngày mai bạn có thể muốn sử dụng một treehoặc priority queuethay vào đó. Tất cả những người dùng trong lớp của bạn cần là một cách để nhập điểm và tên addScore()và một cách để tìm ra ai là top 10 getTop(n).

Họ không cần phải truy cập cấu trúc dữ liệu cơ bản. Âm thanh tuyệt vời? Vâng, có một số hãy cẩn thận.

Dù sao thì bạn cũng không nên lưu trữ nhiều trạng thái, hầu hết là không cần thiết. Trạng thái lưu trữ sẽ làm phức tạp mã của bạn ngay cả khi nó được "tách riêng" thành một lớp cụ thể. Hãy nghĩ về nó, biến riêng đó có thể đã thay đổi vì một đối tượng khác gọi là phương thức của lớp đó. Bạn vẫn cần phải tìm ra khi nào và ở đâu phương thức đó được gọi và phương thức công khai đó có thể được gọi ở bất cứ đâu về mặt lý thuyết.

Điều tốt nhất bạn có thể làm là giới hạn số lượng trạng thái mà chương trình của bạn chứa và sử dụng các hàm thuần túy khi có thể.


-1
  • Truy cập một biến có nghĩa là truy cập giá trị của nó khi chương trình chạy, làm cho một biến riêng tư bảo vệ giá trị của nó khi mã chạy.
  • Điểm của cái gọi là "ẩn dữ liệu" là giữ dữ liệu nội bộ ẩn khỏi các lớp khác sử dụng lớp. Các lớp khác chỉ nên truy cập hành vi bằng cách gọi các phương thức trên lớp, không phải bằng cách thay đổi giá trị của các biến trực tiếp.
  • Bằng cách biến biến thành thành viên dữ liệu riêng tư, bạn có thể dễ dàng đảm bảo rằng giá trị không bao giờ được sửa đổi hoặc thay đổi. Mặt khác, nếu biến là công khai, một lớp khác có thể sửa đổi hoặc thay đổi giá trị có thể khiến các phần khác của mã bị sập.

1
điều này dường như không cung cấp bất cứ điều gì đáng kể qua các điểm được thực hiện và giải thích trong 17 câu trả lời trước
gnat

-4

Trước tiên bạn nên hiểu khái niệm về lập trình hướng đối tượng. Nó có sự trừu tượng, đóng gói, vv

Trừu tượng - Bạn có được ý tưởng về logic mà không cần biết chi tiết gạch chân của việc thực hiện.

Đóng gói - Bạn không thể thấy việc thực hiện gạch chân của đối tượng. Chỉ có bạn có thể thấy giao diện công cộng của đối tượng.

Bây giờ trong triển khai cụ thể với một trong các chương trình hướng đối tượng như C #, Java, C ++, bạn có thể thực hiện các khái niệm này.

riêng tư - Việc thực hiện cần được ẩn khỏi thế giới bên ngoài. Vì vậy, bạn có thể thay đổi điều đó và người dùng của lớp của bạn không bị ảnh hưởng.

công khai - Đây là giao diện mà người ta có thể sử dụng đối tượng của bạn.


1
được bảo vệ - có thể truy cập trực tiếp bởi các lớp con (bộ mở rộng) (java).
Sức mạnh Bảo Bình
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.