150 bài tập -- lập trình hướng đối tượng trong python - oop

Đây là bài thứ ba trong loạt năm bài học, tóm tắt nhu cầu thực tế đối với lập trình hướng đối tượng và trình bày các phương tiện phổ biến được cung cấp bởi các ngôn ngữ hướng đối tượng. Các ví dụ bằng Python, nhấn mạnh cách tiếp cận và triển khai Python. Trong các chương trước, chúng ta đã nắm được các từ vựng cơ bản về lập trình hướng đối tượng. khả năng thay thế chức năng, sử dụng mô hình thông báo, cho các đối tượng có hành vi và dữ liệu được đóng gói trong các lớp. Trong bài học hiện tại, dựa trên nhận xét rằng việc áp dụng thiết kế hướng đối tượng trong thực tế phải có kỷ luật, tuân theo các thành ngữ, mẫu và kiến ​​trúc đã được thiết lập, chúng tôi tiến hành với một ví dụ chi tiết về cách tiếp cận hướng đối tượng đối với cấu trúc dữ liệu đệ quy - “ . Cuối cùng, chúng ta sẽ sử dụng sự khôn ngoan này để kết thúc ví dụ của bài học trước

Các phần trong bài học này

  1. Trường hợp phổ biến về khả năng thay thế hướng đối tượng - Đa hình “thuận tay trái”
  2. Thách thức của tính đa hình “kép”
  3. Lối Thoát Dễ Dàng. “Đa phương pháp”
  4. Lối thoát hướng đối tượng — “Double Dispatch”
  5. Mô hình "Khách truy cập"
  6. Biến thể "Khách truy cập phân cấp"
  7. Tập thể dục. Tệp “Phác thảo” với nhiều trình kết xuất

1. Trường hợp phổ biến về khả năng thay thế hướng đối tượng - Đa hình “thuận tay trái”

Như đã giới thiệu trong bài học đầu tiên của khóa học này, giải pháp hướng đối tượng để yêu cầu khả năng thay thế [chúng ta biết phải làm gì, nhưng có nhiều cách để làm điều đó] là đa hình thuận tay trái, dựa trên một [rõ ràng hoặc ẩn] . Đưa ra một đối tượng ẩn danh — nhưng phù hợp với giao diện — là đối tượng nhận tin nhắn [được đặt ở phía bên trái của toán tử dấu chấm], chúng tôi chuyển cho nó bộ chọn tin nhắn và các đối số [ở bên phải của toán tử dấu chấm]. Liên kết động sẽ lo phần còn lại

Nói một cách đơn giản, cách giải thích hướng đối tượng của “chúng tôi biết phải làm gì, nhưng có nhiều cách để làm điều đó…” tiếp tục với trường hợp “…và nó [cách thực hiện] phụ thuộc vào loại của . ”. Đúng. mô hình hướng đối tượng thay thế thách thức kích hoạt khả năng thay thế bằng thách thức đơn giản hơn nhiều trong việc giải quyết một đối tượng có thể thay thế. Đa hình thuận tay trái rất đơn giản, dễ hiểu và rất hiệu quả để triển khai theo chương trình. [Không có câu hỏi đáng xấu hổ nào được hỏi, chẳng hạn như cấu trúc chuyển đổi/trường hợp rõ ràng]. Nhưng với một hạn chế rõ ràng [đáng tiếc là bị một số người bỏ qua]. một miền vấn đề phù hợp. Để đa hình thuận tay trái có thể áp dụng được, phải có một đối tượng ở phía bên trái và chính xác một đối tượng như vậy

Đầu tiên, chúng ta hãy xem xét một trường hợp tuân thủ - “Thử nghiệm va chạm hình dạng/điểm”. Trình chỉnh sửa đồ họa hiển thị trên màn hình một khung vẽ trên đó một số hình dạng được hiển thị. Người dùng click chuột. Chương trình đánh thức sự kiện, nhận tọa độ nhấp chuột [x. điểm y]. Danh sách các hình được trình bày được quét để tìm hình [đầu tiên] va chạm với điểm. Cách hướng đối tượng để thực hiện điều này là chuyển thông báo Boolean “đang va chạm” vào từng hình dạng và chọn hình dạng đầu tiên phản hồi tích cực. Tất nhiên, tất cả các Hình dạng cụ thể [Hình tròn và Hình chữ nhật] đều thực hiện phương pháp này

Giải pháp hướng đối tượng cho vấn đề - "Hình dạng, bạn có va chạm với Điểm này không?" . Một Shape biết về Points [e. g. , nó có một điểm ở giữa] và chỉ có một loại Điểm trong hình học này. Để so sánh, giải pháp hướng đối tượng ngược lại - "Điểm, bạn có xảy ra bên trong Hình dạng này không?" . Và giải pháp toàn cầu - "Có sự va chạm nào giữa Hình dạng này [bất kể nó là gì] và Điểm này không?"

mã ví dụ

chú thích

  1. “Is va chạm” là một phương thức trừu tượng trong Hình dạng trừu tượng
  2. Phương pháp Circle của "đang va chạm"
  3. Phương pháp Rectangle của “đang va chạm”

đầu ra

Testing collision with 110:110
1. Circle at 100:100. r=50: True
2. Circle at 200:100. r=10: False
3. Rectangle at 100:200. 100x100: False
4. Rectangle at 100:105. 20x30: True

2. Thách thức của tính đa hình “kép”

“Thuận tay trái” - hay “số ít” - tính đa hình dường như đáp ứng - một cách tự nhiên hoặc với một số điều chỉnh - hầu hết các thách thức thiết kế về khả năng thay thế chức năng [điều này giải thích cho tính phổ biến của nó]. Tuy nhiên - ngạc nhiên, ngạc nhiên. - nó không phải là thuốc chữa bách bệnh [như một số người quá khích hướng đối tượng sẽ có nó]. Giống như bất kỳ công cụ nào, nó có lĩnh vực ứng dụng và giới hạn của nó, mà người chuyên nghiệp phải ghi nhớ. Một hạn chế nổi tiếng như vậy là đa hình, được thừa nhận là hiếm hơn nhiều so với đa dạng đơn lẻ, nhưng vẫn có, được tính đến với. [Hay nó hiếm vì chúng ta có điều kiện để tránh nó?]

