it-swarm-vi.com

Trong OOP, không phải là từ khóa 'được bảo vệ'?

Một số ngôn ngữ hiện đại (ví dụ: Swift, Dart) không hỗ trợ từ khóa sửa đổi truy cập protected. Swift là ngôn ngữ hướng giao thức, nhưng tôi đã nghe nói rằng Dart là ngôn ngữ hoàn toàn hướng đối tượng.

Tại sao những ngôn ngữ hiện đại này không hỗ trợ protected? Bạn chỉ cần privatepublic để lập trình hướng đối tượng hoàn chỉnh?

Tôi nghĩ thật tiện lợi khi có một từ khóa sửa đổi truy cập protected khi có một số dữ liệu hoặc giao diện mà tôi muốn chuyển từ lớp cha sang lớp con. Tại sao một số ngôn ngữ hiện đại không hỗ trợ protected?

28
ShutUpILoveYou

Nó phụ thuộc vào những gì bạn có nghĩa là "bắt buộc".

Sửa đổi truy cập không phải là một điều cần thiết. Bạn có thể thay thế mọi công cụ sửa đổi truy cập bằng public và hầu hết các ứng dụng sẽ hoạt động giống như khi bạn sử dụng các công cụ sửa đổi truy cập khác nhau, chứng minh rằng mục tiêu chính của trình biên dịch (xuất ra ứng dụng hoạt động) không phụ thuộc trực tiếp vào công cụ sửa đổi truy cập .

Như Delioth đã đề cập trong các nhận xét, cả Javascript và Python đều có khả năng OOP chưa có khái niệm về công cụ sửa đổi truy cập; chứng minh điểm đó = OOP không yêu cầu sửa đổi truy cập.

Tuy nhiên, sửa đổi truy cập rất nhiều vấn đề từ quan điểm của nhà phát triển nếu bạn muốn tránh sai lầm. Thiếu các hạn chế truy cập dẫn đến các nhà phát triển truy cập trực tiếp vào các phụ thuộc mà họ không nên (ví dụ: phá vỡ lớp xác thực/ủy quyền) và điều này sẽ dẫn đến các lỗi, dẫn đến mất thời gian và công sức.

Tóm lại, các công cụ sửa đổi truy cập không cần thiết cho trình biên dịch, nhưng chúng hầu hết được coi là một công cụ rất tốt để thực hành tốt. Các hướng dẫn như vậy "yêu cầu" các nhà phát triển thực hiện kiểm soát truy cập chuyên sâu - ngay cả khi trình biên dịch không cần đến nó.

Tại sao một số ngôn ngữ hiện đại loại bỏ protected?

Không có câu trả lời áp dụng chung cho câu hỏi đó, ngoại trừ "bởi vì đó là điều mà các nhà thiết kế ngôn ngữ quyết định làm".

45
Flater

Không, không bắt buộc: Bjarne Stroustrup, đã giải thích cách anh ta ngây thơ thêm protected vào bản phát hành C++ 1.2, suy nghĩ để cung cấp một tính năng hữu ích cho các nhà phát triển lớp, chỉ để kết luận chỉ 5 năm sau đó là một nguồn lỗi khó chịu, may mắn thay, không ai bị buộc phải sử dụng. Ngày nay, anh khuyên không nên sử dụng nó .

đối số thực tế chống lại protected là lợi thế của đóng gói mạnh hơn và nguyên tắc ít kiến ​​thức nhất :

  • Một thành viên là public và có thể được sử dụng bởi bất kỳ ai;
  • Hoặc thành viên là private và cần được bảo vệ chống lại truy cập bên ngoài.
  • Một thành viên protected, yêu cầu sử dụng cẩn thận (nếu không nó sẽ là công khai) có thể bị lạm dụng nhiều bởi những người trong cuộc (nhà phát triển của lớp dẫn xuất) như bất kỳ ai khác.

Đối số chính thức xác nhận trải nghiệm thực tế. Điều này có liên quan đến nguyên tắc thay thế Liskov và chính xác hơn là quy tắc lịch sử:

Chúng tôi nghĩ rằng nó chỉ đủ để người dùng chỉ biết về loại hình rõ ràng của đối tượng; kiểu con nên bảo tồn bất kỳ thuộc tính nào có thể được chứng minh về siêu kiểu.
[.__.] - Barbara Liskov & Jeanette Wing trong Một khái niệm hành vi của phân nhóm

Không đi sâu vào chi tiết của bài báo được trích dẫn, các thành viên được bảo vệ cho phép một lớp dẫn xuất (kiểu con) thay đổi trạng thái của đối tượng lớp cơ sở (siêu kiểu) một cách bất ngờ, mà không cần dựa vào các hoạt động công khai của nó.

Điều này đang được nói, hãy cẩn thận với sự xuất hiện và lời hứa sai. Swift private nằm ở giữa privateprotected trong các ngôn ngữ khác:

