Làm thế nào để tránh quá tải phương pháp?


16

Chúng tôi có khá nhiều vị trí trong mã nguồn của ứng dụng của chúng tôi, trong đó một lớp có nhiều phương thức có cùng tên và các tham số khác nhau. Các phương thức đó luôn có tất cả các tham số của phương thức 'trước' cộng thêm một tham số.

Đó là kết quả của quá trình tiến hóa dài (mã kế thừa) và suy nghĩ này (tôi tin):

" Có một phương thức M thực hiện điều A. Tôi cần thực hiện A + B. OK, tôi biết ... Tôi sẽ thêm một tham số mới cho M, tạo một phương thức mới cho điều đó, chuyển mã từ M sang phương thức mới có thêm một tham số, thực hiện A + B ở đó và gọi phương thức mới từ M với giá trị mặc định của tham số mới. "

Đây là một ví dụ (bằng ngôn ngữ giống như Java):

class DocumentHome {

  (...)

  public Document createDocument(String name) {
    // just calls another method with default value of its parameter
    return createDocument(name, -1);
  }

  public Document createDocument(String name, int minPagesCount) {
    // just calls another method with default value of its parameter
    return createDocument(name, minPagesCount, false);
  }

  public Document createDocument(String name, int minPagesCount, boolean firstPageBlank) {
    // just calls another method with default value of its parameter
    return createDocument(name, minPagesCount, false, "");
  }

  public Document createDocument(String name, int minPagesCount, boolean firstPageBlank, String title) {
    // here the real work gets done
    (...)
  }

  (...)
}

Tôi cảm thấy như thế này là sai. Không chỉ chúng ta không thể tiếp tục thêm các tham số mới như thế này mãi mãi, mà mã khó mở rộng / thay đổi vì tất cả các phụ thuộc giữa các phương thức.

Dưới đây là một số cách để làm điều này tốt hơn:

  1. Giới thiệu một đối tượng tham số:

    class DocumentCreationParams {
    
      String name;
      int minPagesCount;
      boolean firstPageBlank;
      String title;
    
      (...)
    }
    
    class DokumentHome {
    
      public Document createDocument(DocumentCreationParams p) {
        // here the real work gets done
        (...)
      }
    }
    
  2. Đặt tham số cho DocumentHomeđối tượng trước khi chúng ta gọicreateDocument()

      @In
      DocumentHome dh = null;
    
      (...)
    
      dh.setName(...);
      dh.setMinPagesCount(...);
      dh.setFirstPageBlank(...);
    
      Document newDocument = dh.createDocument();
    
  3. Tách công việc thành các phương thức khác nhau và gọi chúng khi cần thiết:

      @In
      DocumentHome dh = null;
    
      Document newDocument = dh.createDocument();
      dh.changeName(newDocument, "name");
      dh.addFirstBlankPage(newDocument);
      dh.changeMinPagesCount(new Document, 10);
    

Những câu hỏi của tôi:

  1. Là vấn đề được mô tả thực sự là một vấn đề?
  2. Bạn nghĩ gì về các giải pháp được đề xuất? Bạn thích cái nào hơn (dựa trên kinh nghiệm của bạn)?
  3. Bạn có thể nghĩ ra giải pháp nào khác không?

1
Ngôn ngữ nào bạn đang nhắm mục tiêu hoặc nó chỉ là sth tướng?
Knerd

Không có ngôn ngữ cụ thể, chỉ chung chung. Vui lòng chỉ ra cách các tính năng trong các ngôn ngữ khác có thể giúp với điều này.
Ytus

như tôi đã nói ở đây, lập trình viên.stackexchange.com/questions/235096/ C C và C ++ có một số tính năng.
Knerd

Rõ ràng là câu hỏi này áp dụng cho mọi ngôn ngữ hỗ trợ loại quá tải phương thức như vậy.
Doc Brown

1
@DocBrown ok, nhưng không phải ngôn ngữ nào cũng hỗ trợ các lựa chọn thay thế giống nhau;)
Knerd

Câu trả lời:


20

Có thể thử mô hình xây dựng ? (lưu ý: kết quả Google khá ngẫu nhiên :)

var document = new DocumentBuilder()
                   .FirstPageBlank()
                   .Name("doc1final(2).doc")
                   .MinimumNumberOfPages(4)
                   .Build();

Tôi không thể đưa ra một danh sách đầy đủ về lý do tại sao tôi thích trình xây dựng hơn các tùy chọn bạn đưa ra, nhưng bạn đã xác định được một vấn đề lớn với rất nhiều mã. Nếu bạn nghĩ rằng bạn cần nhiều hơn hai tham số cho một phương thức thì có thể mã của bạn bị cấu trúc sai (và một số sẽ tranh luận một!).

