Việc sử dụng toán tử đúc rõ ràng của tôi là hợp lý hay là một hack xấu?


24

Tôi có một đối tượng lớn:

class BigObject{
    public int Id {get;set;}
    public string FieldA {get;set;}
    // ...
    public string FieldZ {get;set;}
}

và một đối tượng chuyên biệt, giống như DTO:

class SmallObject{
    public int Id {get;set;}
    public EnumType Type {get;set;}
    public string FieldC {get;set;}
    public string FieldN {get;set;}
}

Cá nhân tôi tìm thấy một khái niệm về việc chuyển BigObject thành SmallObject một cách rõ ràng - biết rằng đó là một hoạt động mất dữ liệu một chiều - rất trực quan và dễ đọc:

var small = (SmallObject) bigOne;
passSmallObjectToSomeone(small);

Nó được thực hiện bằng toán tử tường minh:

public static explicit operator SmallObject(BigObject big){
    return new SmallObject{
        Id = big.Id,
        FieldC = big.FieldC,
        FieldN = big.FieldN,
        EnumType = MyEnum.BigObjectSpecific
    };
}

Bây giờ, tôi có thể tạo một SmallObjectFactorylớp với FromBigObject(BigObject big)phương thức, điều đó sẽ làm điều tương tự, thêm nó vào phép nội xạ phụ thuộc và gọi nó khi cần ... nhưng với tôi nó dường như thậm chí còn quá phức tạp và không cần thiết.

PS Tôi không chắc điều này có liên quan hay không, nhưng OtherBigObjectcũng sẽ có thể được chuyển đổi thành SmallObject, thiết lập khác nhau EnumType.


4
Tại sao không phải là một nhà xây dựng?
edc65

2
Hoặc một phương pháp nhà máy tĩnh?
Brian Gordon

Tại sao bạn cần một lớp học nhà máy, hoặc tiêm phụ thuộc? Bạn đã thực hiện một sự phân đôi giả ở đó.
dùng253751

1
@immibis - bởi vì tôi bằng cách nào đó đã không nghĩ về những gì @Telastyn đề xuất: .ToSmallObject()phương thức (hoặc GetSmallObject()). Một sai sót nhất thời của lý trí - Tôi biết có gì đó không ổn với suy nghĩ của mình, vì vậy tôi đã hỏi các bạn :)
Gerino

3
Điều này nghe có vẻ như là một trường hợp sử dụng hoàn hảo cho giao diện ISmallObject chỉ được BigObject triển khai như một phương tiện để cung cấp quyền truy cập vào một tập hợp hạn chế dữ liệu / hành vi mở rộng của nó. Đặc biệt là khi kết hợp với ý tưởng về ToSmallObjectphương pháp của @ Telastyn .
Marjan Venema

Câu trả lời:


0

Không có câu trả lời nào khác đúng trong quan điểm khiêm tốn của tôi. Trong câu hỏi stackoverflow này, câu trả lời được bình chọn cao nhất lập luận rằng mã ánh xạ nên được giữ ngoài miền. Để trả lời câu hỏi của bạn, không - việc sử dụng toán tử diễn viên của bạn không tốt. Tôi khuyên bạn nên tạo một dịch vụ lập bản đồ nằm giữa DTO và đối tượng miền của bạn hoặc bạn có thể sử dụng thiết bị tự động cho việc đó.


Đây là một ý tưởng tuyệt vời. Tôi đã có Automapper tại chỗ, vì vậy nó sẽ rất dễ dàng. Vấn đề duy nhất tôi có với nó: không nên có một số dấu vết cho thấy BigObject và SmallObject có liên quan đến nhau?
Gerino

1
Không, tôi không thấy bất kỳ lợi thế nào bằng cách ghép nối BigObject và SmallObject với nhau ngoài dịch vụ ánh xạ.
Esben Skov Pedersen

7
Có thật không? Một máy tự động là giải pháp của bạn cho các vấn đề thiết kế?
Telastyn

