Các chức năng hạng nhất có thể thay thế cho mẫu Chiến lược không?


15

Mẫu thiết kế chiến lược thường được coi là sự thay thế cho các chức năng hạng nhất trong các ngôn ngữ thiếu chúng.

Vì vậy, ví dụ nói rằng bạn muốn truyền chức năng vào một đối tượng. Trong Java, bạn phải truyền vào đối tượng một đối tượng khác gói gọn hành vi mong muốn. Trong một ngôn ngữ như Ruby, bạn chỉ cần truyền chính chức năng đó dưới dạng một chức năng ẩn danh.

Tuy nhiên tôi đã suy nghĩ về nó và quyết định rằng có lẽ Chiến lược cung cấp nhiều hơn một chức năng ẩn danh đơn giản.

Điều này là do một đối tượng có thể giữ trạng thái tồn tại độc lập với thời kỳ khi phương thức của nó chạy. Tuy nhiên, một hàm ẩn danh tự nó chỉ có thể giữ trạng thái ngừng tồn tại vào thời điểm hàm kết thúc thực thi.

Trong một ngôn ngữ hướng đối tượng hỗ trợ các chức năng hạng nhất, mẫu chiến lược có bất kỳ lợi thế nào so với việc sử dụng các chức năng không?


10
"Tuy nhiên, một hàm ẩn danh tự nó chỉ có thể giữ trạng thái ngừng tồn tại vào thời điểm hàm kết thúc thực thi.": Điều này không đúng: một bao đóng có thể giữ trạng thái sống qua các lệnh khác nhau.
Giorgio

"Tuy nhiên, một hàm ẩn danh tự nó chỉ có thể giữ trạng thái ngừng tồn tại vào thời điểm hàm kết thúc thực thi." : không quên các biến toàn cục và các biến tĩnh ít nhất.
gbjbaanb

Câu trả lời:


13

Khi ngôn ngữ hỗ trợ các tham chiếu đến chức năng ( Java thực hiện từ phiên bản 8 ), đây thường là một lựa chọn tốt cho các chiến lược, vì chúng thường diễn đạt cùng một điều với ít cú pháp hơn. Tuy nhiên, có một số trường hợp một đối tượng thực sự có thể hữu ích.

Một giao diện Chiến lược có thể có nhiều phương thức. Hãy lấy một giao diện RouteFindingStragegylàm ví dụ, gói gọn các thuật toán tìm tuyến khác nhau. Nó có thể khai báo các phương thức như

  • Route findShortestRoute(Node start, Node destination)
  • boolean doesRouteExist(Node start, Node destination)
  • Route[] findAllPossibleRoutes(Node start, Node destination)
  • Route findShortestRouteToClosestDestination(Node start, Node[] destinations)
  • Route findTravelingSalesmanRoute(Node[] stations)

mà sau đó tất cả sẽ được thực hiện bởi chiến lược. Một số thuật toán tìm tuyến đường có thể cho phép tối ưu hóa nội bộ cho một số trường hợp sử dụng này và một số thuật toán có thể không, vì vậy người triển khai có thể quyết định cách triển khai từng phương thức này.

Một trường hợp khác là khi chiến lược có một trạng thái bên trong. Chắc chắn, trong một số ngôn ngữ, việc đóng cửa có thể có trạng thái bên trong, nhưng khi trạng thái bên trong này trở nên rất phức tạp, nó thường trở nên thanh lịch hơn để thúc đẩy việc đóng cửa thành một lớp đầy đủ.


2
"Khi trạng thái bên trong này trở nên rất phức tạp, nó thường trở nên hữu ích để thúc đẩy việc đóng cửa thành một lớp đầy đủ": Tại sao? Nếu trạng thái bên trong trở nên phức tạp, bạn cũng có thể đặt nó vào một đối tượng / bản ghi được lưu trữ bên trong bao đóng.
Giorgio

1
@Giorgio Nhưng sau đó, bạn sẽ có hai thực thể cú pháp để duy trì - bao đóng và lớp quản lý trạng thái bên trong của nó. Vì vậy, mã có thể được đơn giản hóa bằng cách di chuyển bao đóng sang lớp đó. Điều này có thể tốt hơn hoặc có thể không, tùy thuộc vào trường hợp sử dụng chính xác, ngôn ngữ lập trình và sở thích cá nhân.
Philipp

Có vẻ như một quan điểm hợp lý với tôi.
Giorgio

5

Không phải là một hàm ẩn danh chỉ có thể giữ trạng thái ngừng tồn tại khi hàm kết thúc thực thi.

Lấy ví dụ sau trong Common Lisp:

(defun number-strings (ss)
  (let ((counter 0))
    (mapcar #'(lambda (s) (format nil "~a: ~a" (incf counter) s)) ss)))

Hàm này lấy danh sách các chuỗi và đặt trước một bộ đếm cho từng thành phần của danh sách. Vì vậy, ví dụ, gọi

(number-strings '("a" "b" "c"))

cho

("1: a" "2: b" "3: c")

Hàm number-stringsbên trong sử dụng một hàm ẩn danh với một biến countergiữ trạng thái (giá trị hiện tại của bộ đếm) được sử dụng lại mỗi khi hàm được gọi.

Nói chung, bạn có thể nghĩ về một bao đóng như một đối tượng chỉ với một phương thức. Ngoài ra, một đối tượng là một tập hợp các bao đóng có chung các biến đóng. Vì vậy, tôi không chắc liệu có trường hợp nào bạn cần sử dụng một đối tượng thay vì đóng cửa hay không: tôi sẽ cho rằng cả hai đều là cách nhìn cùng một mô hình từ các quan điểm khác nhau.

Cụ thể, mẫu chiến lược yêu cầu một đối tượng chỉ có một phương thức, do đó, một bao đóng sẽ thực hiện công việc. Nhưng, như Philipp đã quan sát trong câu trả lời của mình, tùy thuộc vào hoàn cảnh (trạng thái phức tạp) và ngôn ngữ lập trình, bạn có thể có được một giải pháp thanh lịch hơn bằng cách sử dụng các đối tượng.


Vì vậy, trong một ngôn ngữ hỗ trợ các chức năng hạng nhất là đóng cửa, bạn có bao giờ sử dụng Chiến lược 'cổ điển' không?
Manila Cohn

1
Tôi có xu hướng đồng ý với Philipp: nó phụ thuộc vào ngôn ngữ và sở thích cá nhân. Tôi sẽ luôn chọn cách tiếp cận làm cho ký hiệu đơn giản nhất có thể. Ví dụ, trong Lisp tôi có thể định nghĩa trạng thái của mình là một danh sách các biến thông qua a letvà sau đó xác định đóng của tôi bên trong nó. Về cơ bản, tôi đã định nghĩa một đối tượng với một phương thức đang hoạt động. Trong một ngôn ngữ khác (ví dụ Java), có thể thuận tiện hơn (đơn giản hơn về mặt cú pháp) để xác định một đối tượng thích hợp để giữ trạng thái. Vì vậy, tôi sẽ quyết định từ trường hợp này sang trường hợp khác.
Giorgio

1

Chỉ vì hai thiết kế có thể giải quyết cùng một vấn đề không có nghĩa là chúng là sự thay thế trực tiếp cho nhau.

Nếu bạn cần theo dõi trạng thái trong một chương trình chức năng, bạn không biến đổi một biến đóng, ngay cả khi ngôn ngữ cho phép. Bạn sắp xếp để gọi một hàm lấy một trạng thái làm đối số và trả về trạng thái mới làm giá trị trả về của nó.

Kiến trúc của bạn sẽ trông rất khác biệt, nhưng bạn sẽ hoàn thành cùng một mục tiêu. Đừng cố ép các mô hình của một mô hình trực tiếp lên mô hình kia.


"Nếu bạn cần theo dõi trạng thái trong một chương trình chức năng, bạn không biến đổi một biến đóng, ngay cả khi ngôn ngữ cho phép.": Tôi là một người hâm mộ của phong cách chức năng thuần túy và tôi đồng ý với lời khuyên của bạn. Mặt khác, đóng cửa không chỉ là một cấu trúc chức năng, chứ chưa nói gì đến chức năng thuần túy. Ý tưởng đóng các biến từ bối cảnh từ vựng là trực giao với tính minh bạch / bất biến tham chiếu.
Giorgio

Xin lỗi, nhưng tôi không thể làm theo lập luận của bạn. Việc xử lý nhà nước trong lập trình chức năng thuần túy có liên quan gì đến câu hỏi trong tầm tay?
Philipp

1
Vấn đề là nếu bạn sử dụng một phần của mô hình chức năng, như các hàm hạng nhất, đừng ngạc nhiên nếu bạn cần kéo vào các phần khác của mô hình để làm cho nó hoạt động trơn tru.
Karl Bielefeldt

1

Chiến lược là một khái niệm , một công thức hữu ích để giải quyết một vấn đề cụ thể, định kỳ. Đây không phải là một cấu trúc ngôn ngữ, cũng không phải là về bất kỳ một hình thức thực hiện nào . Một đóng cửa có thể được sử dụng để thực hiện Chiến lược một ngày và Quan sát viên vào ngày hôm sau.

Các hạn Chiến lược là chủ yếu là hữu ích trong cuộc nói chuyện với các lập trình viên khác để xác bày tỏ ý định của bạn. Không có gì kỳ diệu về nó.


2
Câu hỏi đặc biệt đề cập đến mẫu thiết kế chiến lược có cấu trúc lớp cụ thể. Ý nghĩa khác của "chiến lược" là một kế hoạch hành động nhằm hoàn thành một mục tiêu cụ thể là không chính xác trong bối cảnh này.

Tôi có khuynh hướng đồng ý với @Snowman. Bạn có chắc là bạn đang nói về mô hình chiến lược ?
Rowan Freeman

1
@Snowman, ngay cả trang bạn đã liên kết không nêu chính xác cách triển khai mẫu này, thay vào đó họ đưa ra các ví dụ bằng các ngôn ngữ cụ thể, nhưng không có gì trong sơ đồ UML nói rằng tôi cần sử dụng kế thừa C ++, giao diện Java hoặc khối Ruby . Vì vậy, tôi vui lòng không đồng ý với phân tích của bạn.
ngốc nghếch
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.