Vấn đề với một đối tượng params là (trừ khi đối tượng bạn tạo theo cách nào đó có thật), bạn chỉ cần đẩy vấn đề lên một mức, và bạn kết thúc với một cụm các tham số không liên quan tạo thành 'đối tượng'.

Các nỗ lực khác của bạn trông giống như một người nào đó tiếp cận với mẫu xây dựng nhưng không hoàn toàn đạt được điều đó :)


Cảm ơn bạn. Câu trả lời của bạn có thể là giải pháp num. 4, vâng. Tôi đang tìm kiếm nhiều câu trả lời hơn theo cách này: 'Theo kinh nghiệm của tôi, điều này dẫn đến ... và có thể được sửa chữa ....' hoặc 'Khi tôi thấy điều này trong mã của đồng nghiệp của mình, tôi đề nghị anh ta ... thay vào đó.'
Ytus

Tôi không biết ... dường như với tôi rằng bạn chỉ đang chuyển vấn đề. Ví dụ, nếu tôi có thể xây dựng một tài liệu trên các phần khác nhau của ứng dụng, tốt hơn hết là tổ chức và kiểm tra việc cách ly việc xây dựng tài liệu này trên một lớp riêng biệt (như sử dụng a DocumentoFactory). Có một builderđịa điểm khác nhau, khó kiểm soát các thay đổi trong tương lai khi xây dựng Tài liệu (như thêm trường bắt buộc mới vào tài liệu, ví dụ) và thêm mã soạn sẵn trong các bài kiểm tra để đáp ứng nhu cầu của người xây dựng tài liệu trên các lớp đang sử dụng trình xây dựng.
Dherik

1

Sử dụng một đối tượng tham số là một cách tốt để tránh quá tải (quá mức) các phương thức:

  • nó làm sạch mã
  • tách dữ liệu từ chức năng
  • làm cho mã dễ bảo trì hơn

Tôi sẽ, tuy nhiên, không đi quá xa với nó.

Có một quá tải ở đây và không có một điều xấu. Nó được hỗ trợ bởi ngôn ngữ lập trình, vì vậy hãy sử dụng nó để lợi thế của bạn.

Tôi đã không biết về mẫu xây dựng, nhưng đã sử dụng nó "một cách tình cờ" trong một vài lần. Áp dụng tương tự ở đây: đừng lạm dụng nó. Mã trong ví dụ của bạn sẽ được hưởng lợi từ nó, nhưng dành nhiều thời gian để thực hiện nó cho mọi phương thức có một phương thức quá tải duy nhất không hiệu quả lắm.

Chỉ 2 xu của tôi.


0

Tôi thực sự không thấy một vấn đề lớn với mã. Trong C # và C ++, bạn có thể sử dụng các tham số tùy chọn, đó sẽ là một lựa chọn thay thế, nhưng theo tôi biết Java không hỗ trợ loại tham số đó.

Trong C #, bạn có thể đặt tất cả các quá tải riêng tư và một phương thức với các tham số tùy chọn được công khai để gọi nội dung.

Để trả lời câu hỏi của bạn phần 2, tôi sẽ lấy đối tượng tham số hoặc thậm chí là từ điển / HashMap.

Thích như vậy:

class DokumentHome {

  public Document createDocument(Map<string, object> params) {
    if (params.containsKey("yourkey")) {
       // do that
    }
    // here the real work gets done
    (...)
  }
}

Từ chối trách nhiệm, trước tiên tôi là lập trình viên C # và JavaScript và sau đó là lập trình viên Java.


4
Đó là một giải pháp, nhưng tôi không nghĩ đó là một giải pháp tốt (ít nhất, không phải trong bối cảnh mà sự an toàn được mong đợi).
Doc Brown

Đúng rồi. Do các trường hợp như vậy, tôi thích các phương thức quá tải hoặc các tham số tùy chọn.
Knerd

2
Sử dụng từ điển làm tham số là một cách dễ dàng để giảm số lượng tham số rõ ràng cho một phương thức, nhưng nó che khuất các phụ thuộc thực tế của phương thức. Điều này buộc người gọi phải tìm nơi khác để biết chính xác những gì cần có trong từ điển, cho dù đó là trong các bình luận, các cuộc gọi khác cho phương thức hoặc chính việc thực hiện phương thức.
Mike Partridge

