Thực tiễn tốt nhất về phương pháp ánh xạ và mở rộng kiểu


15

Tôi muốn hỏi một số câu hỏi về các thực tiễn tốt nhất về các loại ánh xạ và sử dụng các phương thức mở rộng trong C #. Tôi biết chủ đề này đã được thảo luận nhiều lần trong vài năm qua, nhưng tôi đã đọc rất nhiều bài viết và vẫn còn nghi ngờ.

Vấn đề tôi gặp phải là mở rộng lớp mà tôi sở hữu với chức năng "chuyển đổi". Hãy nói rằng tôi có lớp "Người" đại diện cho một đối tượng sẽ được sử dụng bởi một số logic. Tôi cũng có một lớp "Khách hàng" đại diện cho phản hồi từ API bên ngoài (thực tế sẽ có nhiều hơn một API, vì vậy tôi cần ánh xạ mỗi phản hồi của API theo loại chung: Person). Tôi có quyền truy cập vào mã nguồn của cả hai lớp và về mặt lý thuyết có thể thực hiện các phương thức của riêng tôi ở đó. Tôi cần chuyển đổi Khách hàng thành Người để tôi có thể lưu nó vào cơ sở dữ liệu. Dự án không sử dụng bất kỳ trình ánh xạ tự động.

Tôi có 4 giải pháp khả thi trong đầu:

  1. Phương thức .ToPerson () trong lớp Người tiêu dùng. Nó đơn giản, nhưng có vẻ như phá vỡ mẫu Trách nhiệm đơn đối với tôi, đặc biệt là lớp Người tiêu dùng được ánh xạ tới các lớp khác (một số được yêu cầu bởi API bên ngoài khác), do đó, nó sẽ cần chứa nhiều phương thức ánh xạ.

  2. Trình xây dựng ánh xạ trong lớp Person lấy Consumer làm đối số. Cũng dễ dàng và cũng có vẻ như phá vỡ mô hình Trách nhiệm đơn. Tôi cần phải có nhiều hàm tạo ánh xạ (vì sẽ có lớp từ một API khác, cung cấp cùng một dữ liệu như Người tiêu dùng nhưng ở định dạng hơi khác nhau)

  3. Lớp chuyển đổi với các phương thức mở rộng. Bằng cách này, tôi có thể viết phương thức .ToPerson () cho lớp Người tiêu dùng và khi một API khác được giới thiệu với lớp NewConsumer của riêng nó, tôi chỉ có thể viết một phương thức mở rộng khác và giữ tất cả trong cùng một tệp. Tôi đã nghe một ý kiến ​​rằng các phương pháp mở rộng nói chung là xấu xa và chỉ nên được sử dụng nếu thực sự cần thiết vì vậy đó là những gì đang giữ tôi lại. Nếu không thì tôi thích giải pháp này

  4. Lớp chuyển đổi / Mapper. Tôi tạo lớp riêng biệt sẽ xử lý các chuyển đổi và triển khai các phương thức sẽ lấy thể hiện của lớp nguồn làm đối số và trả về thể hiện của lớp đích.

Tóm lại, vấn đề của tôi có thể được giảm xuống số lượng câu hỏi (tất cả theo ngữ cảnh với những gì tôi đã mô tả ở trên):

  1. Việc đặt phương thức chuyển đổi bên trong đối tượng (POCO?) (Như phương thức .ToPerson () trong lớp Người tiêu dùng) có được coi là phá vỡ mẫu trách nhiệm đơn lẻ không?

  2. Việc sử dụng các hàm tạo chuyển đổi trong lớp (giống như DTO) có được coi là phá vỡ mẫu trách nhiệm đơn lẻ không? Đặc biệt là nếu lớp như vậy có thể được chuyển đổi từ nhiều loại nguồn, vì vậy nhiều hàm tạo chuyển đổi sẽ được yêu cầu?

  3. Việc sử dụng các phương thức mở rộng trong khi có quyền truy cập vào mã nguồn lớp gốc được coi là thực tiễn xấu? Hành vi như vậy có thể được sử dụng như một mô hình khả thi để phân tách logic hay nó là một mô hình chống?


Personlớp một DTO? nó có chứa bất kỳ hành vi?
Yacoub Massad

