Mẫu thiết kế để nhập dữ liệu của các loại nguồn khác nhau và đến các loại đích khác nhau


14

Tôi phải thiết kế và xây dựng tập lệnh nhập (trong C #) có thể xử lý các thao tác sau:

  • đọc dữ liệu từ nhiều nguồn khác nhau (XML, XSLX, CSV)
  • xác minh dữ liệu
  • ghi dữ liệu vào các loại đối tượng khác nhau (khách hàng, địa chỉ)

Dữ liệu sẽ đến từ một số nguồn nhưng một nguồn sẽ luôn có một định dạng nhập (csv, xml, xslx). Định dạng nhập có thể thay đổi từ nguồn này sang nguồn khác. Định dạng nhập mới có thể được thêm vào trong tương lai. Các loại đối tượng đích luôn giống nhau (khách hàng, địa chỉ và một số chi tiết khác).

Tôi đã suy nghĩ về việc sử dụng thuốc generic và tôi đã đọc một vài điều về mô hình nhà máy nhưng tôi là một người khá lớn trong lĩnh vực này vì vậy mọi lời khuyên đều được chào đón nhiều hơn.

Một mẫu thiết kế phù hợp để giải quyết vấn đề này là gì?


Giữ cho nó đơn giản.
NoChance

Câu trả lời:


11

Bạn đang quá nhiệt tình với các khái niệm ưa thích là quá sớm. Generics - khi bạn thấy một trường hợp sử dụng chúng, nhưng nếu không thì đừng lo lắng. Mô hình nhà máy - cách quá linh hoạt (và thêm nhầm lẫn) cho điều này chưa.

Giữ cho nó đơn giản. Sử dụng thực hành cơ bản.

  1. Hãy thử tưởng tượng những điều phổ biến giữa việc đọc cho XML, đọc cho CSV bất cứ điều gì. Những thứ như, bản ghi tiếp theo, dòng tiếp theo. Vì các định dạng mới có thể được thêm vào, hãy thử tưởng tượng điểm chung mà định dạng được xác định sẽ có với các định dạng đã biết. Sử dụng điểm chung này và xác định một 'giao diện' hoặc hợp đồng mà tất cả các định dạng phải tuân thủ. Mặc dù họ tuân thủ nền tảng chung, tất cả họ có thể có các quy tắc nội bộ cụ thể của họ.

  2. Để xác thực dữ liệu, hãy thử cung cấp một cách dễ dàng cắm vào các khối mã xác thực mới hoặc khác nhau. Vì vậy, một lần nữa, hãy cố gắng xác định một giao diện trong đó mỗi trình xác nhận, chịu trách nhiệm cho một loại xây dựng dữ liệu cụ thể tuân thủ hợp đồng.

  3. Để tạo các cấu trúc dữ liệu, có lẽ bạn sẽ bị hạn chế bởi bất kỳ ai thiết kế các đối tượng đầu ra được đề xuất nhiều hơn bất cứ thứ gì. Cố gắng tìm hiểu bước tiếp theo cho các đối tượng dữ liệu là gì và có bất kỳ tối ưu hóa nào bạn có thể thực hiện bằng cách biết sử dụng cuối cùng. Ví dụ: nếu bạn biết các đối tượng sẽ được sử dụng trong một ứng dụng tương tác, bạn có thể giúp nhà phát triển ứng dụng đó bằng cách cung cấp 'tổng kết' hoặc đếm các đối tượng hoặc các loại thông tin dẫn xuất khác.

Tôi muốn nói rằng hầu hết trong số này là các mẫu Mẫu hoặc mẫu Chiến lược. Toàn bộ dự án sẽ là một mẫu Adaptor.


+1, đặc biệt là cho đoạn đầu tiên (và thật tuyệt khi thấy bạn đã đi đến kết luận giống như tôi trong đoạn cuối).
Doc Brown

Ngoài ra, hãy ghi nhớ kiến ​​trúc của toàn bộ dự án, để thích ứng định dạng này với định dạng khác. Bạn có thể tưởng tượng bất kỳ tình huống mà ai đó có thể sử dụng chỉ một phần của điều đó trong một dự án khác không? EG Có thể một trình xác nhận dữ liệu mới xuất hiện trên thị trường và nó chỉ hoạt động với máy chủ SQL. Vì vậy, bây giờ bạn chỉ muốn đọc XML tùy chỉnh và đặt vào máy chủ SQL, bỏ qua các bước còn lại.
Andyz Smith

Để tạo điều kiện cho điều này, không chỉ các mảnh có hợp đồng nội bộ mà chúng tuân thủ, nên có một bộ hợp đồng xác định sự tương tác giữa các mảnh .
Andyz Smith

@AndyzSmith - Tôi có vấn đề giống hệt trong mã của mình. Tôi hiểu tất cả về mã của bạn ngoại trừ mẫu Adaptor. Khi bạn nói toàn bộ dự án là một ví dụ về mẫu Adaptor, bạn có thể minh họa điều đó không?
gansub

9

Điều rõ ràng là áp dụng mô hình Chiến lược . Có một lớp cơ sở chung ReadStrategyvà cho mỗi định dạng đầu vào như một lớp con XmlReadStrategy, CSVReadStrategyv.v. Điều này sẽ cho phép bạn thay đổi xử lý nhập độc lập với xử lý xác minh và xử lý đầu ra.

Tùy thuộc vào chi tiết, có thể giữ hầu hết các phần của nhập chung và chỉ trao đổi các phần của quá trình xử lý đầu vào (ví dụ: đọc một bản ghi). Điều này có thể dẫn bạn đến mẫu Phương thức mẫu .


Có nghĩa là khi sử dụng mẫu chiến lược, tôi phải tạo các phương thức riêng để chuyển đổi các đối tượng (khách hàng, địa chỉ) từ nguồn sang đích. Những gì tôi muốn làm là đọc, chuyển đổi và xác nhận từng đối tượng và đưa nó vào một danh sách để sau đó danh sách có thể được lưu vào cơ sở dữ liệu.
jao

@jao: tốt, nếu bạn đọc lại câu trả lời của tôi, bạn sẽ thấy rằng đề xuất của tôi là tạo ra "ReadStrargety", không phải là "ConvertStrargety". Vì vậy, bạn chỉ phải viết các phương thức khác nhau để đọc các đối tượng (hoặc bất kỳ phần bổ sung nào trong quy trình của bạn là riêng cho định dạng tệp cụ thể).
Doc Brown

7

Một mô hình phù hợp cho một tiện ích nhập khẩu mà bạn có thể cần mở rộng trong tương lai sẽ là sử dụng MEF - bạn có thể giữ mức sử dụng bộ nhớ thấp bằng cách tải bộ chuyển đổi bạn cần từ danh sách lười biếng, tạo các bản nhập MEF được trang trí bằng các thuộc tính giúp chọn trình chuyển đổi phù hợp cho quá trình nhập mà bạn đang cố thực hiện và cung cấp một cách dễ dàng để tách các lớp nhập khác nhau ra.

Mỗi phần MEF có thể được xây dựng để đáp ứng giao diện nhập với một số phương thức tiêu chuẩn chuyển đổi một hàng của tệp nhập thành dữ liệu đầu ra của bạn hoặc ghi đè một lớp cơ sở với chức năng cơ bản.

MEF là một khung để tạo kiến ​​trúc trình cắm - cách xây dựng triển vọng và Visual Studio, tất cả các tiện ích mở rộng đáng yêu trong VS đều là các phần MEF.

Để xây dựng ứng dụng MEF (Khung mở rộng được quản lý) bắt đầu bằng việc bao gồm một tham chiếu đến System.ComponentModel.Composition

Xác định giao diện để chỉ ra những gì trình chuyển đổi sẽ làm

public interface IImportConverter
{
    int UserId { set; }        
    bool Validate(byte[] fileData, string fileName, ImportType importType);
    ImportResult ImportData(byte[] fileData, string fileName, ImportType importType);
}

Điều này có thể được sử dụng cho tất cả các loại tệp bạn muốn nhập.

Thêm thuộc tính cho một lớp mới xác định lớp sẽ "Xuất"

[Export(typeof(IImportConverter))]
[MyImport(ImportType.Address, ImportFileType.CSV, "4eca4a5f-74e0")]
public class ImportCSVFormat1 : ImportCSV, IImportConverter
{
 ...interface methods...
}

Điều này sẽ xác định một lớp sẽ nhập tệp CSV (có định dạng cụ thể: Format1) và có các thuộc tính tùy chỉnh đặt Siêu dữ liệu thuộc tính xuất MEF. Bạn sẽ lặp lại điều này cho từng định dạng hoặc loại tệp bạn muốn nhập. Bạn có thể đặt thuộc tính tùy chỉnh với một lớp như:

[MetadataAttribute]
[AttributeUsage(AttributeTargets.All, AllowMultiple = false)]
public class ImportAttribute : ExportAttribute
{
    public ImportAttribute(ImportType importType, ImportFileType fileType, string customerUID)
        : base(typeof(IImportConverter))
    {
        ImportType = importType;
        FileType = fileType;
        CustomerUID = customerUID;
    }

    public ImportType ImportType { get; set; }
    public ImportFileType FileType { get; set; }
    public string CustomerUID { get; set; }
}

Để thực sự sử dụng bộ chuyển đổi MEF, bạn cần nhập các phần MEF bạn tạo khi mã chuyển đổi của bạn được chạy:

[ImportMany(AllowRecomposition = true)]
protected internal Lazy<IImportConverter, IImportMetadata>[] converters { get; set; }
AggregateCatalog catalog = new AggregateCatalog();

catalog thu thập các phần từ một thư mục, mặc định là vị trí ứng dụng.

converters là một danh sách lười biếng của các bộ phận MEF nhập khẩu

Sau đó, khi bạn biết loại tệp nào bạn muốn chuyển đổi ( importFileTypeimportType) có được trình chuyển đổi từ danh sách các phần được nhập trongconverters

var tmpConverter = (from x in converters
                    where x.Metadata.FileType == importFileType
                    && x.Metadata.ImportType == importType 
                    && (x.Metadata.CustomerUID == import.ImportDataCustomer.CustomerUID)
                    select x).OrderByDescending(x => x.Metadata.CustomerUID).FirstOrDefault();

if (tmpConverter != null)
{
     var converter = (IImportConverter)tmpConverter.Value;
     result = converter.ImportData(import.ImportDataFile, import.ImportDataFileName, importType);
....
}

Cuộc gọi đến converter.ImportDatasẽ sử dụng mã trong lớp đã nhập.

Có thể có rất nhiều mã và có thể mất một lúc để bạn hiểu được những gì đang diễn ra nhưng nó cực kỳ linh hoạt khi thêm các loại trình chuyển đổi mới và thậm chí có thể cho phép bạn thêm các mã mới trong thời gian chạy.


Tôi chưa nghe nói về MEF trước đây. Nó là gì?
jao

2
@jao kiểm tra link để được giải thích đầy đủ. Đã thêm một số ví dụ MEF vào câu trả lời của tôi.
Matt

1
Đây là một cách tuyệt vời để khởi động vào MEF. +1
paqogomez

MEF là một công nghệ, không phải là một mẫu thiết kế. Không -1từ tôi vì ý tưởng cơ bản vẫn có ý nghĩa và dựa vào một mẫu chiến lược được cai trị bởi IImportConvertergiao diện.
GETah

0

Một mẫu thiết kế phù hợp để giải quyết vấn đề này là gì?

Thành ngữ C # liên quan đến việc sử dụng khung nối tiếp được xây dựng để làm điều này. Bạn chú thích các đối tượng bằng siêu dữ liệu và sau đó khởi tạo các bộ nối tiếp khác nhau sử dụng các chú thích đó để trích xuất dữ liệu để đưa vào đúng biểu mẫu hoặc ngược lại.

Các dạng Xml, JSON và nhị phân là phổ biến nhất, nhưng tôi sẽ không ngạc nhiên nếu các dạng khác đã tồn tại ở dạng đóng gói đẹp để bạn tiêu thụ.


Chà, điều này hoạt động tốt nếu bạn tự do sử dụng định dạng tệp của riêng mình, nhưng tôi đoán cách tiếp cận này sẽ thất bại đối với các định dạng phức tạp, được xác định trước như XSLX, có nghĩa là các tệp MS Excel ở định dạng XML được nén.
Doc Brown

Tôi có thể ánh xạ một dòng tệp Excel tới một đối tượng, nhưng tôi sẽ cần sao chép và điều chỉnh phương thức đó cho các trình đọc XML và CSV. Và tôi muốn giữ mã càng sạch càng tốt ...
jao

@docBrown - thế nào? Về mặt khái niệm, việc biến một đối tượng thành một chuỗi các ô trong Excel không thực sự khác biệt với việc biến nó thành một tài liệu xml.
Telastyn

@Telastyn: bạn nói rằng bạn có thể sử dụng khung tuần tự hóa tích hợp của khung .NET để đọc định dạng XLSX? Nếu điều đó là đúng, các thư viện như Open XML SDK hoặc NPOI đã lỗi thời.
Doc Brown

@docbrown: lời xin lỗi của tôi, bạn đã đúng - Tôi cứ quên rằng không có lớp cơ sở tuần tự chung nào vì đó là một trong những điều đầu tiên được thực hiện trong bất kỳ cơ sở mã nào tôi làm việc.
Telastyn
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.