Ví dụ: chúng tôi có thể giải quyết thử thách “Hình dạng va chạm với Điểm” một cách tầm thường, bằng cách chuyển thông báo Boolean “đang va chạm” tới một hình dạng ẩn danh với đối số là điểm, bởi vì [1], loại Hình dạng có thể thay thế được, nhưng . [tôi. e. , người nhận tin nhắn có thể thay thế được, nhưng tin nhắn được gõ mạnh — Dự kiến ​​điểm. Không có gì khác sẽ được chấp nhận. ]

Bây giờ, hãy xem xét trường hợp sử dụng “Hình dạng xung đột với Hình dạng”. [Ví dụ: chúng ta có thể muốn sơn khu vực được chia sẻ bằng hỗn hợp của cả hai màu]. Có rất ít lý do để buộc một giải pháp thuận tay trái cho vấn đề này [mà như chúng ta sẽ thấy, dù sao nó cũng sẽ không hoạt động], bởi vì không có lý do gì để thích “Hình tròn, bạn có va chạm với Hình chữ nhật này” hơn “Hình chữ nhật, làm . “Hình bên trái, bất kể bạn là gì, bạn có va chạm với hình bên phải không, bất kể đó là gì?”

Ở đây, có liên quan đến hai đối tượng có thể thay thế và cả hai đều có quyền bình đẳng đối với danh hiệu “Người nhận thông điệp”. Vì vậy, không ai trong số họ sẽ giành chiến thắng. Đây là một thách thức về thủ tục "các Hình tròn và Hình chữ nhật này có va chạm nhau không?" . “hai Hình dạng này, dù chúng là gì, có va chạm vào nhau không?”]. Vì thách thức này không dễ dàng phù hợp với thông điệp — đối với một đối tượng — khung mẫu, người ta có thể tự do kết luận rằng trường hợp sử dụng này không đề xuất thiết kế hướng đối tượng nghiêm ngặt, nếu có. [Rõ ràng, những điều như vậy tồn tại. ]

Một giải pháp chức năng cho vấn đề này sẽ đề xuất một chức năng [toàn cầu] “đang va chạm” với hai tham số — có thể thay thế —. Một chức năng như vậy còn được gọi là đa phương thức. một "phương pháp" của [như thể] hai [hoặc nhiều] lớp. Thách thức thiết kế ở đây là mỗi sự kết hợp của hai Hình dạng — [1] Hình tròn/Hình tròn, [2] Hình tròn/Hình chữ nhật và [3] Hình chữ nhật/Hình chữ nhật] — cần một phương pháp riêng biệt. [Trừ khi bạn có ý định đếm pixel, sử dụng va chạm Hình dạng/Điểm, dù sao thì điều này cũng phụ thuộc vào việc triển khai. Mặt khác, hãy nhớ rằng các công thức hình học được yêu cầu là khác nhau]

Để bắt đầu, chúng ta cần ngôn ngữ lập trình của mình hỗ trợ “nạp chồng hàm” — khả năng xác định nhiều hàm [trong cùng một không gian tên] bằng cùng một tên, nhưng với một danh sách các loại tham số khác nhau. [Chúng tôi cần tất cả các chức năng khác nhau này được gọi bằng cùng một tên, bởi vì - trong thời gian gọi - chúng tôi không biết cần thực hiện chức năng nào. ] Và Python gõ vịt sẽ không nhận ra hai hàm sau đây là hai hàm riêng biệt. [1] “isColliding[Trái. Vòng tròn, bên phải. Circle]” và [2] “isColliding[Trái. Vòng tròn, bên phải. Hình chữ nhật]”. Để tăng thách thức thiết kế, độ phân giải của phương pháp được yêu cầu phải được chuyển sang thời gian chạy. [Trong thời gian biên dịch, chúng tôi đang va chạm Hình dạng ẩn danh với Hình dạng ẩn danh, điều này không giúp chúng tôi khôn ngoan hơn chút nào]. Khả năng lập trình này ngày nay được gọi là “đa công văn” và được hỗ trợ bởi rất ít ngôn ngữ lập trình hiện tại [nổi tiếng nhất là Julia]

Python không hỗ trợ — như đã nêu — ngay cả yêu cầu đơn giản về nạp chồng hàm và vì một lý do đơn giản. kiến trúc dựa trên từ điển. Vì tên hàm — một chuỗi — được sử dụng làm khóa cho từ điển của đối tượng [hoặc mô-đun], nên có thể chỉ có một hàm như vậy. Và cũng vì lý do đó, chúng ta có thể không cung cấp cho mỗi lớp Shape các phương thức phát hiện xung đột với các Shape khác; . Hãy thử làm điều này và Python sẽ thay thế không thương tiếc từng định nghĩa phương thức khi nó xuất hiện, kết thúc bằng việc trỏ đến định nghĩa cuối cùng, đây không phải là điều chúng ta muốn]. Và sau đó, nhúng tên loại đối số vào tên phương thức — chẳng hạn như “Vòng tròn đang va chạm”, “Hình chữ nhật đang va chạm”, v.v. - cũng sẽ không hoạt động; . Mặc dù các phương thức khác nhau này — mang các tên riêng biệt — sẽ được giữ nguyên, nhưng chúng sẽ không thể truy cập được. [Python sẽ không thể gửi đến đúng phương thức liên quan đến loại, được đặt tên phương thức khác và một số Hình dạng ẩn danh]

Và cuối cùng, việc cung cấp chính xác một phương thức "hình dạng đang va chạm" cũng sẽ không hoạt động và vì lý do tương tự. Trừ khi chúng ta dùng đến việc thực hiện công văn phụ thuộc vào loại rõ ràng của riêng mình bên trong bằng cách sử dụng siêu dữ liệu - ít nhất là rườm rà và khác xa với một phương pháp chung - để sao chép/dán bất cứ nơi nào có nhu cầu. [Tuy nhiên, lưu ý rằng việc làm ảnh hưởng đến nguyên tắc mở/đóng - sẽ được thảo luận sau - không phải là vấn đề ở đây; trong miền vấn đề này, tất cả các loại Hình dạng đều đã được biết trước và kho lưu trữ của chúng sẽ không được bổ sung thêm]