1
BigObject có thể ánh xạ tới SmallObject, chúng không thực sự liên quan đến nhau theo nghĩa OOP cổ điển và mã phản ánh điều này (cả hai đối tượng tồn tại trong miền, khả năng ánh xạ được đặt trong cấu hình ánh xạ cùng với nhiều đối tượng khác). Nó loại bỏ mã đáng ngờ (ghi đè toán tử không may của tôi), nó để lại các mô hình sạch sẽ (không có phương thức nào trong chúng), vì vậy, nó dường như là một giải pháp.
Gerino

2
@EsbenSkovPedersen Giải pháp này giống như sử dụng máy ủi để đào một cái lỗ để cài đặt hộp thư của bạn. May mắn thay, OP muốn đào lên sân dù thế nào, vì vậy một chiếc máy ủi hoạt động trong trường hợp này. Tuy nhiên, tôi sẽ không đề xuất giải pháp này nói chung .
Neil

81

Nó là ... Không tuyệt vời. Tôi đã làm việc với mã đã thực hiện thủ thuật thông minh này và nó đã dẫn đến sự nhầm lẫn. Rốt cuộc, bạn sẽ mong đợi có thể chỉ định biến BigObjectthành một SmallObjectbiến nếu các đối tượng đủ liên quan để truyền chúng. Mặc dù vậy, nó không hoạt động - bạn gặp lỗi trình biên dịch nếu bạn thử liên quan đến hệ thống loại, chúng không liên quan. Nó cũng gây khó chịu nhẹ cho người vận hành đúc để tạo ra các vật thể mới.

Tôi muốn giới thiệu một .ToSmallObject()phương pháp thay thế. Rõ ràng hơn về những gì đang thực sự xảy ra và về như dài dòng.


18
Doh ... ToSmallObject () có vẻ như là sự lựa chọn rõ ràng nhất. Đôi khi rõ ràng nhất là khó nắm bắt nhất;)
Gerino

6
mildly distastefullà một cách đánh giá thấp. Thật không may là ngôn ngữ cho phép loại thứ này trông giống như một kiểu chữ. Không ai có thể đoán đó là một sự chuyển đổi đối tượng thực tế trừ khi họ tự viết nó. Trong một nhóm một người, tốt. Nếu bạn cộng tác với bất kỳ ai, trong trường hợp tốt nhất, sẽ lãng phí thời gian vì bạn phải dừng lại và tìm hiểu xem đó có thực sự là diễn viên hay không, nếu đó là một trong những biến đổi điên rồ đó.
Kent A.

3
@Telastyn Đồng ý rằng đó không phải là mùi mã lớn nhất. Nhưng việc tạo ra một đối tượng mới từ hoạt động mà hầu hết các lập trình viên hiểu là một hướng dẫn cho trình biên dịch để coi đối tượng đó là một loại khác, không tốt cho bất kỳ ai phải làm việc với mã của bạn sau bạn. :)
Kent A.

4
+1 cho .ToSmallObject(). Hầu như không bao giờ bạn nên ghi đè các nhà khai thác.
ytoledano

6
@dorus - ít nhất là trong .NET, Getngụ ý trả lại một thứ hiện có. Trừ khi bạn ghi đè các thao tác trên đối tượng nhỏ, hai Getcuộc gọi sẽ trả về các đối tượng không bằng nhau, gây nhầm lẫn / lỗi / wtfs.
Telastyn

11

Trong khi tôi có thể thấy lý do tại sao bạn cần phải có một SmallObject, tôi sẽ tiếp cận vấn đề khác đi. Cách tiếp cận của tôi đối với loại vấn đề này là sử dụng Mặt tiền . Mục đích duy nhất của nó là đóng gói BigObjectvà chỉ cung cấp các thành viên cụ thể có sẵn. Theo cách này, nó là một giao diện mới trên cùng một ví dụ và không phải là một bản sao. Tất nhiên bạn cũng có thể muốn thực hiện một bản sao, nhưng tôi khuyên bạn nên làm như vậy thông qua một phương thức được tạo cho mục đích đó kết hợp với Mặt tiền (ví dụ return new SmallObject(instance.Clone())).

