Điều gì làm cho Iterator trở thành một mẫu thiết kế?


9

Tôi đã tự hỏi điều gì làm cho Iterator trở nên đặc biệt khi so sánh với các cấu trúc tương tự khác, và điều đó khiến Gang of Four liệt kê nó như một mẫu thiết kế.

Iterator dựa trên tính đa hình (một hệ thống phân cấp của các bộ sưu tập có giao diện chung) và phân tách các mối quan tâm (lặp qua các bộ sưu tập nên độc lập với cách cấu trúc dữ liệu).

Nhưng điều gì sẽ xảy ra nếu chúng ta thay thế hệ thống phân cấp của các bộ sưu tập, ví dụ, hệ thống phân cấp của các đối tượng toán học (số nguyên, float, phức tạp, ma trận, v.v.) và iterator bằng một lớp biểu diễn một số hoạt động liên quan trên các đối tượng này, ví dụ như các hàm năng lượng. Sơ đồ lớp sẽ giống nhau.

Chúng ta có thể có thể tìm thấy nhiều ví dụ tương tự như Nhà văn, Họa sĩ, Trình mã hóa và có thể là những ví dụ tốt hơn, hoạt động theo cùng một cách. Tuy nhiên tôi chưa bao giờ nghe thấy bất kỳ thứ nào trong số này được gọi là Mẫu thiết kế.

Vậy điều gì làm cho Iterator trở nên đặc biệt?

Có phải thực tế là nó phức tạp hơn vì nó đòi hỏi trạng thái có thể thay đổi để lưu trữ vị trí hiện tại trong bộ sưu tập? Nhưng sau đó, trạng thái đột biến thường không được coi là mong muốn.


Để làm rõ quan điểm của tôi, hãy để tôi đưa ra một ví dụ chi tiết hơn.

Đây là vấn đề thiết kế của chúng tôi:

Giả sử chúng ta có một hệ thống phân cấp các lớp và một hoạt động được xác định trên các đối tượng của các lớp này. Giao diện của hoạt động này là giống nhau cho mỗi lớp, nhưng việc triển khai có thể hoàn toàn khác nhau. Người ta cũng cho rằng nên áp dụng thao tác nhiều lần trên cùng một đối tượng, với các tham số khác nhau.

Đây là một giải pháp hợp lý cho vấn đề thiết kế của chúng tôi (thực tế là khái quát hóa mẫu lặp):

Để phân tách các mối quan tâm, không nên thêm các cài đặt của hoạt động như các hàm vào hệ thống phân cấp lớp ban đầu (các đối tượng toán hạng). Vì chúng ta muốn áp dụng thao tác nhiều lần trên cùng một toán hạng, nên nó được biểu diễn bằng một đối tượng giữ tham chiếu đến toán hạng, không chỉ bởi một hàm. Do đó, đối tượng toán hạng sẽ cung cấp một hàm trả về đối tượng đại diện cho hoạt động. Đối tượng này cung cấp một chức năng thực hiện các hoạt động thực tế.

Một ví dụ:

Có một lớp cơ sở hoặc giao diện MathObject(tên ngu ngốc, tôi biết, có lẽ ai đó có ý tưởng tốt hơn.) Với các lớp dẫn xuất MyIntegerMyMatrix. Đối với mỗi MathObjectthao tác Powernên được xác định cho phép tính toán hình vuông, hình khối, v.v. Vì vậy, chúng tôi có thể viết (bằng Java):

MathObject i = new MyInteger(5);
Power powerOfFive = i.getPower();
MyInteger square = powerOfFive.calculate(2); // should return 25
MyInteger cube = powerOfFive.calculate(3); // should return 125

5
"Sơ đồ lớp sẽ giống nhau" - vậy thì sao? Một mẫu thiết kế không phải là một sơ đồ lớp. Đây là một sự trừu tượng hóa ở mức độ cao đối với một lớp các giải pháp cho một vấn đề định kỳ.
Doc Brown