Theo như tôi biết thì về mặt kỹ thuật là DTO. Nó chứa một số logic "được chèn" như các phương thức mở rộng, nhưng logic này bị giới hạn ở loại phương thức "ConvertToThatClass". Đây là tất cả các phương pháp di sản. Công việc của tôi là triển khai chức năng mới sử dụng một số trong các lớp này, nhưng tôi được cho biết rằng tôi không nên tuân theo cách tiếp cận hiện tại nếu nó không tốt chỉ để giữ cho nó phù hợp. Vì vậy, tôi tự hỏi liệu cách tiếp cận hiện tại này với chuyển đổi bằng các phương thức mở rộng có tốt không và liệu tôi có nên tiếp tục với nó hay thử một cái gì đó khác
emsi

Câu trả lời:


11

Việc đặt phương thức chuyển đổi bên trong đối tượng (POCO?) (Như phương thức .ToPerson () trong lớp Người tiêu dùng) có được coi là phá vỡ mẫu trách nhiệm đơn lẻ không?

Có, bởi vì chuyển đổi là một trách nhiệm khác.

Việc sử dụng các hàm tạo chuyển đổi trong lớp (giống như DTO) có được coi là phá vỡ mẫu trách nhiệm đơn lẻ không? Đặc biệt là nếu lớp như vậy có thể được chuyển đổi từ nhiều loại nguồn, vì vậy nhiều hàm tạo chuyển đổi sẽ được yêu cầu?

Có, chuyển đổi là một trách nhiệm khác. Sẽ không có gì khác biệt nếu bạn thực hiện thông qua các hàm tạo hoặc thông qua các phương thức chuyển đổi (ví dụ ToPerson).

Việc sử dụng các phương thức mở rộng trong khi có quyền truy cập vào mã nguồn lớp gốc được coi là thực tiễn xấu?

Không cần thiết. Bạn có thể tạo các phương thức mở rộng ngay cả khi bạn có mã nguồn của lớp mà bạn muốn mở rộng. Tôi nghĩ rằng bạn có tạo ra một phương thức mở rộng hay không nên được xác định bởi bản chất của phương pháp đó. Ví dụ, nó có chứa rất nhiều logic? Có phụ thuộc vào bất cứ điều gì khác mà các thành viên của chính đối tượng không? Tôi muốn nói rằng bạn không nên có một phương thức mở rộng yêu cầu các phụ thuộc để hoạt động hoặc có chứa logic phức tạp. Chỉ có logic đơn giản nhất nên được chứa trong một phương thức mở rộng.

Hành vi như vậy có thể được sử dụng như một mô hình khả thi để phân tách logic hay nó là một mô hình chống?

Nếu logic phức tạp, thì tôi nghĩ rằng bạn không nên sử dụng phương pháp mở rộng. Như tôi đã lưu ý trước đó, bạn chỉ nên sử dụng các phương thức mở rộng cho những điều đơn giản nhất. Tôi sẽ không xem xét chuyển đổi đơn giản.

Tôi đề nghị bạn tạo các dịch vụ chuyển đổi. Bạn có thể có một giao diện chung duy nhất cho nó như thế này:

public interface IConverter<TSource,TDestination>
{
    TDestination Convert(TSource source_object);
}

Và bạn có thể có bộ chuyển đổi như thế này:

public class PersonToCustomerConverter : IConverter<Person,Customer>
{
    public Customer Convert(Person source_object)
    {
        //Do the conversion here. Note that you can have dependencies injected to this class
    }
}

Và bạn có thể sử dụng Dependency Injection để tiêm bộ chuyển đổi (ví dụ IConverter<Person,Customer>) cho bất kỳ lớp nào yêu cầu khả năng chuyển đổi giữa PersonCustomer.


Nếu tôi không nhầm, đã có một IConverterkhung trong đó, chỉ chờ để được thực hiện.
RubberDuck

@RubberDuck, nó tồn tại ở đâu?
Yacoub Massad 4/11/2015

Tôi đã nghĩ về IConvertable, đó không phải là những gì chúng ta đang tìm kiếm ở đây. Lỗi của tôi.
RubberDuck

