Wzorzec powielania klas?

11

Obecnie pracuję jako programista solo nad moim obecnym projektem. Projekt odziedziczyłem od innego programisty, który odszedł z firmy. Jest to aplikacja internetowa w stylu kontrolera widoku modelu w języku C #. Używa Entity Framework do mapowania relacyjnego obiektu. Istnieją dwa różne zestawy klas dla typów w modelu domeny. Jeden zestaw służy do interakcji z ORM, a drugi jest używany jako modele w systemie MVC. Na przykład mogą istnieć dwie klasy:

public class Order{
    int ID{get;set;}
    String Customer{get;set;}
    DateTime DeliveryDate{get;set;}
    String Description{get;set;}
}

i

public class OrderModel{
    String Customer{get;set;}
    DateTime DeliveryDate{get;set;}
    String Description{get;set;}
    public OrderModel( Order from){
        this.Customer= from.Customer;
        // copy all the properties over individually
    }
    public Order ToOrder(){
        Order result =new Order();
        result.Customer = this.Customer;
        // copy all the properties over individually
    }

}

Mogę wymyślić kilka wad tego podejścia (więcej miejsc do zmiany kodu, jeśli coś się zmieni, więcej obiektów siedzi w pamięci, więcej czasu spędzonego na kopiowaniu danych), ale nie jestem pewien, jakie są zalety tego rozwiązania. Chyba większa elastyczność klas modelowych? Ale mogłem to osiągnąć, również podklasując klasy jednostek. Moją skłonnością byłoby połączenie tych dwóch grup klas lub ewentualnie, aby klasy modeli były podklasami klas jednostek. Czy brakuje mi więc czegoś ważnego? Czy to typowy wzorzec projektowy, o którym nie wiem? Czy istnieją dobre powody, aby nie przejść przez refaktor, który rozważam?

AKTUALIZACJA

Niektóre odpowiedzi tutaj uświadamiają mi, że w moim początkowym opisie projektu brakowało pewnych ważnych szczegółów. W projekcie istnieje także trzecia grupa klas: klasy modelu strony. Są one faktycznie używane jako model kopii strony. Zawierają również informacje specyficzne dla interfejsu użytkownika i nie byłyby przechowywane z zamówieniem w bazie danych. Przykładową klasą modelu strony może być:

public class EditOrderPagelModel
{
    public OrderModel Order{get;set;}
    public DateTime EarliestDeliveryDate{get;set;}
    public DateTime LatestAllowedDeliveryDate{get;set;}
}

Całkowicie widzę, że użyteczność tej trzeciej grupy jest tutaj odrębna i nie planuję łączyć jej z czymś innym (chociaż mógłbym zmienić jej nazwę).

Klasy w grupie modeli są obecnie również używane przez interfejs API aplikacji, do którego również chciałbym się dowiedzieć, czy to dobry pomysł.

Powinienem również wspomnieć, że klient reprezentowany tutaj jako ciąg znaków miał uprościć przykład, a nie dlatego, że faktycznie jest reprezentowany w ten sposób w systemie. Rzeczywisty system ma klienta jako odrębny typ w modelu domeny z własnymi właściwościami

Robert Ricketts
źródło
1
„Odziedziczyłem projekt od innego programisty, który odszedł z firmy” - jest strona poświęcona opowiadaniom, które zaczynają się w ten sposób .
Jeśli chodzi o twoją aktualizację: wydaje się, że to zbyt dużo pośrednich, aby nie zapewnić wystarczających korzyści.
Robert Harvey
@RobertHarvey Co masz na myśli mówiąc o zbyt dużej pośredniczości?
Robert Ricketts
1
The OrderModel. OrderViewModel (co prawdopodobnie nazywacie EditOrderPageModel) jest wystarczającą pośrednią; patrz moja odpowiedź poniżej.
Robert Harvey
@RobertHarvey To brzmi jak odpowiedź na pytanie, które tak naprawdę chciałem zadać
Robert Ricketts

Odpowiedzi:

16

Czy brakuje mi więc czegoś ważnego?

TAK

Chociaż wyglądają tak samo i reprezentują to samo w domenie, nie są to te same obiekty (OOP).

Jednym z nich jest Orderczęść kodu związana z pamięcią danych. Drugi to Orderznany interfejs użytkownika. Chociaż przyjemnym zbiegiem okoliczności jest to, że klasy te mają te same właściwości, nie ma gwarancji , że tak będzie.

Lub, aby spojrzeć na to z innej perspektywy, rozważ zasadę pojedynczej odpowiedzialności. Kluczowym czynnikiem motywującym jest to, że „klasa ma jeden powód do zmiany”. Zwłaszcza, gdy mamy do czynienia z wszechobecnymi frameworkami, takimi jak ORM i / lub UI, twoje obiekty muszą być dobrze izolowane, ponieważ zmiany w frameworku często powodują zmianę twoich bytów.

