Truyền đối tượng hai lần cho cùng một phương thức hoặc hợp nhất với giao diện kết hợp?


15

Tôi có một phương pháp tạo tệp dữ liệu sau khi nói chuyện với bảng kỹ thuật số:

CreateDataFile(IFileAccess boardFileAccess, IMeasurer boardMeasurer)

Ở đây boardFileAccessboardMeasurerlà cùng một thể hiện của một Boardđối tượng thực hiện cả hai IFileAccessIMeasurer. IMeasurerđược sử dụng trong trường hợp này cho một phương thức duy nhất sẽ đặt một chân trên bảng hoạt động để thực hiện phép đo đơn giản. Dữ liệu từ phép đo này sau đó được lưu trữ cục bộ trên bảng bằng cách sử dụng IFileAccess. Boardnằm trong một dự án riêng biệt

Tôi đã đi đến kết luận rằng CreateDataFileđang thực hiện một điều bằng cách thực hiện phép đo nhanh và sau đó lưu trữ dữ liệu và thực hiện cả hai trong cùng một phương pháp sẽ trực quan hơn cho người khác sử dụng mã này sau đó phải thực hiện phép đo và ghi vào tệp như các cuộc gọi phương thức riêng biệt.

Đối với tôi, có vẻ khó xử khi truyền cùng một đối tượng cho một phương thức hai lần. Tôi đã xem xét thực hiện một giao diện địa phương IDataFileCreatormà sẽ mở rộng IFileAccessIMeasurervà sau đó có một thực hiện có chứa mộtBoard ví dụ mà chỉ cần sẽ gọi cần Boardphương pháp. Xem xét rằng cùng một đối tượng bảng sẽ luôn được sử dụng để đo lường và ghi tệp, có phải là một thực tế xấu khi truyền cùng một đối tượng cho một phương thức hai lần? Nếu vậy, sử dụng một giao diện cục bộ và thực hiện một giải pháp thích hợp?


2
Thật khó để không thể thực hiện ý định mã của bạn từ tên bạn đang sử dụng. Một giao diện có tên IDataFileCreator đang được truyền cho một phương thức có tên là CreatDataFile đang gây chú ý. Họ đang cạnh tranh cho trách nhiệm để duy trì dữ liệu? Lớp nào là CreatDataFile là một phương thức của anyway? Đo lường không có gì để làm với dữ liệu dai dẳng, rất nhiều là rõ ràng. Câu hỏi của bạn không phải là về vấn đề lớn nhất bạn gặp phải với mã của mình.
Martin Maat

Có bao giờ có thể hiểu được rằng đối tượng truy cập tệp của bạn và đối tượng đo của bạn có thể là hai đối tượng khác nhau không? Tôi sẽ nói có. Nếu bạn thay đổi ngay bây giờ, bạn sẽ phải thay đổi lại trong phiên bản 2 hỗ trợ thực hiện các phép đo trên mạng.
dùng253751

2
Đây là một câu hỏi khác - tại sao đầu tiên các đối tượng truy cập và đo lường tệp dữ liệu giống nhau?
dùng253751

Câu trả lời:


40

Không, điều này là hoàn toàn tốt. Điều đó chỉ có nghĩa là API được thiết kế quá mức liên quan đến ứng dụng hiện tại của bạn .

Nhưng điều đó không chứng minh rằng sẽ không bao giờ có trường hợp sử dụng trong đó nguồn dữ liệu và bộ đo khác nhau. Quan điểm của API là cung cấp các khả năng lập trình viên ứng dụng, không phải tất cả chúng sẽ được sử dụng. Bạn không nên hạn chế một cách giả tạo những gì người dùng API có thể làm trừ khi điều đó làm phức tạp API để mức độ dễ hiểu của mạng bị giảm.


7

Đồng ý với câu trả lời của @ KilianFoth rằng điều này là hoàn toàn tốt.

Tuy nhiên, nếu bạn muốn, bạn có thể tạo một phương thức lấy một đối tượng thực hiện cả hai giao diện:

public object CreateDataFile<T_BoardInterface>(
             T_BoardInterface boardInterface
    )
    where T_BoardInterface : IFileAccess, IMeasurer
{
    return CreateDataFile(
                boardInterface
            ,   boardInterface
        );
}

Không có lý do chung nào cho thấy các đối số cần phải là các đối tượng khác nhau và nếu một phương thức yêu cầu các đối số phải khác nhau, thì đó sẽ là một yêu cầu đặc biệt mà hợp đồng của nó cần phải làm rõ.


4

Tôi đã đi đến kết luận rằng CreateDataFileđang thực hiện một điều bằng cách thực hiện phép đo nhanh và sau đó lưu trữ dữ liệu và thực hiện cả hai trong cùng một phương pháp sẽ trực quan hơn cho người khác sử dụng mã này sau đó phải thực hiện phép đo và ghi vào tệp như các cuộc gọi phương thức riêng biệt.

