Bản đồ các chức năng so với tuyên bố chuyển đổi


21

Tôi đang làm việc trên một dự án xử lý các yêu cầu và có hai thành phần cho yêu cầu: lệnh và các tham số. Trình xử lý cho mỗi lệnh rất đơn giản (<10 dòng, thường <5). Có ít nhất 20 lệnh và có khả năng sẽ có hơn 50 lệnh.

Tôi đã đưa ra một vài giải pháp:

  • một chuyển đổi lớn / if-other trên các lệnh
  • bản đồ các lệnh đến chức năng
  • ánh xạ các lệnh đến các lớp / singletons tĩnh

Mỗi lệnh thực hiện một kiểm tra lỗi nhỏ và bit duy nhất có thể được trừu tượng hóa là kiểm tra số lượng tham số, được xác định cho mỗi lệnh.

Điều gì sẽ là giải pháp tốt nhất cho vấn đề này, và tại sao? Tôi cũng mở cho bất kỳ mẫu thiết kế nào tôi có thể đã bỏ lỡ.

Tôi đã đưa ra danh sách pro / con sau đây cho mỗi:

công tắc điện

  • ưu
    • giữ tất cả các lệnh trong một chức năng; vì chúng đơn giản, điều này làm cho nó trở thành một bảng tra cứu trực quan
    • không cần làm lộn xộn nguồn với hàng tấn các hàm / lớp nhỏ sẽ chỉ được sử dụng ở một nơi
  • khuyết điểm
    • rất dài
    • khó thêm lệnh theo lập trình (cần xâu chuỗi bằng trường hợp mặc định)

lệnh ánh xạ -> chức năng

  • ưu
    • khối nhỏ, kích thước cắn
    • có thể thêm / xóa các lệnh theo chương trình
  • khuyết điểm
    • nếu được thực hiện trong dòng, tương tự trực quan như chuyển đổi
    • nếu không được thực hiện nội tuyến, rất nhiều chức năng chỉ được sử dụng ở một nơi

lệnh ánh xạ -> lớp tĩnh / singleton

  • ưu
    • có thể sử dụng đa hình để xử lý kiểm tra lỗi đơn giản (chỉ như 3 dòng, nhưng vẫn)
    • lợi ích tương tự với bản đồ -> giải pháp chức năng
  • khuyết điểm
    • rất nhiều lớp rất nhỏ sẽ làm lộn xộn dự án
    • thực hiện không phải tất cả ở cùng một nơi, vì vậy không dễ để quét các triển khai

Ghi chú thêm:

Tôi đang viết bài này bằng Go, nhưng tôi không nghĩ giải pháp đó là ngôn ngữ cụ thể. Tôi đang tìm kiếm một giải pháp tổng quát hơn bởi vì tôi có thể cần phải làm một cái gì đó rất giống với các ngôn ngữ khác.

Một lệnh là một chuỗi, nhưng tôi có thể dễ dàng ánh xạ nó thành một số nếu thuận tiện. Chữ ký hàm là một cái gì đó như:

Reply Command(List<String> params)

Go có các chức năng cấp cao nhất và các nền tảng khác mà tôi đang xem xét cũng có các chức năng cấp cao nhất, do đó có sự khác biệt giữa các tùy chọn thứ hai và thứ ba.


8
ánh xạ lệnh đến các chức năng và tải chúng khi chạy từ cấu hình. sử dụng mẫu lệnh.
Steven Evers

3
Đừng quá sợ tạo ra nhiều chức năng nhỏ. Thông thường, một tập hợp của một số chức năng nhỏ có thể duy trì nhiều hơn một chức năng lớn.
Bart van Ingen Schenau

8
Điều gì với thế giới lộn xộn này, nơi mọi người trả lời các câu hỏi trong các bình luận và hỏi thêm thông tin trong các câu trả lời?
pdr

4
@SteveEvers: Nếu nó không cần xây dựng, đó là một câu trả lời, bất kể ngắn đến đâu. Nếu có, và bạn không có thời gian hay bất cứ điều gì, thì hãy để nó cho người khác trả lời (luôn cảm thấy như gian lận để viết một câu trả lời xác nhận một nhận xét đã có nửa tá upvote). Cá nhân, tôi cảm thấy điều này cần sự công phu, OP thực sự muốn biết tại sao giải pháp tốt nhất là giải pháp tốt nhất.
pdr

1
@pdr - Phải rồi. Xu hướng của tôi là bản đồ các lệnh cho các chức năng, nhưng tôi là một lập trình viên tương đối trẻ trong khóa học thiết kế CS. Giáo sư của tôi thích rất nhiều lớp học, vì vậy có ít nhất 2 giải pháp hợp pháp. Tôi muốn biết yêu thích của cộng đồng.
beatgammit

Câu trả lời:


14