@DocBrown: Đúng, nhưng không phải là các hoạt động toán học, ghi các đối tượng vào một tệp, đầu ra đồ họa hoặc mã hóa các vấn đề định kỳ, giống như lặp đi lặp lại?
Frank Puffer

Sự lựa chọn của Mẫu thiết kế là chủ quan (tức là trong mắt của "nhà thiết kế", hoặc những người đánh giá thiết kế). Việc đặt tên cho các mẫu thiết kế được dự định là không xác định miền (để chúng ta không bị phân tâm khi nghĩ rằng nó là miền cụ thể). Chỉ là ý kiến ​​của tôi, tôi không có bất kỳ giới thiệu nào để trích dẫn.
rwong

@FrankPuffer Nếu bạn phác thảo một giải pháp chung để ghi các đối tượng vào một tệp, bạn có thể viết ra giải pháp của mình và gọi nó là Mẫu Viết đối tượng nếu làm như vậy là hữu ích.
Brandin

3
Bạn đang xem xét lại điều này. Mẫu thiết kế là một giải pháp nổi tiếng cho một vấn đề điện toán phổ biến, và đó là tất cả. Bạn sử dụng mô hình khi bạn có thể nhận ra và áp dụng những lợi ích mà nó cung cấp.
Robert Harvey

Câu trả lời:


9

Hầu hết các mẫu trong sách GoF đều có những điểm chung sau:

  • họ giải quyết các vấn đề thiết kế cơ bản , sử dụng các phương tiện hướng đối tượng
  • mọi người thường phải đối mặt với những vấn đề loại này trong các chương trình tùy ý, độc lập từ miền hoặc doanh nghiệp
  • chúng là các công thức để làm cho mã có thể tái sử dụng nhiều hơn, thường bằng cách làm cho nó RẮN hơn
  • họ trình bày các giải pháp canonic cho những vấn đề này

Các vấn đề được giải quyết theo các mẫu này rất cơ bản đến nỗi nhiều nhà phát triển hiểu chúng chủ yếu là giải pháp cho các tính năng ngôn ngữ lập trình bị thiếu , đó là IMHO một quan điểm hợp lệ (lưu ý rằng sách GoF là từ năm 1995, trong đó Java và C ++ không cung cấp nhiều tính năng như ngày nay).

Mẫu lặp rất phù hợp với mô tả này: nó giải quyết một vấn đề cơ bản xảy ra rất thường xuyên, độc lập với bất kỳ miền cụ thể nào và như bạn tự viết, đây là một ví dụ tốt cho "phân tách mối quan tâm". Như bạn chắc chắn biết, hỗ trợ iterator trực tiếp là thứ bạn tìm thấy trong rất nhiều ngôn ngữ lập trình hiện nay.

Bây giờ so sánh điều này với các vấn đề bạn đã chọn:

  • ghi vào một tệp - đó là IMHO đơn giản là không đủ "cơ bản". Đó là một vấn đề rất cụ thể. Cũng không có một giải pháp canonic tốt - có rất nhiều cách tiếp cận khác nhau để ghi vào một tập tin, và không có "thực hành tốt nhất" rõ ràng.
  • Họa sĩ, Bộ mã hóa: bất cứ điều gì bạn có trong đầu với điều đó, những vấn đề đó trông thậm chí ít cơ bản hơn đối với tôi và thậm chí không độc lập với miền.
  • có chức năng "sức mạnh" có sẵn cho các loại đối tượng khác nhau: thoạt nhìn, có thể đáng để nuôi một mô hình, nhưng giải pháp đề xuất của bạn không thuyết phục được tôi - nó trông giống như một nỗ lực để biến chức năng năng lượng thành một thứ tương tự mô hình lặp. Tôi đã triển khai rất nhiều mã với các tính toán kỹ thuật, nhưng tôi không thể nhớ một tình huống trong đó một cách tiếp cận tương tự như đối tượng chức năng sức mạnh của bạn sẽ giúp tôi (tuy nhiên, các trình vòng lặp là thứ tôi phải xử lý hàng ngày).

