2010-03-28 45 views
6

Tôi luôn tự hỏi về chủ đề của các thuộc tính public, protectedprivate. Bộ nhớ của tôi có thể dễ dàng nhớ lại thời gian khi tôi để hack mã của ai đó và việc có các biến lớp bị tấn công được khai báo là private luôn gây khó chịu.Quan điểm thực tế về riêng tư và công khai

Ngoài ra, đã có (nhiều lần) tôi đã tự viết một lớp và chưa bao giờ nhận ra bất kỳ lợi ích tiềm năng nào của việc tư nhân hóa tài sản. Tôi nên lưu ý ở đây rằng sử dụng các vars công cộng là không phải là trong thói quen của tôi: Tôi tuân theo các nguyên tắc của OOP bằng cách sử dụng getters và setters.

Vì vậy, toàn bộ điểm trong những hạn chế này là gì?

+0

Tôi hy vọng bạn không sử dụng getters và setters cho tất cả mọi thứ, nhưng chỉ khi thực sự cần thiết. Nói chung, họ vi phạm các nguyên tắc của OOP bằng cách phơi bày chi tiết thực hiện. –

Trả lời

7

Việc sử dụng riêngcông khai được gọi là Encapsulation. Nó là cái nhìn sâu sắc đơn giản rằng một gói phần mềm (lớp hoặc mô-đun) cần một bên trong và bên ngoài.

Bên ngoài (công khai) là hợp đồng của bạn với phần còn lại của thế giới. Bạn nên cố gắng giữ cho nó đơn giản, mạch lạc, rõ ràng, dễ hiểu và, rất quan trọng, ổn định.

Nếu bạn quan tâm đến thiết kế phần mềm tốt, quy tắc đơn giản là: tạo tất cả dữ liệu riêng tư và chỉ đặt phương thức công khai khi cần.

Nguyên tắc ẩn dữ liệu là tổng của tất cả các trường trong một lớp xác định các đối tượng trạng thái. Đối với một lớp học được viết tốt, mỗi đối tượng phải chịu trách nhiệm giữ trạng thái hợp lệ. Nếu một phần của tiểu bang là công khai, lớp học không bao giờ có thể đảm bảo như vậy.

Một ví dụ nhỏ, giả sử chúng ta có:

class MyDate 
{ 
    public int y, m, d; 
    public void AdvanceDays(int n) { ... } // complicated month/year overflow 
    // other utility methods 
}; 

Bạn không thể ngăn chặn người dùng của lớp để bỏ qua AdvanceDays() và chỉ cần làm:

date.d = date.d + 1; // next day 

Nhưng nếu bạn thực hiện y, m, d tư nhân và kiểm tra tất cả các phương pháp MyDate của bạn, bạn có thể đảm bảo rằng sẽ chỉ có ngày hợp lệ trong hệ thống.

+0

Vâng, đúng vậy. Nhưng nếu tôi mắc sai lầm khủng khiếp trong 'AdvanceDays' thì sao? Người dùng trong lớp của tôi sẽ không thể khắc phục nếu tất cả dữ liệu là riêng tư. –

+1

Bạn rõ ràng đang làm việc theo một hướng khác tại đây. Người dùng của bạn chỉ nên đá bạn (: –

+0

Nếu tôi đã có tất cả công khai, họ sẽ không cần phải) –

2

Không nên sử dụng từ khóa riêng tư để tư nhân hóa thuộc tính mà bạn muốn hiển thị, nhưng để bảo vệ mã nội bộ của lớp học của bạn. Tôi thấy chúng rất hữu ích vì chúng giúp bạn xác định các phần của mã của bạn phải được ẩn khỏi những phần mà mọi người có thể truy cập được.

+0

Tại sao bạn muốn ẩn mã của mình với bất kỳ ai? :) –

0

Một ví dụ mà tôi nghĩ đến là khi bạn cần thực hiện một số điều chỉnh hoặc kiểm tra trước khi thiết lập/nhận giá trị của một thành viên riêng tư. Vì vậy, bạn sẽ tạo một bộ công khai/getter với một số logic (kiểm tra nếu một cái gì đó là null hoặc bất kỳ tính toán khác) thay vì truy cập vào biến riêng tư trực tiếp và luôn luôn phải xử lý logic đó trong mã của bạn. Nó giúp với các hợp đồng mã và những gì được mong đợi.