3. Lối Thoát Dễ Dàng. “Đa phương pháp”

Theo truyền thống, thách thức của tính đa hình kép được giải quyết theo [1] cách “chức năng” - gửi nhiều lần [sử dụng “đa phương thức”] - hoặc [2] cách hướng đối tượng [vâng, có một. ] — “công văn kép”

Đầu tiên, chúng ta hãy xem xét giải pháp đa phương pháp, bởi vì nó đơn giản [ít nhất là đối với bài toán đơn giản này]. Và đây là yêu cầu. Chúng tôi cần triển khai Python cho nhiều công văn [chức năng quá tải được giải quyết trong thời gian chạy]. Có nghĩa là, trong trường hợp của chúng tôi. Đối với mỗi sự kết hợp của hai Hình dạng, chúng ta phải có khả năng xác định một kiểu toàn cục và kiểu mạnh riêng biệt [. ] chức năng [lấy hai đối tượng của các loại rõ ràng này]. Và tất cả các chức năng này sẽ đáp ứng cùng một tên

Mặc dù Python không hỗ trợ nhiều công văn, nhưng không khó để mở rộng ngôn ngữ, sử dụng cơ chế ba lần, như đã được nhiều tác giả vạch ra, bao gồm Bjarne Stroustroup [đối với C++] và bản thân BDFL [đối với Python]. [1], một từ điển đa phương thức [một cho mỗi tên hàm có thể thay thế, được khóa bởi một danh sách các loại tham số được sắp xếp theo thứ tự], [2], một cơ chế đăng ký [đến từ điển thích hợp] và [3], một cơ chế điều phối thời gian chạy. Tất nhiên, chúng tôi sẽ hoan nghênh một giải pháp thông minh bằng cách nào đó quản lý để che giấu sự tồn tại của tất cả bộ máy này [từ điển, đăng ký và gửi thời gian chạy] dưới vỏ bọc của cú pháp Python thông thường

May mắn thay, Python đủ phong phú để đương đầu với thử thách. Và may mắn hơn nữa, chúng ta không cần phải tự viết cơ chế này; . Đối với bài học này, chúng ta sẽ sử dụng gói "multidispatch" [có sẵn thông qua tiện ích pip tiêu chuẩn], mặc dù được định nghĩa là "công việc đang tiến hành", đủ ổn định và toàn diện để đáp ứng thách thức đơn giản của chúng ta. Điều quan trọng nhất - gói này thực sự minh bạch với người dùng, ẩn toàn bộ cơ chế không tầm thường đằng sau giao diện trực quan kiểu Julia của - cho mỗi đa phương thức - trình trang trí chức năng sử dụng danh sách loại thích hợp

Đây là một ví dụ

chú thích

  1. Nhập gói "nhiều công văn"
  2. Đa phương pháp cho "đang va chạm" bằng Circle và Circle
  3. Đa phương pháp cho "đang va chạm" bằng cách sử dụng Hình tròn và Hình chữ nhật
  4. Đa phương pháp cho "đang va chạm" bằng Hình chữ nhật và Hình chữ nhật
  5. Đa phương pháp cho "đang va chạm" bằng Hình chữ nhật và Hình tròn. Vì sự kết hợp là đối xứng, nên nó chỉ đơn giản là đảo ngược, sử dụng phương pháp ngược lại. Không có lý do để sao chép logic

đầu ra

Circle at 100:100. r=50 / Circle at 150:200. r=60: False
Circle at 100:100. r=50 / Rectangle at 100:100. 10x10: True
Circle at 100:100. r=50 / Rectangle at 140:190. 20x30: False
Circle at 150:200. r=60 / Circle at 100:100. r=50: False
Circle at 150:200. r=60 / Rectangle at 100:100. 10x10: False
Circle at 150:200. r=60 / Rectangle at 140:190. 20x30: True
Rectangle at 100:100. 10x10 / Circle at 100:100. r=50: True
Rectangle at 100:100. 10x10 / Circle at 150:200. r=60: False
Rectangle at 100:100. 10x10 / Rectangle at 140:190. 20x30: False
Rectangle at 140:190. 20x30 / Circle at 100:100. r=50: False
Rectangle at 140:190. 20x30 / Circle at 150:200. r=60: True
Rectangle at 140:190. 20x30 / Rectangle at 100:100. 10x10: False

4. Lối thoát hướng đối tượng — “Double Dispatch”

Thành ngữ “gửi kép” [một thuật ngữ cũ hơn nhiều so với “gửi nhiều lần”, và không được trộn lẫn với nó] chia đa phương thức thành hai. Hít một hơi thật sâu. Phía bên trái nhận bên phải và sau đó gửi chính nó sang phía bên phải. Kỳ tích nhào lộn này liên quan đến việc giải quyết hai lần một mặt trái đa hình [tình cờ là chuyên ngành hướng đối tượng. ] Hiệu suất không đổi [chính xác là hai thông báo, so với chi phí tra cứu từ điển không thể đoán trước trong giải pháp thay thế đa phương pháp] đạt được với điều kiện tiên quyết là "phân cấp ổn định" [tất cả các Hình dạng ứng cử viên phải được biết trước], trong đó,

Yêu cầu chức năng “Hình va chạm với hình dạng” được giảm bớt, trong trường hợp “Hình tròn va chạm với Hình chữ nhật” thành hai thông báo. [1], “Hình tròn, bạn có va chạm với Hình dạng này không” [mà chúng tôi biết, tình cờ là Hình chữ nhật] và [2], “Một số Hình dạng [mà chúng tôi biết, tình cờ là Hình chữ nhật từ bước 1], bạn có . tôi là một vòng tròn. ”

Thí dụ