Chỉ sử dụng từ điển cho các tham số thực sự tùy chọn để các trường hợp sử dụng cơ bản không cần đọc quá nhiều tài liệu.
dùng949300

0

Tôi nghĩ rằng đây là một ứng cử viên tốt cho mô hình xây dựng. Mẫu Builder rất hữu ích khi bạn muốn tạo các đối tượng cùng loại, nhưng với các cách biểu diễn khác nhau.

Trong trường hợp của bạn, tôi sẽ có một trình xây dựng với các phương thức sau:

SetTitle (Document document) { ... }
SetFirstPageBlank (Document document) { ... }
SetMinimumPageCount (Document document) { ... }

Sau đó, bạn có thể sử dụng trình xây dựng theo cách sau:

_builder.SetTitle(document);
_builder.SetFirstPageBlank(document);

Mặt khác, tôi không bận tâm đến một số tình trạng quá tải đơn giản - cái quái gì, .NET framework sử dụng chúng ở mọi nơi với các trình trợ giúp HTML. Tuy nhiên, tôi sẽ đánh giá lại những gì tôi đang làm nếu tôi phải truyền nhiều hơn hai tham số cho mọi phương thức.


0

Tôi nghĩ rằng buildergiải pháp có thể hoạt động trên hầu hết các kịch bản, nhưng trong các trường hợp phức tạp hơn, trình xây dựng của bạn cũng sẽ phức tạp để thiết lập , bởi vì nó dễ dàng phạm một số lỗi theo thứ tự của các phương thức, phương thức nào cần được phơi bày hay không , v.v ... Vì vậy, nhiều người trong chúng ta sẽ thích một giải pháp đơn giản nhất.

Nếu bạn chỉ tạo một trình xây dựng đơn giản để xây dựng một tài liệu và truyền bá mã này trên các phần (các lớp) khác nhau của ứng dụng, thì sẽ khó:

  • tổ chức : bạn sẽ có nhiều lớp xây dựng một tài liệu theo nhiều cách khác nhau
  • duy trì : mọi thay đổi về khởi tạo tài liệu (như thêm một yêu cầu bắt buộc mới) sẽ đưa bạn đến Phẫu thuật Shotgun
  • kiểm tra : nếu bạn đang kiểm tra một classe xây dựng một tài liệu, bạn sẽ cần thêm mã soạn sẵn chỉ để đáp ứng việc khởi tạo tài liệu.

Nhưng điều này không trả lời câu hỏi OP.

Thay thế cho quá tải

Một số lựa chọn thay thế:

  • Đổi tên tên phương pháp : nếu tên cùng một phương pháp là tạo ra một số nhầm lẫn, hãy thử đổi tên các phương pháp để tạo ra một tên có ý nghĩa hơn createDocument, như: createLicenseDriveDocument, createDocumentWithOptionalFields, vv Tất nhiên, điều này có thể dẫn bạn đến tên phương pháp khổng lồ, vì vậy đây không phải là một giải pháp cho tất cả các trường hợp.
  • Sử dụng phương pháp tĩnh . Cách tiếp cận này là loại tương tự nếu so với phương án đầu tiên ở trên. Bạn có thể sử dụng một tên có ý nghĩa cho từng trường hợp và khởi tạo tài liệu từ một phương thức tĩnh trên Document, như : Document.createLicenseDriveDocument().
  • Tạo một giao diện chung : bạn có thể tạo một phương thức duy nhất được gọi createDocument(InterfaceDocument interfaceDocument)và tạo các triển khai khác nhau cho InterfaceDocument. Ví dụ : createDocument(new DocumentMinPagesCount("name")). Tất nhiên, bạn không cần một triển khai duy nhất cho từng trường hợp, bởi vì bạn có thể tạo nhiều hơn một hàm tạo trên mỗi triển khai, nhóm một số trường có ý nghĩa đối với triển khai đó. Mẫu này được gọi là các nhà xây dựng kính thiên văn .

Hoặc chỉ cần ở lại với giải pháp quá tải . Thậm chí, đôi khi, một giải pháp xấu xí, không có nhiều hạn chế khi sử dụng nó. Trong trường hợp đó, tôi thích sử dụng các phương thức quá tải trên một lớp riêng biệt, giống như một phương thức DocumentoFactorycó thể được thêm vào như là sự phụ thuộc vào các lớp cần tạo tài liệu. Tôi có thể tổ chức và xác thực các trường mà không cần phức tạp để tạo một trình xây dựng tốt và duy trì mã ở một nơi duy nhất.

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.