Đây là một sự phù hợp tuyệt vời cho bản đồ (giải pháp đề xuất thứ 2 hoặc thứ 3). Tôi đã sử dụng nó hàng chục lần, và nó đơn giản và hiệu quả. Tôi không thực sự phân biệt giữa các giải pháp này; điểm quan trọng là có một bản đồ với các tên hàm là các phím.

Ưu điểm chính của phương pháp bản đồ, theo tôi, là bảng là dữ liệu. Điều này có nghĩa là nó có thể được truyền xung quanh, tăng cường hoặc sửa đổi khi chạy; cũng dễ dàng mã hóa các chức năng bổ sung diễn giải bản đồ theo những cách mới, thú vị. Điều này sẽ không thể thực hiện được với giải pháp case / switch.

Tôi chưa thực sự trải nghiệm những nhược điểm mà bạn đề cập, nhưng tôi muốn đề cập đến một nhược điểm bổ sung: việc gửi đi rất dễ nếu chỉ có tên chuỗi, nhưng nếu bạn cần đưa thêm thông tin vào để quyết định thực hiện chức năng nào , nó sạch sẽ hơn rất nhiều.

Có lẽ tôi chưa bao giờ gặp phải một vấn đề đủ khó, nhưng tôi thấy ít giá trị trong cả mẫu lệnh và mã hóa công văn dưới dạng phân cấp lớp, như những người khác đã đề cập. Cốt lõi của vấn đề là ánh xạ các yêu cầu đến các chức năng; một bản đồ là đơn giản, rõ ràng và dễ kiểm tra. Một hệ thống phân cấp lớp đòi hỏi nhiều mã và thiết kế hơn, tăng sự ghép nối giữa mã đó và có thể buộc bạn phải đưa ra nhiều quyết định trả trước mà sau này bạn có thể sẽ cần phải thay đổi. Tôi không nghĩ rằng mô hình lệnh áp dụng ở tất cả.


4

Vấn đề của bạn cho vay rất độc đáo cho mẫu thiết kế Command . Vì vậy, về cơ bản, bạn sẽ có một Commandgiao diện cơ bản và sau đó sẽ có nhiều CommandImpllớp sẽ thực hiện giao diện đó. Giao diện về cơ bản chỉ cần có một phương thức duy nhất doCommand(Args args). Bạn có thể có các đối số được truyền qua một thể hiện của Argslớp. Bằng cách này, bạn tận dụng sức mạnh của đa hình thay vì các câu lệnh if / other cồng kềnh. Ngoài ra thiết kế này là dễ dàng mở rộng.


3

Bất cứ khi nào tôi thấy mình hỏi liệu tôi nên sử dụng câu lệnh chuyển đổi hay đa hình kiểu OO, tôi đề cập đến Bài toán biểu thức . Về cơ bản, nếu bạn có các "trường hợp" khác nhau cho dữ liệu và đũa phép của mình để hỗ trợ các "hành động" khác nhau (trong đó mỗi hành động làm điều gì đó khác nhau cho từng trường hợp) thì thực sự rất khó để tạo ra một hệ thống cho phép bạn thêm cả trường hợp mới và hành động mới trong tương lai.

Nếu bạn sử dụng câu lệnh chuyển đổi (hoặc mẫu Khách truy cập) thì thật dễ dàng để thêm các hành động mới (vì bạn muốn mọi thứ trong một chức năng) nhưng khó để thêm các trường hợp mới (vì bạn cần quay lại và chỉnh sửa các chức năng cũ)

Ngược lại, nếu bạn sử dụng đa hình kiểu OO, bạn có thể dễ dàng thêm các trường hợp mới (chỉ cần tạo một lớp mới) nhưng khó để thêm các phương thức vào một giao diện (vì sau đó bạn cần quay lại và chỉnh sửa một loạt các lớp)

Trong trường hợp của bạn, bạn chỉ có một phương thức mà bạn cần hỗ trợ (xử lý một yêu cầu) nhưng rất nhiều trường hợp có thể xảy ra (mỗi lệnh khác nhau). Vì việc dễ dàng thêm các trường hợp mới quan trọng hơn việc thêm các phương thức mới, chỉ cần tạo một lớp riêng cho mỗi lệnh khác nhau.


Nhân tiện, từ quan điểm về mức độ mở rộng của mọi thứ, nó không tạo ra sự khác biệt lớn cho dù bạn sử dụng các lớp hay hàm. Nếu chúng ta đang so sánh với một câu lệnh chuyển đổi thì vấn đề là làm thế nào mọi thứ được "gửi đi" và cả các lớp và hàm được gửi đi theo cùng một cách. Do đó, chỉ cần sử dụng bất cứ điều gì thuận tiện hơn trong ngôn ngữ của bạn (và vì Go có phạm vi và đóng từ vựng, sự khác biệt giữa các lớp và chức năng thực sự rất nhỏ).