Mặt tiền có một số lợi thế khác, cụ thể là đảm bảo rằng một số phần nhất định trong chương trình của bạn chỉ có thể sử dụng các thành viên có sẵn thông qua mặt tiền của bạn, đảm bảo hiệu quả rằng nó không thể sử dụng những gì không nên biết. Ngoài ra, nó cũng có một lợi thế to lớn là bạn có thể linh hoạt hơn trong việc thay đổi BigObjecttrong bảo trì trong tương lai mà không phải lo lắng quá nhiều về cách sử dụng nó trong suốt chương trình của bạn. Miễn là bạn có thể mô phỏng hành vi cũ dưới hình thức này hay hình thức khác, bạn có thể thực hiện SmallObjectcông việc giống như trước đây mà không phải thay đổi chương trình của mình ở mọi nơi BigObjectsẽ được sử dụng.

Lưu ý, điều này có nghĩa là BigObjectkhông phụ thuộc vào SmallObjectmà là cách khác (vì nó nên theo ý kiến ​​khiêm tốn của tôi).


Ưu điểm duy nhất mà bạn đề cập rằng một mặt tiền có các trường sao chép sang một lớp mới là tránh việc sao chép (có lẽ không phải là vấn đề trừ khi các đối tượng có số lượng trường vô lý). Mặt khác, có một nhược điểm là bạn phải sửa đổi lớp gốc mỗi khi bạn cần chuyển đổi sang một lớp mới, không giống như một phương thức chuyển đổi tĩnh.
Doval

@Doval Tôi cho rằng đó là điểm. Bạn sẽ không chuyển đổi nó sang một lớp mới. Bạn sẽ tạo một mặt tiền khác nếu đó là những gì bạn yêu cầu. Các thay đổi được thực hiện cho BigObject chỉ cần được áp dụng cho lớp Facade và không phải ở mọi nơi mà nó được sử dụng.
Neil

Một điểm khác biệt thú vị giữa cách tiếp cận này và câu trả lời của Telastyn là liệu trách nhiệm tạo ra SmallObjectlời nói dối với SmallObjecthay BigObject. Theo mặc định, phương pháp này buộc SmallObjectphải tránh sự phụ thuộc vào các thành viên tư nhân / được bảo vệ BigObject. Chúng ta có thể tiến thêm một bước và tránh phụ thuộc vào các thành viên riêng tư / được bảo vệ SmallObjectbằng cách sử dụng ToSmallObjectphương pháp mở rộng.
Brian

@Brian Bạn có nguy cơ lộn xộn BigObjecttheo cách đó. Nếu bạn muốn làm một cái gì đó tương tự, bạn sẽ chứng minh cho việc tạo một ToAnotherObjectphương thức mở rộng trong BigObject? Đây không phải là mối quan tâm của BigObject, vì có lẽ, nó đã đủ lớn như nó là. Nó cũng cho phép bạn tách BigObjectkhỏi việc tạo ra các phụ thuộc của nó, nghĩa là bạn có thể sử dụng các nhà máy và những thứ tương tự. Các cách tiếp cận khác mạnh mẽ các cặp vợ chồng BigObjectSmallObject. Điều đó có thể ổn trong trường hợp cụ thể này, nhưng đó không phải là cách thực hành tốt nhất theo quan điểm khiêm tốn của tôi.
Neil

1
@Neil Trên thực tế, Brian giải thích nó sai, nhưng ông đúng - phương pháp khuyến nông làm thoát khỏi các khớp nối. Nó không còn BigObjectđược kết hợp với SmallObject, nó chỉ là một phương thức tĩnh ở đâu đó có một đối số BigObjectvà trả về SmallObject. Các phương thức mở rộng thực sự chỉ là đường cú pháp để gọi các phương thức tĩnh theo cách đẹp hơn. Phương thức mở rộng không phải là một phần của BigObject, nó là một phương thức tĩnh hoàn toàn riêng biệt. Đây thực sự là một cách sử dụng khá tốt các phương thức mở rộng và đặc biệt rất tiện dụng cho các chuyển đổi DTO.
Luaan

6