chú thích

  1. Phương thức Hình dạng phổ biến của “đang va chạm” [với một Hình dạng khác] xây dựng tên của phương thức “đang va chạm” bổ sung [một chuỗi] phù hợp với kiểu của chính nó [là một phần của tên phương thức ở phía bên kia], trông giống như
  2. Phương pháp va chạm Circle với Circle
  3. Phương pháp va chạm Hình tròn với Hình chữ nhật
  4. Phương pháp va chạm Hình chữ nhật với Hình tròn. Vì tính đa hình kép này xảy ra đối xứng về mặt chức năng - Hình tròn/Hình chữ nhật và Hình chữ nhật/Hình tròn cho cùng một kết quả - nên không cần phải sao chép logic
  5. Phương pháp va chạm Hình chữ nhật với Hình chữ nhật

[Đầu ra vẫn giữ nguyên]

Giải pháp Python liên quan đến một số phép thuật siêu dữ liệu, làm cho nó đơn giản hơn so với đối tác truyền thống của nó trong các ngôn ngữ gõ mạnh [chẳng hạn như C++, Java hoặc C#]. Giả sử rằng đối tượng bên phải, bất kể nó là gì, có một phương thức có tên bao gồm “is va chạm”, theo sau là tên của lớp đối tượng bên trái [chúng ta đang ở đâu], phương thức bên trái sẽ xây dựng tên

5. Mô hình "Khách truy cập"

Giả sử chúng ta không chỉ được yêu cầu sử dụng thư viện Hình dạng để tính toán hình học mà còn để hiển thị các thiết kế làm từ Hình dạng. Mặc dù có thể có một phương tiện mặc định [giả sử, “. png”, được kết xuất bằng Thư viện hình ảnh Python], chúng tôi mong muốn thiết kế cho phép mở/đóng mở rộng sang các công nghệ kết xuất và phương tiện đầu ra khác

Rõ ràng, thách thức thiết kế chính của chúng tôi là khả năng thay thế nhiều. Có [1] nhiều loại hình dạng để kết xuất, [2] nhiều phương tiện đầu ra để kết xuất trên đó, [3] nhiều công cụ kết xuất để dựa vào, v.v.

Nhiều công văn dường như không phù hợp ở đây [ít nhất là không phải như vậy], bởi vì nó liên quan đến rất nhiều chức năng toàn cầu mà [dường như] không biết về nhau và không quản lý trạng thái. Nhưng trường hợp sử dụng này yêu cầu đóng gói. chúng ta cần một đối tượng [“Trình kết xuất”] quản lý trạng thái [những gì nó đã kết xuất cho đến nay và thiết bị đi kèm với nó. canvas, phông chữ, bút vẽ, v.v. ]. Hơn nữa, tại bất kỳ thời điểm nào cũng có thể có nhiều Trình kết xuất đang bận kết xuất các bộ Hình dạng khác nhau trên các khung vẽ khác nhau, không để ý đến nhau. Vì vậy, một giải pháp toàn cầu là không cần thiết. [Tuy nhiên, nhiều công văn được gói gọn bằng cách nào đó nghe có vẻ là một thách thức thiết kế thú vị và tôi sẽ đánh giá cao những đề xuất cụ thể. ]

Và sau đó, ở đây chúng ta bắt gặp tính đa hình kép của một loại khác. Đối với một điều, khả năng thay thế chức năng này là không đối xứng. Mặt khác, chỉ một trong số những người tham gia ở đây là thực sự có thể thay thế

1. Trong trường hợp sử dụng “Hình dạng có thể xung đột với Hình dạng”, trách nhiệm chia sẻ là đối xứng, được phân bổ đồng đều giữa hai người tham gia. Mỗi người đều có yêu cầu như nhau để trở thành người nhận. Chúng tôi không có lý do gì để thích "Hình tròn, bạn có va chạm với Hình chữ nhật này không?" . Nhưng trong trường hợp sử dụng “Trình kết xuất hình dạng hiển thị hình dạng”, việc phân bổ trách nhiệm là không đối xứng. Các chức năng quan trọng giúp quy mô có lợi cho Trình kết xuất. “Renderer, please render this Shape” nghe có vẻ tự nhiên. Nhưng “A Shape, please have yourself rendered by this Renderer”, mặc dù có thể [và đã được thực hành] là một thiết kế yếu hơn nhiều và vòng lặp khả năng hiển thị mà nó đề xuất là dư thừa. Mặc dù Hình dạng cung cấp thông tin có giá trị, nhưng nếu không có Trình kết xuất, sẽ không có hình ảnh [đó là những gì chúng tôi đã ký hợp đồng để cung cấp]. Nhưng không có Hình dạng, vẫn sẽ có một hình ảnh - một hình ảnh trống rỗng. [Người ta có thể tranh luận về tính hữu ích của đầu ra này, tuy nhiên, người ta không thể phủ nhận sự tồn tại của nó]. Và nhớ lại rằng các chức năng được xác định chính xác bởi đầu ra của chúng [chứ không phải công việc ẩn bên trong để tạo ra nó]

2. Danh tính và loại của trình kết xuất là không đổi. Chúng tôi cần có [cùng] Trình kết xuất [bất kể đó là ai] trong toàn bộ hoạt động kết xuất. Nhưng những hình dạng mà nó thể hiện được thay thế. Vậy tại sao chúng ta không xé bỏ tấm màn che khỏi khuôn mặt của người kết xuất và để lộ ra con người thật của nó ngay từ đầu? . trong [1] khởi tạo chương trình, và sau đó [2], tùy thuộc vào cấu hình lại của người dùng]. Vì vậy, khi quá trình kết xuất bắt đầu, chỉ có một Trình kết xuất trong ngữ cảnh hiện tại [không thay đổi], kết xuất rất nhiều Hình dạng [thuộc bất kỳ loại Hình dạng nào]

Mẫu “Khách truy cập” giải quyết thách thức đa hình kép bất đối xứng này bằng cách gửi kép [đây là một giải pháp hướng đối tượng]. Hít một hơi thật sâu. Với Hình dạng hiện tại, Trình kết xuất yêu cầu nó chấp nhận nó [trình kết xuất], điều này làm cho Hình dạng [biết loại của chính nó] yêu cầu Trình kết xuất [bất kể đó là ai] kết xuất nó, chỉ định loại [của hình dạng] của nó