Wiążąc swoje byty z obiema platformami, czynisz to o wiele gorszym.

Telastyn
źródło
7

Celem Modelu widoku jest zapewnienie rozdzielenia na dwa sposoby: poprzez dostarczenie danych w kształcie wymaganym przez Widok (niezależnie od modelu) i (szczególnie w MVVM) poprzez wypchnięcie części lub całości logiki Widoku z powrotem z Widoku do widoku modelu.

W modelu w pełni znormalizowanym klient nie byłby reprezentowany w modelu przez ciąg, ale przez identyfikator. Model, jeśli potrzebuje nazwy klienta, szukałby nazwy z tabeli Klienci, używając identyfikatora klienta znalezionego w tabeli Zamówienia.

Z drugiej strony model View może być bardziej zainteresowany Nameklientem niż identyfikatorem. Nie wymaga identyfikatora, chyba że klient jest aktualizowany w modelu.

Ponadto model widoku może i zwykle przyjmuje inny kształt (chyba że jest to czysty CRUD ). Obiekt modelu widoku faktury może mieć nazwę klienta, adresy i elementy zamówienia, ale model zawiera klientów i opisy.

Innymi słowy, faktury zwykle nie są przechowywane w taki sam sposób, w jaki są wyświetlane.

Robert Harvey
źródło
3

Pod pewnymi względami masz rację: oba odnoszą się do tego samego obiektu domeny, który jest nazywany order. Ale mylisz się co do tego, że nie jest to prawdziwe powielanie niż inna reprezentacja - wywodząca się z różnych celów. Jeśli chcesz zachować zamówienie, np. Chcesz uzyskać dostęp do każdej kolumny. Ale nie chcesz tego wyciec na zewnątrz. Poza tym przepychanie całych Istot przez drut nie jest tym, czego naprawdę chcesz. Znam przypadki, w których zbiór MB orderszostał wysłany do klienta, gdzie wystarczy kilka kilobajtów .

Krótko mówiąc - oto główne powody, dla których chcesz to zrobić:

1) Enkapsulacja - ukrywanie informacji o Twojej aplikacji

2) Małe modele są szybkie do serializacji i łatwe do przesłania

3) Służą one różnym celom, chociaż nakładają się na siebie, dlatego też powinny istnieć różne przedmioty.

4) Czasami sensowne jest posiadanie dla różnych zapytań różnych obiektów wartości . Nie wiem, jak to jest w C #, ale w Javie można używać POJO do zbierania wyników. Jeśli potrzebuję pojedynczego order, używam Order -Entity, ale jeśli potrzebuję tylko niektórych kolumn wypełnionych kwotami danych, użycie mniejszych Obiektów jest bardziej wydajne.

Thomas Junk
źródło
Ze względu na strukturę struktury encji encje są zwykłymi starymi obiektami C #, więc przesyłanie ich przez sieć nie stanowi większego problemu. Widzę użyteczność polegającą na tym, że w niektórych okolicznościach mogę wysłać tylko część treści zamówienia.
Robert Ricketts
Jeden czy kilka nie stanowi problemu, jak powiedziałem. Ale są przypadki, w których masz MB danych, co spowalnia czas ładowania. Pomyśl o czasach ładowania telefonu komórkowego
Thomas Junk
0

(Zastrzeżenie: Widziałem, że jest używane tylko w ten sposób. Mogłem nie rozumieć prawdziwego celu takiego działania. Podejmij tę odpowiedź z podejrzliwością.)

Oto kolejny brakujący bit: konwersja między Orderi OrderModel.

OrderKlasa jest przywiązany do swojego ORM, gdy OrderModeljest związana z projektem swojego widoku modelu.

Zazwyczaj te dwie metody będą dostarczane „w magiczny sposób” (czasami nazywane DI, IoC, MVVM lub jeszcze inną rzeczą). Oznacza to, że zamiast implementować te dwie metody konwersji w ramach OrderModel, będą one należeć do klasy poświęconej wzajemnej konwersji tych rzeczy; klasa ta zostanie w jakiś sposób zarejestrowana w frameworku, aby frameworek mógł go łatwo wyszukać.

public class OrderModel {
    ...
    public OrderModel(Order from) { ... }
    public Order ToOrder() { ... }
}

Powodem jest to, że robi to, gdy znajduje się przejście od OrderModeldo Order, lub odwrotnie, wiesz, że niektóre dane muszą być ładowane z lub zapisać do ORM.

rwong
źródło
Jeśli trzymam oba przy sobie, zdecydowanie chciałbym je skonfigurować, aby konwersja była bardziej zautomatyzowana
Robert Ricketts
@RobertRicketts To, co widziałem, wygląda tak: IMvxValueConverter . Chociaż mogłem źle zrozumieć jego cel.
rwong