Có một quy ước rất mạnh mẽ rằng các kiểu tham chiếu có thể thay đổi là bảo toàn danh tính. Bởi vì hệ thống thường không cho phép các toán tử đúc do người dùng định nghĩa trong các tình huống có thể gán một đối tượng của loại nguồn cho tham chiếu của kiểu đích, nên chỉ có một vài trường hợp các thao tác truyền do người dùng xác định sẽ hợp lý cho tham chiếu có thể thay đổi các loại.

Tôi sẽ đề nghị như một yêu cầu, x=(SomeType)foo;sau đó được đưa ra sau đó y=(SomeType)foo;, với cả hai phôi được áp dụng cho cùng một đối tượng, x.Equals(y)sẽ luôn luôn và mãi mãi là đúng, ngay cả khi đối tượng trong câu hỏi đã được sửa đổi giữa hai phôi. Một tình huống như vậy có thể áp dụng nếu ví dụ một người có một cặp đối tượng thuộc các loại khác nhau, mỗi đối tượng có một tham chiếu bất biến cho đối tượng kia và truyền một đối tượng sang loại khác sẽ trả về thể hiện được ghép nối của nó. Nó cũng có thể áp dụng với các loại đóng vai trò là trình bao bọc cho các đối tượng có thể thay đổi, với điều kiện là danh tính của các đối tượng được bọc là không thay đổi và hai trình bao bọc cùng loại sẽ tự báo cáo là bằng nhau nếu chúng bao bọc cùng một bộ sưu tập.

Ví dụ cụ thể của bạn sử dụng các lớp có thể thay đổi, nhưng không bảo tồn bất kỳ dạng nhận dạng nào; như vậy, tôi sẽ đề nghị rằng nó không phải là cách sử dụng phù hợp của toán tử đúc.


1

Nó có thể ổn

Một vấn đề với ví dụ của bạn là bạn sử dụng các tên ví dụ như vậy. Xem xét:

SomeMethod(long longNum)
{
  int num = (int)longNum;
  /* ... */

Bây giờ, khi bạn đã là một ý tưởng tốt những gì một dàiint phương tiện, sau đó cả hai diễn viên tiềm ẩn của intđể longvà các diễn viên rõ ràng từ longđể intlà khá dễ hiểu. Cũng có thể hiểu được cách 3trở thành 3và chỉ là một cách khác để làm việc 3. Có thể hiểu điều này sẽ thất bại như thế nào int.MaxValue + 1trong bối cảnh được kiểm tra. Ngay cả cách nó sẽ hoạt động int.MaxValue + 1trong một bối cảnh không được kiểm soát để dẫn đến int.MinValuekhông phải là điều khó khăn nhất để tìm kiếm.

Tương tự như vậy, khi bạn chuyển hoàn toàn sang loại cơ sở hoặc rõ ràng sang loại dẫn xuất, điều đó dễ hiểu đối với bất kỳ ai biết cách kế thừa hoạt động như thế nào và kết quả sẽ như thế nào (hoặc làm thế nào nó có thể thất bại).

Bây giờ, với BigObjectSmallObject tôi không có ý thức về mối quan hệ này hoạt động như thế nào. Nếu các kiểu thực sự của bạn trong đó mối quan hệ truyền hình rõ ràng, thì việc đúc thực sự có thể là một ý tưởng tốt, mặc dù rất nhiều thời gian, có lẽ là đại đa số, nếu đây là trường hợp thì nó nên được phản ánh trong hệ thống phân cấp lớp và đúc dựa trên thừa kế bình thường sẽ đủ.


Trên thực tế, chúng không nhiều hơn những gì được cung cấp trong câu hỏi - nhưng ví dụ BigObjectcó thể mô tả một Employee {Name, Vacation Days, Bank details, Access to different building floors etc.}, và SmallObjectcó thể là một MoneyTransferRecepient {Name, Bank details}. Có một bản dịch đơn giản từ Employeeđến MoneyTransferRecepient, và không có lý do gì để gửi cho ứng dụng ngân hàng bất kỳ dữ liệu nào nhiều hơn mức cần thiết.
Gerino
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.