Quyền truy cập riêng tư hạn chế việc sử dụng một thực thể đối với khai báo kèm theo, và đối với các phần mở rộng của khai báo đó trong cùng một tệp . (...).
[.__.] - Apple, in The Swift ngôn ngữ lập trình

33
Christophe

Python cũng là một ngôn ngữ tuân thủ mạnh mẽ phương pháp lập trình hướng đối tượng. Nó sử dụng cách tiếp cận cổ điển của các lớp và các đối tượng.

Tuy nhiên, điều cần nhớ là bất kỳ "Word" nào cũng chỉ là hợp đồng giữa bạn và những người duy trì (tương lai). Có một tên khác, hoặc thậm chí không rõ ràng cho một cái gì đó không có nghĩa là hợp đồng này không có ở đó.

Python sử dụng thông tin "chúng ta đều là người lớn" và mong mọi người làm việc với các đối tượng thay vì chống lại nó. Do đó, nó xem xét mọi thứ công khai và bạn sẽ tự lập hợp đồng bằng cách mô tả lớp học. (PEP8, sách thiết kế, thông báo tiền tố với _ là một ý tưởng tốt để hiển thị hợp đồng của các lĩnh vực tư nhân IDE hiểu điều này).

Được bảo vệ (như một ý tưởng rằng bạn không thể truy cập trực tiếp vào biến, trừ khi bạn xuất phát từ nó) dù sao cũng là một hợp đồng yếu. Nếu bạn muốn 'ngăn chặn' các lỗi do thay đổi sai đối với các trường quan trọng, để bảo vệ trạng thái bên trong, một biến được bảo vệ vẫn có thể thay đổi theo ý muốn và một lớp dẫn xuất có thể dễ dàng phơi bày điều này và thay đổi nó một cách tồi tệ.

Vì vậy, câu hỏi nên được đặt ra cho bạn: "tại sao lại thêm một mô hình bổ sung" vào một ngôn ngữ mà không sử dụng lợi thế trực tiếp? YAGNI cũng có thể áp dụng ở đây.

9
paul23

Trước khi chúng tôi quyết định rằng công cụ sửa đổi truy cập được bảo vệ phải được xóa khỏi tất cả các ngôn ngữ phổ biến OO tôi muốn chỉ ra rằng sẽ rất bất tiện khi mất nó.

Trong các lớp cơ sở trừu tượng đóng vai trò là kế hoạch chi tiết cho một số lớp dẫn xuất, bạn có thể sẽ có rất nhiều phương thức hỗ trợ cho các đạo hàm này sẽ vô nghĩa đối với người dùng cuối của các đạo hàm đó. Ergo, bạn sẽ nhận được các giao diện ồn ào và bạn sẽ phải tìm một cách khác để báo hiệu các phương thức này không được gọi bởi các máy khách đối tượng.

Một số có thể nói có nhiều cách xung quanh đó. Rằng bạn có thể áp dụng thành phần thay thế. Họ sẽ cung cấp cho bạn một số lý do để không sử dụng thừa kế ở nơi đầu tiên. Bất cứ giá trị nào có thể có trong các tuyên bố này, được bảo vệ là có để hỗ trợ việc áp dụng thừa kế. Viết các lớp trừu tượng hữu ích mà không được bảo vệ sẽ khó khăn.

Tôi có thể nói rằng tôi không sử dụng nó nhiều bên ngoài các lớp cơ sở trừu tượng. Nhưng miễn là chúng tôi có các lớp cơ sở trừu tượng tôi muốn giữ từ khóa được bảo vệ của tôi cảm ơn bạn.

8
Martin Maat

Một trong những ngôn ngữ hướng đối tượng đầu tiên, Smalltalk, không có từ khóa hoặc cơ chế protectedprivate cũng không rõ ràng nhưng ngụ ý cho các biến thể hiện và được đề xuất theo quy ước cho các phương thức. Hoạt động khá tốt trừ khi mọi người xem tính linh hoạt như một lời mời đánh mọi thứ bằng một cây búa lớn :-)

3
Hans-Martin Mosner

protected là về kiểm soát truy cập dữ liệu. OOP là về đóng gói.

Mục tiêu chính của OOP là cấu trúc mã sao cho các thực thể (dữ liệu + thao tác trên nó) được kết hợp yếu với nhau. Thực tế là dữ liệu được đóng gói được kiểm soát (tương đối với quyền truy cập của chúng) hoặc Bảo vệ có liên quan chặt chẽ hơn với thừa kế, một trong những kỹ thuật để nhận ra mối quan hệ khái quát hóa/chuyên môn hóa. Nhưng ngay cả thừa kế cũng không cần thiết, ủy quyền có thể được sử dụng để thực hiện G/S một cách tinh tế hơn, và trong trường hợp đó được bảo vệ là không sử dụng.

1
Jean-Baptiste Yunès

Như Flater đã viết, các hạn chế về acces không thực sự cần thiết.