Ví dụ khác là chức năng trợ giúp. Bạn có thể phá vỡ một số logic lớn hơn của bạn thành các hàm nhỏ hơn, nhưng điều đó không có nghĩa là bạn muốn mọi người thấy và sử dụng các hàm trợ giúp này, bạn chỉ muốn họ truy cập các hàm API chính của bạn.

Nói cách khác, bạn muốn ẩn một số nội bộ trong mã của bạn từ giao diện.

Xem một số video trên API, chẳng hạn như this Google talk.

+0

Nếu tôi phạm sai lầm trong những người định cư công cộng/getters? Điều gì sẽ xảy ra nếu người dùng trong lớp của tôi mở rộng theo cách thực sự * cần * các chức năng trợ giúp đó? –

4

Toàn bộ vấn đề là sử dụng privateprotected để tránh hiển thị chi tiết bên trong lớp học của bạn, để các lớp khác chỉ có quyền truy cập vào "giao diện" công khai do lớp của bạn cung cấp. Điều này có thể đáng giá nếu được thực hiện đúng cách.

Tôi đồng ý rằng private có thể là một nỗi đau thực sự, đặc biệt nếu bạn đang mở rộng các lớp học từ thư viện. Trong khi đó, tôi phải mở rộng các lớp khác nhau từ khung Piccolo.NET và nó đã làm mới mọi thứ tôi cần là protected thay vì private, vì vậy tôi có thể mở rộng mọi thứ tôi cần mà không phải sao chép mã và/hoặc sửa đổi thư viện . Một bài học cất cánh quan trọng từ đó là nếu bạn đang viết mã cho một thư viện hoặc thành phần "có thể tái sử dụng" khác, bạn thực sự nên suy nghĩ kỹ trước khi khai báo bất cứ điều gì private.

+0

Nếu một người dùng lớp có thể mở rộng nó và, có lẽ, viết một số bộ định tuyến/getters trực tiếp, thì tất cả các công cụ 'protected' này sẽ trở thành giống như' public'. Tại sao không tuyên bố mọi thứ 'công khai' ngay từ đầu? –

+0

Bởi vì sau đó sẽ không có đóng gói. Điểm bảo vệ là các phần tử đó chỉ có thể được truy cập bởi lớp này hoặc các phần tử của nó - những đối tượng này cần phải biết chi tiết bên trong, nhưng bất kỳ mã bên ngoài nào sử dụng chúng thì không. –

+0

Nhưng bất kỳ người dùng nào cũng có thể mở rộng lớp học của tôi và phá vỡ đóng gói theo bất kỳ cách nào anh ta muốn. Có * không * đóng gói hiệu quả với 'bảo vệ'. –

0

Có gần đây đã có sự sang trọng cực đoan của việc thiết kế và thực hiện một hệ thống đối tượng từ đầu, tôi đã thực hiện chính sách buộc tất cả các biến phải là (tương đương) protected. Mục tiêu của tôi là khuyến khích người dùng luôn coi các biến đó là một phần của quá trình triển khai chứ không phải là đặc điểm kỹ thuật. OTOH, tôi cũng để lại trong móc để cho phép mã để phá vỡ hạn chế này vì vẫn còn lý do để không làm theo nó (ví dụ, các công cụ serialization đối tượng không thể làm theo các quy tắc).

Lưu ý rằng các lớp học của tôi không cần thực thi bảo mật; ngôn ngữ có các cơ chế khác cho điều đó.

0

Theo quan điểm của tôi, lý do quan trọng nhất để sử dụng thành viên riêng là ẩn cài đặt, để nó có thể thay đổi trong tương lai mà không thay đổi con cháu.

0

Một số ngôn ngữ - Smalltalk, ví dụ - không có bộ sửa đổi hiển thị nào cả.

