Bạn có nên sử dụng các biến thành viên được bảo vệ không? Những lợi thế là gì và những vấn đề này có thể gây ra?
Bạn có nên sử dụng các biến thành viên được bảo vệ không? Những lợi thế là gì và những vấn đề này có thể gây ra?
Câu trả lời:
Bạn có nên sử dụng các biến thành viên được bảo vệ không?
Phụ thuộc vào việc bạn kén chọn trạng thái ẩn như thế nào.
Nếu một nhà phát triển đến và phân lớp lớp con của bạn, họ có thể làm rối nó vì họ không hiểu nó đầy đủ. Với các thành viên riêng tư, khác với giao diện công khai, họ không thể xem chi tiết triển khai cụ thể về cách mọi thứ đang được thực hiện, điều này giúp bạn có thể linh hoạt thay đổi nó sau này.
Cảm giác chung ngày nay là chúng gây ra sự kết hợp quá mức giữa các lớp dẫn xuất và cơ sở của chúng.
Chúng không có lợi thế cụ thể nào so với các phương thức / thuộc tính được bảo vệ (ngày xưa chúng có thể có một chút lợi thế về hiệu suất), và chúng cũng được sử dụng nhiều hơn trong thời đại mà tính kế thừa rất sâu sắc đang là mốt, mà hiện tại thì không.
no particular advantage over protected methods/properties
có no particular advantage over *private* methods/properties
?
super
tạo để gọi hàm tạo mẹ; sau đó nó sẽ đảm nhận việc khởi tạo các biến trạng thái private trong lớp cha.
Nói chung, nếu điều gì đó không được cố tình coi là công khai, tôi sẽ đặt nó ở chế độ riêng tư.
Nếu một tình huống phát sinh trong đó tôi cần quyền truy cập vào biến hoặc phương thức riêng đó từ một lớp dẫn xuất, tôi sẽ thay đổi nó từ private thành protected.
Điều này hiếm khi xảy ra - tôi thực sự không phải là một fan hâm mộ của sự kế thừa, vì nó không phải là một cách đặc biệt tốt để mô hình hóa hầu hết các tình huống. Bằng mọi giá, tiếp tục, không phải lo lắng.
Tôi muốn nói rằng điều này là tốt (và có lẽ là cách tốt nhất để tiếp tục) đối với đa số các nhà phát triển.
Thực tế đơn giản của vấn đề là , nếu một số nhà phát triển khác đến cùng một năm sau đó và quyết định họ cần quyền truy cập vào biến thành viên riêng của bạn, họ sẽ chỉ cần chỉnh sửa mã, thay đổi nó thành được bảo vệ và tiếp tục công việc kinh doanh của họ.
Các ngoại lệ thực sự duy nhất cho điều này là nếu bạn đang kinh doanh vận chuyển dll nhị phân ở dạng hộp đen cho các bên thứ ba. Về cơ bản, điều này bao gồm Microsoft, các nhà cung cấp 'Kiểm soát lưới dữ liệu tùy chỉnh' và có thể một vài ứng dụng lớn khác có thư viện khả năng mở rộng. Trừ khi bạn thuộc thể loại đó, còn không thì bạn không nên bỏ thời gian / nỗ lực để lo lắng về loại điều này.
Vấn đề quan trọng đối với tôi là một khi bạn tạo một biến được bảo vệ, thì bạn không thể cho phép bất kỳ phương thức nào trong lớp của mình dựa vào giá trị của nó nằm trong một phạm vi, bởi vì một lớp con luôn có thể đặt nó ngoài phạm vi.
Ví dụ: nếu tôi có một lớp xác định chiều rộng và chiều cao của một đối tượng có thể kết xuất và tôi đặt các biến đó được bảo vệ, thì tôi không thể đưa ra giả định nào đối với (ví dụ), tỷ lệ khung hình.
Quan trọng là, tôi không bao giờ có thể đưa ra những giả định đó tại bất kỳ thời điểm nào kể từ thời điểm mã đó được phát hành dưới dạng thư viện, vì ngay cả khi tôi cập nhật bộ thiết lập của mình để duy trì tỷ lệ khung hình, tôi không đảm bảo rằng các biến đang được đặt thông qua bộ thiết lập hoặc được truy cập qua getters trong mã hiện có.
Bất kỳ lớp con nào trong lớp của tôi cũng không thể chọn đảm bảo điều đó, vì chúng cũng không thể thực thi các giá trị của biến, ngay cả khi đó là toàn bộ điểm của lớp con của chúng .
Ví dụ:
Bằng cách hạn chế các biến ở chế độ riêng tư, sau đó tôi có thể thực thi hành vi mà tôi muốn thông qua setters hoặc getters.
Nói chung, tôi sẽ giữ các biến thành viên được bảo vệ của bạn trong trường hợp hiếm hoi mà bạn có toàn quyền kiểm soát đối với mã sử dụng chúng. Nếu bạn đang tạo một API công khai, tôi sẽ nói là không bao giờ. Dưới đây, chúng tôi sẽ đề cập đến biến thành viên như một "thuộc tính" của đối tượng.
Dưới đây là những gì lớp cha của bạn không thể làm sau khi tạo một biến thành viên được bảo vệ thay vì những người truy cập riêng tư:
lười biếng tạo giá trị khi đang đọc thuộc tính. Nếu bạn thêm một phương thức getter được bảo vệ, bạn có thể tạo giá trị một cách lười biếng và chuyển nó trở lại.
biết khi tài sản được sửa đổi hoặc xóa. Điều này có thể tạo ra lỗi khi lớp cha đưa ra các giả định về trạng thái của biến đó. Tạo một phương thức setter được bảo vệ cho biến giữ quyền kiểm soát đó.
Đặt một điểm ngắt hoặc thêm đầu ra gỡ lỗi khi biến được đọc hoặc ghi vào.
Đổi tên biến thành viên đó mà không cần tìm kiếm qua tất cả các mã có thể sử dụng nó.
Nói chung, tôi nghĩ rằng đó sẽ là trường hợp hiếm hoi mà tôi khuyên bạn nên tạo một biến thành viên được bảo vệ. Tốt hơn hết bạn nên dành vài phút để hiển thị thuộc tính thông qua getters / setters hơn là vài giờ sau đó để theo dõi lỗi trong một số mã khác đã sửa đổi biến được bảo vệ. Không chỉ vậy, bạn còn được bảo đảm chống lại việc thêm chức năng trong tương lai (chẳng hạn như tải chậm) mà không vi phạm mã phụ thuộc. Làm sau khó hơn làm bây giờ.
Ở cấp độ thiết kế, có thể thích hợp để sử dụng một thuộc tính được bảo vệ, nhưng để triển khai, tôi không thấy lợi ích nào trong việc ánh xạ điều này tới một biến thành viên được bảo vệ hơn là các phương thức trình truy cập / trình đột biến.
Các biến thành viên được bảo vệ có những nhược điểm đáng kể vì chúng cho phép mã máy khách (lớp con) truy cập vào trạng thái bên trong của lớp lớp cơ sở một cách hiệu quả. Điều này ngăn cản lớp cơ sở duy trì hiệu quả các bất biến của nó.
Vì lý do tương tự, các biến thành viên được bảo vệ cũng làm cho việc viết mã đa luồng an toàn trở nên khó khăn hơn đáng kể trừ khi được đảm bảo không đổi hoặc giới hạn trong một luồng duy nhất.
Các phương pháp bộ truy cập / bộ đột biến cung cấp độ ổn định API hơn đáng kể và khả năng triển khai linh hoạt trong quá trình bảo trì.
Ngoài ra, nếu bạn là người theo chủ nghĩa OO, các đối tượng cộng tác / giao tiếp bằng cách gửi tin nhắn, không phải trạng thái đọc / thiết lập.
Đổi lại họ cung cấp rất ít lợi thế. Tôi không nhất thiết phải xóa chúng khỏi mã của người khác, nhưng bản thân tôi không sử dụng chúng.
Trong hầu hết thời gian, việc sử dụng bảo vệ sẽ rất nguy hiểm vì bạn phá vỡ phần nào sự đóng gói của lớp, lớp này cũng có thể bị phá vỡ bởi một lớp dẫn xuất được thiết kế kém.
Nhưng tôi có một ví dụ điển hình: Giả sử bạn có thể có một số loại thùng chứa chung chung. Nó có một triển khai nội bộ và những người truy cập nội bộ. Nhưng bạn cần cung cấp ít nhất 3 quyền truy cập công khai vào dữ liệu của nó: bản đồ, hash_map, vector-like. Sau đó, bạn có một cái gì đó như:
template <typename T, typename TContainer>
class Base
{
// etc.
protected
TContainer container ;
}
template <typename Key, typename T>
class DerivedMap : public Base<T, std::map<Key, T> > { /* etc. */ }
template <typename Key, typename T>
class DerivedHashMap : public Base<T, std::hash_map<Key, T> > { /* etc. */ }
template <typename T>
class DerivedVector : public Base<T, std::vector<T> > { /* etc. */ }
Tôi đã sử dụng loại mã này cách đây chưa đầy một tháng (vì vậy mã là từ bộ nhớ). Sau một số suy nghĩ, tôi tin rằng trong khi vùng chứa Base chung phải là một lớp trừu tượng, ngay cả khi nó có thể sống khá tốt, bởi vì việc sử dụng trực tiếp Base sẽ là một vấn đề khó khăn nên nó nên bị cấm.
Tóm tắt Như vậy, bạn đã bảo vệ dữ liệu được sử dụng bởi lớp dẫn xuất. Tuy nhiên, chúng ta phải tính đến thực tế là lớp Cơ sở phải là trừu tượng.
protected
không được đóng gói nhiều hơn public
. Tôi sẵn sàng được chứng minh là sai. Tất cả những gì bạn phải làm là viết một lớp có thành viên được bảo vệ và cấm tôi sửa đổi nó. Rõ ràng lớp phải không phải là lớp cuối cùng, vì toàn bộ điểm của việc sử dụng bảo vệ là để kế thừa. Hoặc một cái gì đó được đóng gói, hoặc nó không. Không có trạng thái ở giữa.
Trong ngắn hạn, có.
Các biến thành viên được bảo vệ cho phép truy cập vào biến từ bất kỳ lớp con nào cũng như bất kỳ lớp nào trong cùng một gói. Điều này có thể rất hữu ích, đặc biệt là đối với dữ liệu chỉ đọc. Tuy nhiên, tôi không tin rằng chúng luôn cần thiết vì bất kỳ việc sử dụng biến thành viên được bảo vệ nào cũng có thể được sao chép bằng cách sử dụng biến thành viên riêng và một vài getters và setters.
Để biết thông tin chi tiết về công cụ sửa đổi truy cập .Net, hãy truy cập vào đây
Không có lợi thế hay bất lợi thực sự nào đối với các biến thành viên được bảo vệ, đó là một câu hỏi về những gì bạn cần trong tình huống cụ thể của mình. Nói chung, việc khai báo các biến thành viên là private và cho phép truy cập bên ngoài thông qua các thuộc tính được chấp nhận. Ngoài ra, một số công cụ (ví dụ như một số trình ánh xạ O / R) mong muốn dữ liệu đối tượng được đại diện bởi các thuộc tính và không nhận ra các biến thành viên công khai hoặc được bảo vệ. Nhưng nếu bạn biết rằng bạn muốn các lớp con của mình (và CHỈ là các lớp con của bạn) truy cập vào một biến nhất định thì không có lý do gì để không tuyên bố nó là được bảo vệ.