Hơn nữa, tôi không thấy bất cứ điều gì trong ví dụ về chức năng sức mạnh của bạn mà không thể hiểu là một ứng dụng của mẫu chiến lược hoặc mẫu lệnh, có nghĩa là những phần cơ bản đó đã có trong sách GoF. Một giải pháp tốt hơn có thể chứa các phương thức mở rộng hoặc mở rộng toán tử, nhưng đó là những thứ phải tuân theo các tính năng ngôn ngữ và đó chính xác là những gì "OO có nghĩa" được sử dụng bởi "Gang" không thể cung cấp.


The problems solved by these patterns are so basic that many developers think their main purpose is to be workarounds for missing programming language features- Điều trớ trêu là các nhà phát triển phần mềm thường xuyên sử dụng các mẫu thiết kế phần mềm đã 20 năm tuổi trong khi vẫn tin rằng họ đang viết mã hiện đại.
Robert Harvey

@RobertHarvey: Tôi không nghĩ nhiều nhà phát triển sẽ triển khai mẫu lặp trong ngày hôm nay theo "cách OO" được đề xuất bởi GoF. Họ thực hiện nó thông thường bằng các phương tiện được cung cấp bởi ngôn ngữ hoặc đó là lib tiêu chuẩn (ví dụ: trong C # bằng cách sử dụng IEnumerableyield). Nhưng đối với các mẫu GoF khác, những gì bạn viết có thể đúng.
Doc Brown

1
Có liên quan đến workarounds for missing programming language features: blog.plover.com/prog/johnson.html
jrw32982 hỗ trợ Monica

8

Gang of Four trích dẫn định nghĩa mẫu của Christopher Alexandeller:

Mỗi mẫu mô tả một vấn đề xảy ra lặp đi lặp lại trong môi trường của chúng tôi và sau đó mô tả cốt lõi của giải pháp cho vấn đề đó

Vấn đề được giải quyết bởi các vòng lặp là gì?

Ý định: Cung cấp một cách để truy cập các phần tử của một đối tượng tổng hợp một cách tuần tự mà không để lộ đại diện cơ bản của nó.

Giáo dục

Khả năng áp dụng: Sử dụng mẫu Iterator

  • để truy cập nội dung của đối tượng tổng hợp mà không để lộ đại diện bên trong của đối tượng
  • để hỗ trợ nhiều di chuyển của các đối tượng tổng hợp
  • để cung cấp một giao diện thống nhất để duyệt qua các cấu trúc tổng hợp khác nhau (nghĩa là để hỗ trợ phép lặp đa hình).

Vì vậy, người ta có thể lập luận rằng mẫu lặp là theo định nghĩa miền cụ thể cho các bộ sưu tập. Và điều đó là hoàn toàn OK. Các mẫu khác như mẫu trình thông dịch là miền cụ thể cho các ngôn ngữ dành riêng cho miền, các mẫu xuất xưởng là miền cụ thể để tạo đối tượng, khắc. Tất nhiên đây là một sự hiểu biết khá ngớ ngẩn về tên miền cụ thể của tên miền. Miễn là đó là một cặp giải pháp vấn đề tái diễn, chúng ta có thể gọi nó là một mẫu.

Và thật tốt khi mẫu Iterator tồn tại. Những điều tồi tệ sẽ xảy ra nếu bạn không sử dụng nó. Ví dụ chống yêu thích của tôi là Perl. Ở đây, mỗi bộ sưu tập (mảng hoặc hàm băm) bao gồm trạng thái lặp như là một phần của bộ sưu tập. Tại sao điều này là xấu? Chúng ta có thể dễ dàng lặp lại một hàm băm với một vòng lặp trong mỗi vòng:

while (my ($key, $value) = each %$hash) {
  say "$key => $value";
}

Nhưng nếu chúng ta gọi một hàm trong thân vòng lặp thì sao?

while (my ($key, $value) = each %$hash) {
  do_something_with($key, $value, $hash);
}

Chức năng này bây giờ có thể làm khá nhiều bất cứ điều gì nó muốn, ngoại trừ:

  • thêm hoặc xóa các mục băm, vì những mục này sẽ thay đổi thứ tự lặp không thể đoán trước (trong C ++ - nói, những thứ này sẽ làm mất hiệu lực của trình lặp).
  • Lặp lại cùng một bảng băm mà không thực hiện một bản sao, vì điều đó sẽ tiêu thụ cùng một trạng thái lặp. Giáo sư.

Nếu hàm được gọi nên sử dụng iterator, hành vi của vòng lặp của chúng ta sẽ không được xác định. Đó là vấn đề. Và mẫu lặp có một giải pháp: đặt tất cả trạng thái lặp trong một đối tượng riêng biệt được tạo ra mỗi lần lặp.

Vâng, tất nhiên mẫu lặp có liên quan đến các mẫu khác. Ví dụ, iterator được khởi tạo như thế nào? Trong Java, chúng ta có một giao diện chung Iterable<T>Iterator<T>giao diện. Một trình lặp cụ thể như ArrayList<T>tạo ra một loại trình vòng lặp cụ thể, trong khi đó HashSet<T>có thể cung cấp một kiểu trình vòng lặp hoàn toàn khác. Điều đó nhắc nhở tôi rất nhiều về mô hình nhà máy trừu tượng, trong đó Iterable<T>nhà máy trừu tượng và Iteratorsản phẩm là sản phẩm.

Một trình lặp đa hình cũng có thể được hiểu là một ví dụ về mẫu chiến lược. Ví dụ, một cây có thể cung cấp các loại trình vòng lặp khác nhau (đặt hàng trước, theo thứ tự, sau khi đặt hàng, trên sàn). Bên ngoài, tất cả sẽ chia sẻ một giao diện lặp và mang lại các yếu tố theo trình tự. Mã khách hàng chỉ cần phụ thuộc vào giao diện iterator, không phụ thuộc vào bất kỳ thuật toán duyệt cây cụ thể nào.

Các mẫu không tồn tại trong sự cô lập, độc lập với nhau. Một số mẫu là các giải pháp khác nhau cho cùng một vấn đề và một số mẫu mô tả cùng một giải pháp trong các bối cảnh khác nhau. Một số mẫu ngụ ý khác. Ngoài ra, không gian mẫu không bị đóng khi bạn lật trang cuối cùng của cuốn sách Mẫu thiết kế (xem thêm câu hỏi trước đó của bạn Có phải Gang of Four đã khám phá kỹ lưỡng về mô hình không gian mô hình không? ). Các mẫu được mô tả trong sách Mẫu thiết kế rất linh hoạt và rộng, mở cho sự biến đổi vô hạn và chắc chắn không phải là mẫu duy nhất tồn tại.

Các khái niệm bạn liệt kê (viết, vẽ, mã hóa) không phải là các mẫu vì chúng không mô tả sự kết hợp giải pháp vấn đề. Một nhiệm vụ như của tôi, tôi cần phải viết dữ liệu, hay tôi cần mã hóa dữ liệu, thực sự không phải là vấn đề thiết kế và không bao gồm giải pháp; một giải pháp của người dùng mà chỉ bao gồm những người mà tôi biết, tôi sẽ tạo ra một lớp Nhà văn là vô nghĩa. Nhưng nếu chúng ta gặp vấn đề như, tôi không muốn vẽ đồ họa một nửa trên màn hình, thì có thể tồn tại một mô hình: Tôi biết, tôi sẽ sử dụng đồ họa hai lớp!


NIce trả lời, cảm ơn. Vẫn không hoàn toàn thuyết phục rằng những gì bạn viết trong đoạn cuối áp dụng ở đây. Tôi đã chỉnh sửa câu hỏi của tôi để giải thích những gì tôi có ý nghĩa.
Frank Puffer
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.