Và sau đó, theo thông lệ trong thế giới hướng đối tượng, sẽ tiến thêm một bước ngoài khả năng được yêu cầu rõ ràng [ở đây. kết xuất] và triển khai lớp cha "Khách truy cập" chung. Sự trừu tượng hơn nữa này cho chúng ta khả năng giới thiệu bất kỳ loại thao tác nào trên Hình dạng, với kết xuất là một trường hợp cụ thể. Nó không tốn tiền để làm điều này, và ai biết được?

Thí dụ

chú thích

  1. Nhập thư viện hình ảnh Python
  2. Phương thức Hình dạng phổ biến để “chấp nhận” [Khách truy cập này] xây dựng tên của phương thức của Khách truy cập phù hợp với loại [Hình dạng của Khách truy cập], tìm kiếm bộ chọn thông báo trong từ điển thuộc tính của Khách truy cập và gọi phương thức đó bằng cách sử dụng chính nó. [Trong Python, phương thức này đã được liên kết với Khách truy cập. ]
  3. Phương thức “lấy hộp giới hạn” của Hình dạng [cần cho Thư viện hình ảnh Python, nhưng phổ biến trong đồ họa] là trừu tượng và phải được triển khai [khác nhau] trong từng Hình dạng cụ thể
  4. Tên màu là tên được Thư viện hình ảnh Python mong đợi nhưng chung chung
  5. Việc triển khai Vòng kết nối cho hộp nhận giới hạn
  6. Triển khai Hình chữ nhật cho hộp nhận giới hạn
  7. Giao diện cho một Hình dạng chung “Khách truy cập”
  8. Một Shape Visitor cụ thể để hiển thị hình ảnh “png”
  9. Khách truy cập Hình dạng, được yêu cầu để vẽ một chuỗi các Hình dạng, chuyển tiếp từng Hình dạng để “ghé thăm” nó [Khách truy cập]
  10. Phương thức kết xuất Vòng tròn, mang tên chung "ghé thăm Vòng tròn" [trong Khách truy cập Hình dạng]
  11. Phương thức kết xuất Hình chữ nhật, mang tên chung "visit Circle" [trong Shape Visitor]

đầu ra

Mẫu "Khách truy cập" là một giải pháp không tầm thường cho một vấn đề không tầm thường. Giống như tất cả các cơ chế hạng nặng, nó không đến mà không phải trả tiền. Vì vậy, hãy chú ý đến những hạn chế sau do việc sử dụng nó gây ra

  1. Tất cả các thực thể có thể truy cập phải được biết trước. Bởi vì Khách truy cập chỉ định khả năng "truy cập" riêng biệt cho từng thực thể cụ thể và không có cách nào vượt qua nó. Điều này ít nhất là không thoải mái, trong các ngôn ngữ OO yêu cầu tất cả các phương thức được xác định bên trong lớp. [Tôi không hỏi điều đó ngụ ý gì đối với các ngôn ngữ cho phép — hoặc yêu cầu — các phương thức được thêm vào lớp từ bên ngoài, như C#, Rust và Go — hoan nghênh các nhận xét. ] Sự sắp xếp này thực sự phù hợp với các ứng dụng đồ họa, trong đó kho hình dạng thường được xác định rõ, dựa trên hình học và nhiều năm sử dụng thực tế, và dự kiến ​​sẽ không thay đổi. Ngược lại, mẫu “Khách truy cập” a-priori sẽ không phù hợp với — ví dụ — một hệ thống nhắn tin, trong đó kho các loại tin nhắn có thể được mở rộng một cách tự nhiên mà không cần thông báo trước. [Ở đó, một số cơ chế đăng ký gọi lại sắp xảy ra. ]
  2. Các khai báo dữ liệu liên quan đến mẫu Khách truy cập tạo ra một phụ thuộc không gian tên theo chu kỳ trong các ngôn ngữ được nhập mạnh, mời các giải pháp thay thế đáng ngờ như khai báo chuyển tiếp. Người ta có thể mong đợi vấn đề này không xuất hiện trong Python gõ vịt. Nhưng nếu bạn viết Python được ghi lại đúng cách bằng cách sử dụng các gợi ý kiểu, thì bạn phải xử lý sự phụ thuộc không gian tên tuần hoàn [ngay cả trong Python kiểu vịt], bởi e. g. , trích dẫn các loại được khai báo chuyển tiếp
  3. Và quan trọng nhất của tất cả. Mẫu Khách truy cập xuất phát từ miền giải pháp nhưng có thể phản tác dụng một sự hiểu biết mới về miền vấn đề. Điều này nghe có vẻ có vấn đề, nhưng chỉ là điều tự nhiên và phải được xem xét. Cách của Con người là thiết kế [trong số những thứ khác] bằng cách liên kết tự do, ngoằn ngoèo qua lại giữa các lĩnh vực vấn đề và giải pháp - phải làm gì và thực hiện nó như thế nào. [Ngược lại, hiểu thấu đáo từ vấn đề đến giải pháp, không bao giờ nhìn lại— e. g. , “mô hình thác nước” — là một giáo điều kỹ thuật ngây thơ không bao giờ hiệu quả với bất kỳ ai. ] Thông thường, chúng ta cần dự trữ đầy đủ các phương tiện có sẵn để quyết định chính xác những gì chúng ta có thể yêu cầu trên thực tế - kể tên một trong [nhiều] lý do. Ví dụ, xem xét việc giới thiệu một từ bằng cách nào đó thay đổi ý nghĩa của câu, chỉ để làm cho nó có vần điệu, tình cờ nhận ra chính xác điều mà chúng ta muốn nói trong suốt thời gian qua]. [Ở đâu gieo vần là phương tiện thực hiện của nhà thơ]. Cân nhắc việc bán một chiếc điện thoại thông minh chính thức cho một khách hàng yêu cầu điện thoại di động chỉ để gọi cho dì của cô ấy. Bạn có thể đang giúp đỡ cô ấy [khách hàng, không phải dì], khám phá ra rằng cô ấy thực sự cần một chiếc điện thoại thông minh như thế nào trong suốt những năm qua, hoặc một sự ô nhục - phải lướt qua nhiều menu và tùy chọn chỉ để xuất - hoặc trả lời - một cuộc gọi . Tóm tắt. Gợi ý suy nghĩ lại miền vấn đề đến từ bên dưới [e. g. một số trường hợp áp dụng các mẫu thiết kế xâm nhập, chẳng hạn như “Khách truy cập”] có thể trở thành một sự mặc khải hoặc phiền toái, tùy thuộc vào trường hợp. hãy cảnh giác