Tôi nghĩ rằng đây là vấn đề của bạn, thực sự. Phương pháp này không làm một việc. Nó thực hiện hai hoạt động riêng biệt liên quan đến I / O cho các thiết bị khác nhau , cả hai đều hoạt động không tải cho các đối tượng khác:

  • Lấy số đo
  • Lưu kết quả đó vào một tập tin ở đâu đó

Đây là hai hoạt động I / O khác nhau. Đáng chú ý, cái đầu tiên không làm thay đổi hệ thống tập tin theo bất kỳ cách nào.

Trong thực tế, chúng ta nên lưu ý rằng có một bước giữa ngụ ý:

  • Lấy số đo
  • Nối tiếp phép đo thành một định dạng đã biết
  • Lưu phép đo tuần tự vào một tệp

API của bạn sẽ cung cấp từng loại riêng biệt trong một số hình thức. Làm thế nào để bạn biết một người gọi sẽ không muốn thực hiện một phép đo mà không lưu trữ nó ở bất cứ đâu? Làm thế nào để bạn biết họ sẽ không muốn có được một phép đo từ một nguồn khác? Làm thế nào để bạn biết họ sẽ không muốn lưu trữ nó ở nơi nào khác ngoài thiết bị? Có lý do chính đáng để tách các hoạt động. Tại một trần tối thiểu, mỗi mảnh cá nhân nên có sẵn cho bất kỳ người gọi. Tôi không nên bị buộc phải ghi số đo vào một tệp nếu trường hợp sử dụng của tôi không gọi cho nó.

Ví dụ, bạn có thể tách các hoạt động như thế này.

IMeasurer có một cách để lấy số đo:

public interface IMeasurer
{
    IMeasurement Measure(int someInput);
}

Loại đo lường của bạn có thể chỉ là một cái gì đó đơn giản, như một stringhoặc decimal. Tôi không khẳng định bạn cần một giao diện hoặc lớp cho nó, nhưng nó làm cho ví dụ ở đây tổng quát hơn.

IFileAccess có một số phương pháp để lưu tệp:

interface IFileAccess
{
    void SaveFile(string fileContents);
}

Sau đó, bạn cần một cách nối tiếp một phép đo. Xây dựng nó vào lớp hoặc giao diện đại diện cho một phép đo hoặc có một phương thức tiện ích:

interface IMeasurement
{
    // As part of the type
    string Serialize();
}

// Utility method. Makes more sense if the measurement is not a custom type.
public static string SerializeMeasurement(IMeasurement m)
{
    return ...
}

Vẫn chưa rõ liệu bạn đã tách hoạt động tuần tự hóa này chưa.

Kiểu phân tách này cải thiện API của bạn. Nó cho phép người gọi quyết định những gì họ cần và khi nào, thay vì buộc các ý tưởng định sẵn của bạn về những gì tôi / O thực hiện. Người gọi nên có quyền kiểm soát để thực hiện bất kỳ hoạt động hợp lệ nào , cho dù bạn nghĩ rằng nó hữu ích hay không.

Khi bạn có các triển khai riêng cho từng thao tác, CreateDataFilephương thức của bạn sẽ trở thành một tốc ký cho

fileAccess.SaveFile(SerializeMeasurement(measurer.Measure()));

Đáng chú ý, phương pháp của bạn thêm rất ít giá trị sau khi bạn đã thực hiện tất cả điều này. Dòng mã trên không khó để người gọi của bạn sử dụng trực tiếp và phương pháp của bạn hoàn toàn là thuận tiện. Nó nên và là một cái gì đó tùy chọn . Và đó là cách chính xác để API hành xử.


Khi tất cả các phần có liên quan được bao thanh toán và chúng tôi đã thừa nhận rằng phương pháp này chỉ là sự tiện lợi, chúng tôi cần phải viết lại câu hỏi của bạn:

Trường hợp sử dụng phổ biến nhất cho người gọi của bạn là gì?

Nếu toàn bộ vấn đề là làm cho trường hợp sử dụng điển hình của đo và viết vào cùng một bảng thuận tiện hơn một chút, thì sẽ hoàn toàn hợp lý khi chỉ cần cung Boardcấp trực tiếp trên lớp:

public class Board : IMeasurer, IFileAccess
{
    // Interface methods...

    /// <summary>
    /// Convenience method to measure and immediate record measurement in
    /// default location.
    /// </summary>
    public void ReadAndSaveMeasurement()
    {
        this.SaveFile(SerializeMeasurement(this.Measure()));
    }
}

Nếu điều này không cải thiện sự thuận tiện, thì tôi sẽ không bận tâm đến phương pháp này.


Đây là một phương pháp thuận tiện mang đến một câu hỏi khác.

IFileAccessGiao diện nên biết về loại đo lường và làm thế nào để tuần tự hóa nó? Nếu vậy, bạn có thể thêm một phương thức vào IFileAccess:

interface IFileAccess
{
    void SaveFile(string fileContents);
    void SaveMeasurement(IMeasurement m);
}

Bây giờ người gọi chỉ cần làm điều này:

fileAccess.SaveFile(measurer.Measure());

mà chỉ ngắn gọn và có lẽ rõ ràng hơn phương pháp tiện lợi của bạn như được nghĩ trong câu hỏi.


2

Khách hàng tiêu thụ không cần phải xử lý một cặp mặt hàng khi một mặt hàng đủ. Trong trường hợp của bạn, họ gần như không, cho đến khi gọi CreateDataFile.

Giải pháp tiềm năng mà bạn đề xuất là tạo giao diện dẫn xuất kết hợp. Tuy nhiên, cách tiếp cận này đòi hỏi một đối tượng duy nhất thực hiện cả hai giao diện, khá hạn chế, được cho là một sự trừu tượng bị rò rỉ ở chỗ nó về cơ bản được tùy chỉnh để thực hiện cụ thể. Xem xét mức độ phức tạp của nó nếu ai đó muốn thực hiện hai giao diện trong các đối tượng riêng biệt: họ phải ủy quyền tất cả các phương thức trong một trong các giao diện để chuyển tiếp đến đối tượng khác. (FWIW, một tùy chọn khác là chỉ hợp nhất các giao diện thay vì yêu cầu một đối tượng phải thực hiện hai giao diện thông qua giao diện dẫn xuất.)

Tuy nhiên, một cách tiếp cận khác ít ràng buộc hơn trong việc ra lệnh cho việc triển khai IFileAccesslà kết hợp với một IMeasurerthành phần, để một trong số chúng bị ràng buộc và tham chiếu đến cái khác. (Điều này phần nào làm tăng sự trừu tượng của một trong số chúng vì nó cũng đại diện cho việc ghép đôi.) Sau đó, chỉ CreateDataFilecó thể lấy một trong các tài liệu tham khảo, nói IFileAccess, và vẫn có được cái còn lại khi cần. Thực hiện hiện tại của bạn như một đối tượng mà cụ cả hai giao diện sẽ chỉ đơn giản là return this;cho các tài liệu tham khảo thành phần, ở đây các getter cho IMeasurertrong IFileAccess.

Nếu việc ghép đôi hóa ra là sai tại một thời điểm nào đó trong quá trình phát triển, nghĩa là đôi khi một công cụ đo khác được sử dụng với cùng một quyền truy cập tệp, thì bạn có thể thực hiện việc ghép đôi tương tự nhưng ở cấp độ cao hơn, có nghĩa là giao diện bổ sung được giới thiệu sẽ không phải là một giao diện dẫn xuất, mà là một giao diện có hai getters, ghép nối một truy cập tệp và bộ đo với nhau thông qua thành phần chứ không phải là dẫn xuất. Sau đó, khách hàng tiêu dùng có một mục cần quan tâm miễn là việc giữ cặp và các đối tượng riêng lẻ để xử lý (để tạo các cặp mới) khi cần thiết.


Một lưu ý khác, tôi có thể hỏi ai sở hữu CreateDataFile, và câu hỏi thuộc về bên thứ ba này là ai. Chúng tôi đã có một số khách hàng tiêu thụ mà gọi CreateDataFile, đối tượng sở hữu / lớp CreateDataFile, và IFileAccessIMeasurer. Đôi khi khi chúng ta có một cái nhìn lớn hơn về bối cảnh, các tổ chức thay thế, đôi khi tốt hơn, có thể xuất hiện. Khó để làm ở đây vì bối cảnh không đầy đủ, vì vậy chỉ cần thực phẩm cho suy nghĩ.


0

Một số đã đưa lên đó CreateDataFilelà làm quá nhiều. Tôi có thể đề nghị thay vì Boardlàm quá nhiều, vì việc truy cập một tệp có vẻ như là một mối quan tâm riêng biệt với phần còn lại của hội đồng quản trị.

Tuy nhiên, nếu chúng tôi cho rằng đây không phải là một lỗi, vấn đề lớn hơn là giao diện phải được xác định bởi máy khách, trong trường hợp này CreateDataFile.

Các giao diện Tách riêng Nguyên tắc khẳng định rằng khách hàng không cần phải phụ thuộc vào nhiều hơn một giao diện so với những gì nó cần. Mượn cụm từ từ câu trả lời khác này , điều này có thể được diễn giải là "một giao diện được xác định bởi những gì khách hàng cần."

Bây giờ, có thể soạn giao diện dành riêng cho khách hàng này bằng cách sử dụng IFileAccessIMeasurernhư các câu trả lời khác gợi ý, nhưng cuối cùng, khách hàng này nên có một giao diện được thiết kế riêng cho nó.


@Downvoter - Điều gì về điều này là không chính xác hoặc có thể được cải thiện?
Xtros
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.