Và một số ý kiến ​​cho rằng quyền truy cập được bảo vệ đang cố gắng làm nhiều việc cùng một lúc. Bạn có thể sử dụng bảo vệ trong trường hợp như:

  1. Phương thức nên được gọi bằng phương thức lớp con
  2. Phương thức nên được thực hiện bằng các phương thức của lớp con và sẽ được gọi bởi lớp cha hoặc các lớp con khác
  3. Phương thức có thể được ovveriden theo lớp con và sẽ được gọi bởi lớp cha hoặc các lớp con khác
  4. những điều tương tự với các lĩnh vực

công cụ sửa đổi tốt hơn (trong cú pháp Java ish):

  1. bảo vệ cuối cùng
  2. chia thành hai phương thức, một phương thức được bảo vệ (hoặc riêng tư, nếu các lớp con không được gọi là cuối cùng) (cuộc gọi đó) và một bản tóm tắt được bảo vệ khác mà các lớp con nên thực hiện, nhưng không gọi.
  3. giống như 2. nhưng không trừu tượng

Và để làm cho nó ngắn hơn và rõ ràng hơn, sử dụng 3 từ khác nhau.

0
user470365

Bạn đã đề cập Swift một cách rõ ràng, vì vậy tôi sẽ trả lời về lý do tại sao Swift không có protected.

Không giống như nhiều ngôn ngữ khác, Swift cho phép bạn viết "tiện ích mở rộng" cho các loại khác (lớp, cấu trúc, enum và giao thức giống nhau), ngay cả những ngôn ngữ bạn không sở hữu. để tạo kiểu thư viện A phù hợp với giao thức của thư viện B (ví dụ về "mô hình hồi tố"). Ví dụ: bạn có thể có một đối tượng Image (từ thư viện A) mà bạn muốn tuân thủ giao thức ORM của bạn DatabaseSerializable (từ thư viện B) để có thể được tuần tự hóa thành cơ sở dữ liệu. Trong hầu hết các ngôn ngữ, yêu cầu gói mọi thứ bộ điều hợp ở mọi nơi. Trong Swift, bạn chỉ cần mở rộng Image trực tiếp tuân thủ DatabaseSerializable

extension Image: DatabaseSerializable {
    func serailize(to db: Database) {
        // do whatever is necessary to save to the db or whatever
    }

Chúng là một tính năng rất cốt lõi ảnh hưởng lớn đến phong cách lập trình được thực hiện trong Swift. Ví dụ: chúng thường được sử dụng để phân tách trực quan sự phù hợp với nhiều giao thức, ví dụ:

class Person {
    let firstName: String
    let lastName: String

    init(firstName: String, lastName: String) {
         self.firstName = firstName
         self.firstName = lastName
    }
}

// This impl can be auto-synthesized by the compiler, but I'm showing it here as an example anyway
extension Person: Equatable {
    static func == (lhs: Person, rhs: Person) -> Bool {
        return lhs.firstName == rhs.firstName && lhs.lastName == rhs.lastName
    }
}

// This impl can be auto-synthesized by the compiler, but I'm showing it here as an example anyway
extension Person: Hashable {
    func hash(into hasher: inout Hasher) {
        hasher.combine(self.firstName)
        hasher.combine(self.lastName)
    }
}

extension Person: CustomStringConvertible {
    var description: String { "\(firstName) \(lastName)" }
}

Bây giờ trong ví dụ này, hãy tưởng tượng có một trường được bảo vệ, socialInsuranceNumber. Nếu tôi ở trong bối cảnh của một số lớp khác, nó không thể truy cập được. Nếu tôi thuộc lớp Person hoặc lớp con, nó có thể truy cập được. Nhưng điều gì xảy ra nếu tôi ở trong bối cảnh của phần mở rộng Person? Có nên phụ thuộc vào nơi mở rộng được thực hiện? (ví dụ: cho phép nó trong cùng một mô-đun như Person, nhưng không cho phép truy cập từ tiện ích mở rộng trong các mô-đun khác). Điều gì xảy ra nếu tôi làm điều này?

extension Person {
    public var publicSocialInsuranceNumber: SIN {
        self.socialInsuranceNumber // this should be protected!
    }
}

Tôi vừa mới phá vỡ các biện pháp bảo vệ một mức truy cập protected sẽ cung cấp.

Thay vào đó, Swift có fileprivate, hoạt động như private, ngoại trừ trường có thể truy cập được từ tệp xác định. Vì vậy, mở rộng thành Person trong Person.Swift có thể truy cập socialInsuranceNumber, nhưng Person tiện ích mở rộng được xác định ở bất kỳ nơi nào khác không thể.

Trong Swift, người ta đã quyết định rằng một lớp con không liên quan đáng kể đến lớp cơ sở. Nếu một số thông tin không có sẵn cho công chúng, nó sẽ không có sẵn cho một lớp con.

Ngoài ra còn có bộ phim fileprivate, cho phép các thành viên chỉ có sẵn trong một tệp, vì vậy nếu các lớp có liên quan chặt chẽ với nhau, chúng có thể được thực hiện trong một tệp.

0
gnasher729