Ví dụ: bạn thường có thể sử dụng ủy quyền để thực hiện phần kiểm tra lỗi thay vì dựa vào tính kế thừa (ví dụ của tôi là trong Javascript vì O không biết cú pháp Go, tôi hy vọng bạn không phiền)

function make_command(real_command){
    return function(x){
        if(check_arguments(x)){
            return real_command(x);
        }else{
            //handle error here
        }
    }
 }

 command1 = make_command(function(x){ 
     //do something
 })

 command2 = make_command(function(x){
     //do something else
 })

 command1(17);
 commnad2(42);

Tất nhiên, ví dụ này giả định rằng có một cách hợp lý để có hàm bao bọc hoặc các đối số kiểm tra lớp cha cho mọi trường hợp. Tùy thuộc vào cách mọi thứ diễn ra, việc đặt lệnh gọi tới check_argument bên trong các lệnh có thể đơn giản hơn (vì mỗi lệnh có thể cần gọi hàm kiểm tra với các đối số khác nhau, do số lượng đối số khác nhau, các loại lệnh khác nhau, v.v.)

tl; dr: Không có cách tốt nhất để giải quyết tất cả các vấn đề. Từ quan điểm "làm cho mọi thứ hoạt động", tập trung vào việc tạo ra sự trừu tượng của bạn theo cách thực thi các bất biến quan trọng và bảo vệ bạn khỏi những sai lầm. Từ quan điểm "chứng minh tương lai", hãy ghi nhớ những phần nào của mã có nhiều khả năng được mở rộng.


1

Tôi chưa bao giờ sử dụng go, với tư cách là lập trình viên ac #, tôi có thể sẽ đi xuống dòng sau, hy vọng kiến ​​trúc này sẽ phù hợp với những gì bạn đang làm.

Tôi sẽ tạo một lớp / đối tượng nhỏ cho mỗi hàm có hàm chính để thực thi, mỗi đối tượng nên biết biểu diễn chuỗi của nó. Điều này cung cấp cho bạn khả năng cắm mà nghe có vẻ như bạn muốn vì số lượng chức năng sẽ tăng lên. Lưu ý rằng tôi sẽ không sử dụng số liệu thống kê trừ khi bạn thực sự cần, chúng không mang lại nhiều lợi thế.

Sau đó tôi sẽ có một nhà máy biết cách khám phá các lớp này trong thời gian chạy để thay đổi chúng được tải từ các tổ hợp khác nhau, v.v. điều này có nghĩa là bạn không cần tất cả chúng trong cùng một dự án.

Do đó, cũng làm cho nó trở nên mô-đun hơn để thử nghiệm và nên làm cho mã đẹp và nhỏ hơn, dễ bảo trì hơn sau này.


0

Làm thế nào để bạn chọn lệnh / chức năng của bạn?

Nếu có một số cách "thông minh" để chọn chức năng chính xác thì đó là cách để đi - có nghĩa là bạn có thể thêm hỗ trợ mới (có thể trong thư viện bên ngoài) mà không cần sửa đổi logic cốt lõi.

Ngoài ra, kiểm tra các chức năng riêng lẻ dễ dàng hơn trong việc cách ly hơn là một câu lệnh chuyển đổi khổng lồ.

Cuối cùng, chỉ được sử dụng ở một nơi - bạn có thể thấy rằng một khi bạn nhận được tới 50 bit khác nhau của các chức năng khác nhau có thể được sử dụng lại không?


Các lệnh là các chuỗi duy nhất. Tôi có thể ánh xạ những số nguyên này nếu cần.
beatgammit

0

Tôi không biết Go hoạt động như thế nào, nhưng một kiến ​​trúc tôi đã sử dụng trong ActionScript là có một Danh sách liên kết chắc chắn hoạt động như một Chuỗi trách nhiệm. Mỗi liên kết có chức năng xác định lại (mà tôi đã triển khai dưới dạng gọi lại, nhưng bạn có thể viết riêng từng liên kết nếu hoạt động tốt hơn cho những gì bạn đang cố gắng thực hiện). Nếu một liên kết xác định nó có trách nhiệm, nó sẽ gọi đáp ứng Khả năng đáp ứng (một lần nữa, gọi lại) và điều đó sẽ chấm dứt chuỗi. Nếu nó không có trách nhiệm, nó sẽ chuyển yêu cầu đến liên kết tiếp theo trong chuỗi.

Các trường hợp sử dụng khác nhau có thể dễ dàng được thêm và xóa chỉ bằng cách thêm một liên kết mới giữa các liên kết của (hoặc ở cuối) chuỗi hiện có.

Điều này tương tự, nhưng khác biệt tinh tế với ý tưởng của bạn về bản đồ các chức năng. Điều tốt đẹp là bạn chỉ cần chuyển yêu cầu và nó thực hiện nó mà không cần bạn phải làm gì khác. Mặt trái là nó không hoạt động tốt nếu bạn cần trả về một giá trị.

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.