Do tính chất xâm nhập của nó, mẫu “Khách truy cập” sẽ là một giải pháp có thể chấp nhận được [và hiệu quả], trong đó mọi người đều đồng ý về miền vấn đề mở rộng. “Họ các thực thể kinh doanh này được tạo ra để thao túng [và mở rộng chức năng] từ bên ngoài, và đây là các trường hợp sử dụng. ” Điều thoạt đầu có vẻ giống như một giải pháp kỹ thuật cho một vấn đề kỹ thuật đã trở thành một khám phá

Nhưng khi không phải như vậy — phiên bản Khách truy cập chỉ là một tạo tác thiết kế được áp đặt cho vấn đề — sự phát triển bước vào một giai đoạn tồi tệ mà tôi định nghĩa là “sự bất hòa về công nghệ”. Các nhà phát triển đang bận giải quyết một vấn đề không phải là vấn đề mà họ đã ký hợp đồng để giải quyết [và do đó đang sống trong sự bất hòa về nhận thức], với tất cả các hình phạt bảo trì mà điều này mời gọi. Các yêu cầu mở rộng/sửa đổi sẽ đến từ một mô hình đối tượng và phải được ánh xạ tới — và từ — một mô hình đối tượng khác. Chúc may mắn

6. Biến thể mẫu khách truy cập "phân cấp"

Mẫu “Khách truy cập” phù hợp như một chiếc găng tay so với mẫu “Tổng hợp” đã được giới thiệu trong bài học trước. Để nhắc nhở, mẫu "Tổng hợp" đã đề xuất một mô hình đối tượng không tầm thường, kết hợp với giao diện, để cho phép thao tác cấu trúc đệ quy từ bên ngoài. Và mô hình "Khách truy cập" là về điều đó. thao tác với các đối tượng từ bên ngoài [với một số trợ giúp từ bên trong]. Vì vậy, sự kết hợp của Khách truy cập trên Tổng hợp - được gọi là mẫu "Khách truy cập phân cấp" là một lựa chọn khá tự nhiên. [Trên thực tế, điều đó rất tự nhiên, tôi đã từng vô tình đọc được một hướng dẫn có tên là “Mô hình khách truy cập” thực sự mô tả biến thể Phân cấp, không đề cập đến nguồn gốc không phân cấp của nó. ]

Xem xét cấu trúc liên kết Hình dạng toàn diện này, hoàn chỉnh với các loại hình dạng tổng hợp, một cách tự nhiên, theo mẫu “Tổng hợp”

Mô hình đối tượng này có một số loại Lá và phân cấp phụ Tổng hợp. Line, Circle và Square là lá. Multi-line và Multi-shape là vật liệu tổng hợp [trong đó Multi-line chuyên về Lines], cả hai đều kế thừa hành vi Composite từ hình dạng Compo trừu tượng