Trong trường hợp Smalltalk, tất cả các biến mẫu luôn là riêng tư và tất cả các phương pháp luôn công khai. Một nhà phát triển chỉ ra rằng một phương thức "riêng tư" - một thứ có thể thay đổi hoặc phương thức trợ giúp không có ý nghĩa gì nhiều - bằng cách đặt phương thức trong giao thức "riêng tư".

Người dùng của một lớp học có thể thấy rằng họ nên suy nghĩ kỹ về việc gửi thư được đánh dấu riêng tư cho lớp đó, nhưng vẫn có quyền tự do sử dụng phương pháp.

(Lưu ý:. "Tài sản" trong Smalltalk chỉ đơn giản là getter và setter)

+0

Tôi đánh giá cao kiểu cảnh báo này. Trên thực tế, đó là những gì tôi ủng hộ. –

0

Cá nhân tôi hiếm khi tận dụng thành viên được bảo vệ. Tôi thường ưu tiên composition, số decorator pattern hoặc strategy pattern. Có rất ít trường hợp mà tôi tin tưởng một lớp con (ing programmer) để xử lý các biến được bảo vệ một cách chính xác. Đôi khi tôi đã bảo vệ các phương pháp để cung cấp một giao diện rõ ràng cụ thể cho các lớp con, nhưng những trường hợp này thực sự hiếm.

Hầu hết thời gian tôi có một lớp cơ sở không có chỉ với các virtual virtual thuần túy (nói C++ bây giờ), và triển khai các lớp thực hiện chúng. Đôi khi, họ thêm một số phương pháp khởi tạo đặc biệt hoặc các tính năng cụ thể khác, nhưng phần còn lại là riêng tư.

0

Trước hết 'thuộc tính' có thể đề cập đến những thứ khác nhau bằng các ngôn ngữ khác nhau. Ví dụ, trong Java, bạn sẽ có nghĩa là các biến mẫu, trong khi C# có sự phân biệt giữa hai biến.

Tôi sẽ giả sử bạn có nghĩa là các biến mẫu kể từ khi bạn đề cập đến getters/setters.

Lý do mà những người khác đã đề cập là Đóng gói. Và những gì đóng gói mua chúng tôi?

linh hoạt

Khi mọi thứ phải thay đổi (và họ thường làm), chúng tôi rất ít có khả năng phá vỡ xây dựng bởi các thuộc tính đóng gói đúng cách.

Ví dụ chúng ta có thể quyết định để thực hiện một sự thay đổi như:

int getFoo() 
{ 
    return foo; 
} 

int getFoo() 
{ 
    return bar + baz; 
} 

Nếu chúng ta không gói gọn 'foo' để bắt đầu với, sau đó chúng ta sẽ có nhiều hơn nữa mã để thay đổi. (Hơn một dòng này)

Một lý do khác để đóng gói một tài sản, là để cung cấp một cách để đạn chống mã của chúng tôi:

void setFoo(int val) 
{ 
    if(foo < 0) 
    throw MyException(); // or silently ignore 

    foo = val; 
} 

Đây cũng là tiện dụng như chúng ta có thể thiết lập một breakpoint trong mutator, để chúng tôi có thể phá vỡ bất cứ khi nào một cái gì đó cố sửa đổi dữ liệu của chúng tôi.

Nếu tài sản của chúng tôi là công khai, thì chúng tôi không thể làm được điều này!

+0

Có, chúng tôi có thể. Mỗi lập trình viên đã học cách sử dụng getters/setters khi chúng được cung cấp. Đóng gói là tốt, nhưng tư nhân hóa không, theo ý kiến ​​của tôi. Bạn nghĩ sao? –

+0

Họ có thể có một phương pháp gói chúng, nhưng (nếu bạn có thể bỏ qua phương pháp đó) chúng không được đóng gói. Tôi không đồng ý rằng mọi lập trình viên sẽ thông qua getters/setters nếu chúng được cung cấp (và có tùy chọn truy cập trực tiếp vào trường.) Lý do tôi đã nghe vì đây là 'lý do hiệu quả'. Điều này là vô nghĩa và vấn đề bảo trì sẽ vượt xa mọi chu kỳ xử lý mà bạn có thể lưu. –

Các vấn đề liên quan