À ha! Tôi đã tìm thấy những gì tôi nghĩ về @YacoubMassad. Converterđược sử dụng bởi Listkhi gọi ConvertAll. msdn.microsoft.com/en-us/l Library / kt456a2y (v = vs.110) .aspx Tôi không biết điều đó hữu ích như thế nào đối với OP.
RubberDuck

Cũng có liên quan. Một số người khác đã thực hiện phương pháp bạn đề xuất ở đây. codereview.stackexchange.com/q/51889/41243
RubberDuck

5

Việc đặt phương thức chuyển đổi bên trong đối tượng (POCO?) (Giống như .ToPerson()phương thức trong lớp Người tiêu dùng) có được coi là phá vỡ mẫu trách nhiệm đơn lẻ không?

Đúng. Một Consumerlớp chịu trách nhiệm giữ dữ liệu liên quan đến người tiêu dùng (và có thể thực hiện một số hành động) và không chịu trách nhiệm tự chuyển đổi thành một loại khác, không liên quan.

Việc sử dụng các hàm tạo chuyển đổi trong lớp (giống như DTO) có được coi là phá vỡ mẫu trách nhiệm đơn lẻ không? Đặc biệt là nếu lớp như vậy có thể được chuyển đổi từ nhiều loại nguồn, vì vậy nhiều hàm tạo chuyển đổi sẽ được yêu cầu?

Có lẽ. Tôi thường có các phương thức bên ngoài cả hai đối tượng miền và DTO để thực hiện chuyển đổi. Tôi thường sử dụng một kho lưu trữ trong các đối tượng miền và (de) tuần tự hóa chúng, cho cơ sở dữ liệu, bộ nhớ, tệp, bất cứ điều gì. Nếu tôi đặt logic đó trong các lớp miền của mình, thì bây giờ tôi đã gắn chúng với một dạng tuần tự hóa cụ thể, điều này không tốt cho việc thử nghiệm (và những thứ khác). Nếu tôi đặt logic trong các lớp DTO của mình, thì tôi đã gắn chúng vào miền của mình, một lần nữa hạn chế thử nghiệm.

Việc sử dụng các phương thức mở rộng trong khi có quyền truy cập vào mã nguồn lớp gốc được coi là thực tiễn xấu?

Không nói chung, mặc dù chúng có thể được sử dụng quá mức. Với các phương thức mở rộng, bạn tạo các tiện ích mở rộng tùy chọn giúp mã dễ đọc hơn. Chúng có nhược điểm - dễ tạo ra sự mơ hồ mà trình biên dịch phải giải quyết (đôi khi âm thầm) và có thể làm cho mã khó gỡ lỗi hơn vì không rõ ràng phương thức mở rộng đến từ đâu.

Trong trường hợp của bạn, lớp trình chuyển đổi hoặc trình ánh xạ có vẻ như là cách tiếp cận đơn giản nhất, đơn giản nhất, mặc dù tôi không nghĩ bạn đang làm gì sai bằng cách sử dụng các phương thức mở rộng.


2

Cách sử dụng AutoMapper hoặc trình ánh xạ tùy chỉnh có thể được sử dụng như

MyMapper
   .CreateMap<Person>()
   .To<PersonViewModel>()
   .Map(p => p.Name, vm => vm.FirstName)
   .To<SomeDTO>()
   .Map(...);

từ tên miền

 db.Persons
   .ToListAsync()
   .Map<PersonViewModel>();

Dưới mui xe, bạn có thể trừu tượng AutoMapper hoặc cuộn trình ánh xạ của riêng bạn


2

Tôi biết điều này là cũ, nhưng vẫn còn mặc khải. Điều này sẽ cho phép bạn chuyển đổi cả hai cách. Tôi thấy điều này hữu ích khi làm việc với Entity Framework và tạo chế độ xem (DTO's).

public interface IConverter<TSource, TDestination>
{
    TDestination Convert(TSource source_object);
    TSource Convert(TDestination source_object);
}

public class PersonCustomerConverter : IConverter<Person, Customer>
{
    public Customer Convert(Person source_object)
    {
        //Do the conversion here. Note that you can have dependencies injected to this class
    }
    public Person Convert(Customer source_object)
    {
        //Do the conversion here. Note that you can have dependencies injected to this class
    }
}

Tôi thấy AutoMapper hơi nhiều khi thực hiện thao tác ánh xạ thực sự đơn giản.

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.