Tính kế thừa hạn chế của liên kết — trong “Nhiều dòng chỉ mở cho các Dòng” [mặc dù nó được xây dựng để chứa bất kỳ Hình dạng nào] — có vẻ phản trực giác nhưng lại phổ biến trong mô hình hóa đối tượng [bao thanh toán để sử dụng lại trong hệ thống phân cấp thừa kế . Nó không ảnh hưởng đến nguyên tắc thay thế [sẽ được thảo luận]. Việc kế thừa liên kết hoàn toàn không yêu cầu Đa dòng chứa các phiên bản của tất cả các loại Hình dạng có sẵn. Nó chỉ yêu cầu rằng bất cứ thứ gì xảy ra bên trong phải tuân theo giao diện Shape, và nó — tôi. e. Dòng - thực sự có

Tuy nhiên, một số sàng lọc sắp xảy ra. Hóa ra việc cho phép Khách truy cập vào hệ thống phân cấp tổng hợp yêu cầu một giao diện tham vọng hơn là chỉ chấp nhận/truy cập [ở đây, sẽ chỉ hoạt động đối với Lá]. Trong một mẫu hỗn hợp được thiết kế rõ ràng, Vật liệu tổng hợp không có gì riêng để hiển thị. Kết xuất một Hợp chất có nghĩa là lặp lại khi kết xuất từng thành phần bên trong [nếu là Hợp chất, sẽ lặp lại thêm, v.v. ]. Tất nhiên, chúng tôi cần một giao diện tổng quát hơn [và có thể thay thế phù hợp] hơn là mong đợi Khách truy cập hỏi xem Hình dạng hiện tại là Tổng hợp hay Lá và hành động tương ứng. Toàn bộ ý tưởng của mẫu “Khách truy cập” là chủ đề hướng dẫn khách truy cập truy cập vào nó [chủ đề] - chấp nhận/truy cập - không có câu hỏi đáng xấu hổ nào được đặt ra

Giải pháp là mở rộng giao diện của Khách truy cập phân cấp thành ba phương thức. [1] Phần còn lại của "Chuyến thăm" cho Lá. [2] “Truy cập bắt đầu” và [3] “kết thúc truy cập”, chỉ được cấp từ sự chấp nhận của Tổng hợp. Việc lặp lại trên mỗi thành phần [bên trong hỗn hợp] được thực hiện bởi hỗn hợp [chứ không phải khách truy cập], để duy trì giao diện này. Thông thường, “bắt đầu lượt truy cập” không hiển thị bất kỳ điều gì quan trọng [ngoài có lẽ là dấu phân cách bắt đầu ở dạng văn bản], mà thay vào đó chuẩn bị trạng thái để lặp lại thành cấp độ phụ và tương tự với “kết thúc lượt truy cập” [có thể xóa . Nhớ lại rằng "bắt đầu truy cập" không tiếp tục lặp lại bên trong. Điều này thuộc trách nhiệm của đối tượng Khách truy cập

7. Tập thể dục. Phác thảo với nhiều trình kết xuất

Chúng ta sẽ xây dựng một “Khách truy cập phân cấp” không tầm thường, dựa trên giải pháp sách giáo khoa cho bài tập chức năng kết xuất Tài liệu phác thảo từ Bài học thứ hai, được liệt kê bên dưới

chú thích

  1. “Thành phần phác thảo” không trừu tượng, bởi vì nó thực hiện hành vi mặc định
  2. Theo mặc định, "máy phát điện" này lặp lại 0 lần, đó là hành vi của Lá, đại diện cho sự trống rỗng. [Chúng ta không cần phân biệt giữa Lá và Hợp chất rỗng]. Phương thức này sẽ được ghi đè bằng phép lặp thích hợp bằng cách triển khai tổng hợp. Thử nghiệm vô nghĩa [“nếu sai”] là một cách giải quyết để làm cho Python xác định chức năng này là trình tạo [có tính năng “năng suất”, đừng bận tâm rằng nó không bao giờ được sử dụng]
  3. Theo mặc định, “getter” không trả về gì cả, đó là hành vi Composite. Nó sẽ bị ghi đè bởi việc triển khai Lá
  4. Ghi đè trình lấy lá trả về chuỗi ẩn bên trong
  5. Ghi đè trình lặp tổng hợp lặp lại một cách trung thực những gì bên trong
  6. Trình kết xuất truy vấn thành phần hiện tại cho “tiêu đề” của nó. Đây là một cách “lịch sự” [và có thể mở rộng] hơn là cố gắng tiết lộ xem đó là Lá hay Tổ hợp. Only Leaves có “tiêu đề” [đối với khối bên dưới, nếu có] — chuỗi ẩn bên trong

Các thách thức. Trong triển khai "thủ tục" hiện tại, trình kết xuất Tài liệu phác thảo là một hàm truy xuất trên cấu trúc Tổng hợp, đưa ra đầu ra văn bản "đánh số quân sự". Cấu trúc lại thiết kế này để hỗ trợ bất kỳ kiểu kết xuất nào, sử dụng mẫu “Khách truy cập phân cấp”, cung cấp cho người dùng [ít nhất là những kiểu này] ba kiểu để lựa chọn. [1] đánh số quân sự [logic trình kết xuất hiện tại], [2] XML, [3] sơ đồ phụ thuộc, sử dụng gói “graphviz” [hoặc gói khác]

Đây là mô hình đối tượng, làm nổi bật hộp thoại chấp nhận/truy cập

chương trình đầu vào

to develop software 
to analyze the requirements
to understand the needs
to model the problem domain
to design the solution
to select architecture
to make design decisions
to model the solution domain
to apply design patterns
to confirm fidelity
to implement the solution
to code the solution
to test the solution
to package the solution

Đầu ra số 1. đầu ra đánh số quân sự

1. to develop software 
1.1. to analyze the requirements
1.1.1. to understand the needs
1.1.2. to model the problem domain
1.2. to design the solution
1.2.1. to select architecture
1.2.1.1. to make design decisions
1.2.2. to model the solution domain
1.2.2.1. to apply design patterns
1.2.3. to confirm fidelity
1.3. to implement the solution
1.3.1. to code the solution
1.3.2. to test the solution
1.3.3. to package the solution

Đầu ra #2. đầu ra XML






























Đầu ra #3. Đầu ra đồ họa

Hướng dẫn render phong cách đồ họa

  1. Cài đặt ứng dụng GraphViz từ http. //www. graphviz. org/Download/ [Thư viện Python không sử dụng API C, mà gọi chương trình và hướng dẫn nó tạo ra một tệp]
  2. Đảm bảo chương trình nằm trong đường dẫn tìm kiếm thực thi của hệ điều hành. [Vì một số lý do, đây không phải là mặc định, ít nhất là trong trình cài đặt Windows]
  3. Cài đặt giao diện Python cho graphviz [e. g. “graphviz” — nhưng còn nhiều thứ nữa], sử dụng tiện ích pip tiêu chuẩn

Ví dụ sử dụng giao diện graphviz

Đầu ra [tệp “myDrawing. png"]

FYI, giao diện graphviz tạo [và chạy âm thầm] tệp nguồn graphviz này

digraph {
root
A
root -> A
B
root -> B
}

Đối với giải pháp sách giáo khoa, bấm vào liên kết này

Trước khi khởi hành, đây là lưu ý cuối cùng về việc đưa vào các mẫu thiết kế [hoặc đăng ký các thiết bị lập trình xâm nhập khác, chẳng hạn như khung Máy chủ/Máy khách/GUI của bên thứ ba, v.v. ] với tác dụng phụ là mở rộng miền vấn đề. Đối số sau đây thường bị bỏ qua, với kết quả thảm hại. Ngay cả khi tất cả mọi người [bao gồm cả khách hàng] đồng ý rằng chức năng cần thiết thực sự phải được mở rộng [bất kể xuất phát từ phản hồi triển khai hay nguồn khác], hãy tự hỏi. Đây có phải là liều thuốc chữa bách bệnh làm sẵn mà chúng tôi áp dụng cho miền có vấn đề thực sự là cách đúng đắn để làm điều đó không?

Các tác giả của mẫu "Khách truy cập" đã nghĩ đến một miền vấn đề nhất định và họ chỉ định các ví dụ đầy đủ để chứng minh điều đó. Nhưng đây có phải là phần mở rộng chính xác [của miền vấn đề] để phù hợp với các yêu cầu của ứng dụng mà bạn đang xây dựng không? . Bạn không thể biết được, cho đến khi bạn đã xem xét kỹ lưỡng các yêu cầu thực tế - chứ không phải áp đặt - và thử chúng với nguyên mẫu hoặc bất cứ điều gì cần thiết. Như một bài tập tỉnh táo, hãy cố gắng giải thích xung đột của bạn với khách hàng [nếu bạn đủ may mắn để giải thích]. Đáp lại, khách hàng có thể đưa ra các trường hợp sử dụng khác nhau có chức năng mở rộng và các mô hình đối tượng [ngụ ý]. Nhưng không chắc rằng một người dùng [không phải lập trình viên] sẽ nghĩ ra mẫu "Khách truy cập" mà không cần sự trợ giúp. Bởi vì nó phản trực giác, trong miền vấn đề. Khách hàng quan tâm đến việc phản hồi quá nhiều trường hợp sử dụng riêng biệt và khi bị thúc ép, có thể đưa ra rất nhiều lựa chọn thay thế riêng biệt. Ngược lại, khả năng một kích cỡ phù hợp với tất cả “để xử lý mọi phương án thay thế theo bất kỳ cách nào” [ma thuật mẫu thiết kế] là một thiết bị có lập trình giúp cuộc sống của người triển khai và người bảo trì trở nên thoải mái và giảm chi phí xây dựng. [So ​​sánh với các bộ phận xây dựng đúc sẵn trong công trình dân dụng]. Nhưng — trừ khi thực sự cần thiết — chỉ được chấp nhận miễn là có thể giữ cho người dùng minh bạch

Trong trường hợp ứng dụng "Phác thảo" trong ví dụ của chúng tôi, việc trả lại cho khách hàng cùng với tiết lộ là hợp pháp. “Mặc dù các yêu cầu ban đầu yêu cầu kiểu đầu ra Đánh số quân sự, chúng tôi cho rằng bạn có thể cần các kiểu bổ sung, chẳng hạn như trong các mẫu đính kèm này. May mắn thay, chúng tôi biết cách mở rộng mã một cách tầm thường để làm điều đó. Trong thực tế, nó sẽ dễ dàng hơn cho chúng tôi theo cách đó. ” Khách hàng có thể — hoặc có thể không — đồng ý rằng các kiểu bổ sung sắp xảy ra và tại sao lại như vậy [do đó mở rộng miền vấn đề]. Tuy nhiên, trước khi áp dụng mẫu “Khách truy cập”, hãy thực hiện một số phân tích [hoặc tạo mẫu] để đảm bảo rằng miền vấn đề do tác giả hình thành thực sự phù hợp với yêu cầu của bạn. Mặt khác, khi đến thời điểm gia hạn, bạn có thể thất vọng khi nhận ra rằng cơ sở hạ tầng đắt tiền của mình thực sự có thể được xây dựng thông minh để mở rộng — nhưng không phải tiện ích mở rộng cụ thể này

Và cuối cùng, nó cũng có thể chỉ ra rằng “Đánh số quân sự” thực sự là phong cách duy nhất mà chúng ta cần — e. g. đối với một tập lệnh không đáng kể — và bạn không nên lãng phí thời gian và tài nguyên của chúng tôi vào các thử nghiệm vô ích. [Trừ khi bạn có thể cắt/dán một giải pháp làm sẵn — giải pháp đó thực hiện âm thầm — trong vòng chưa đầy 60 giây]

7. Tiếp theo là gì?

Trong ba bài học đầu tiên, chúng ta đã được giới thiệu về giải pháp hướng đối tượng cho thách thức thiết kế về khả năng thay thế chức năng. mô hình Tin nhắn, liên quan đến việc chuyển một tin nhắn đến một đối tượng thuộc loại không xác định, được giải quyết trong thời gian chạy bằng một phương thức [theo tên đó] trong lớp của nó. Chúng tôi đã thấy cách "gõ vịt" của Python làm cho khả năng thay thế trở nên trực quan giữa bất kỳ loại nào không chính thức - nhưng về mặt ngữ nghĩa - có liên quan. Chúng tôi cũng đã được giới thiệu về cấu trúc đối tượng kế thừa và thấy nó hữu ích - mặc dù trong Python, không bắt buộc - cơ sở hạ tầng để đảm bảo khả năng thay thế và tính phổ biến của yếu tố [của giao diện, dữ liệu và triển khai] và tạo điều kiện thuận lợi cho phương sai [của việc triển khai]. Chúng tôi đã coi một số mẫu thiết kế đa hình [chủ yếu là “Tổng hợp” và “Khách truy cập”] làm ví dụ về thiết kế hướng đối tượng phù hợp, trong việc sử dụng khả năng thay thế và kế thừa của chúng để giải quyết các vấn đề thiết kế phổ biến. Cho đến nay, “vinh quang” của lập trình hướng đối tượng. Trong bài học tiếp theo, chúng ta sẽ thảo luận về một số mẫu thiết kế được sử dụng thường xuyên mà việc triển khai trong Python rất tầm thường [do cách gõ vịt và tính sẵn có của siêu dữ liệu đối tượng] khiến chúng trở nên dư thừa hoặc gần như dư thừa

Làm thế nào để thực hành đối tượng

Mục lục .
OOP Bài tập 1. Tạo một Lớp với các thuộc tính thể hiện
OOP Bài tập 2. Tạo một lớp Xe mà không có bất kỳ biến và phương thức nào
OOP Bài tập 3. Tạo một lớp con Bus sẽ kế thừa tất cả các biến và phương thức của lớp Xe
OOP Bài tập 4. Kế thừa lớp

Tại sao Python không hướng đối tượng 100?

Tuy nhiên, Python không phải là ngôn ngữ OOP xuyên suốt vì nó không cho phép đóng gói mạnh . Điều này là do người tạo ra nó, Guido van Rossum, muốn giữ mọi thứ đơn giản và điều đó có nghĩa là không che giấu dữ liệu theo nghĩa chặt chẽ nhất của thuật ngữ này.

Python có hướng đối tượng 100 phần trăm không?

Vâng, Python có phải là ngôn ngữ lập trình hướng đối tượng không? . Ngoại trừ luồng điều khiển, mọi thứ trong Python đều là đối tượng. Yes, it is. With the exception of control flow, everything in Python is an object.

OOP trong Python có khó không?

OOP Python rất khó .

Chủ Đề