Czy „Mapper” jest prawidłowym wzorem projektowym, czy może jest odmianą wzoru „Factory”?

37

Typowy wzorzec, który widzę, to tak zwany Mapperwzorzec (którego nie należy mylić z DataMapperczymś zupełnie innym), który jako argument przyjmuje pewne „surowe” źródło danych (np. ADO.NET DataReaderlub DataSet) i mapuje pola na właściwości obiektu biznesowego / domeny. Przykład:

class PersonMapper
{
    public Person Map(DataSet ds)
    {
        Person p = new Person();
        p.FirstName = ds.Tables[0].Rows[0]["FirstName"].ToString();
        // other properties...
        return p;
    }
}

Ideą jest twoja brama / DAO / Repository / etc. zadzwoni do programu mapującego przed jego zwróceniem, aby uzyskać bogaty obiekt biznesowy w porównaniu do bazowego kontenera danych.

Wydaje się jednak, że jest to powiązane, jeśli nie identyczne, ze wzorem Factory (w każdym razie w języku DDD), który konstruuje i zwraca obiekt domeny. Wikipedia mówi, że to: Fabryka DDD:

Fabryka: metody tworzenia obiektów domeny powinny być przekazywane do wyspecjalizowanego obiektu fabryki, tak aby alternatywne implementacje mogły być łatwo wymieniane.

Z tego cytatu jedyną różnicą, o której mogłem pomyśleć, jest to, że Fabrykę w stylu DDD można sparametryzować, aby mogła zwrócić specjalistyczny typ obiektu, jeśli zajdzie taka potrzeba (np. BusinessCustomer kontra ResidentialCustomer), podczas gdy „Mapper” jest przypisany do konkretnej klasy i tylko tłumaczenie.

Czy istnieje różnica między tymi dwoma wzorami, czy są one zasadniczo takie same z różnymi nazwami?

Wayne Molina
źródło
Czy różni się to od ORM, a jeśli tak, to na czym polega różnica?
JB King,
ORM automatycznie obsługuje dla ciebie mapowanie - jest to bardziej scenariusz, w którym nie możesz użyć ORM (lub musisz napisać własną warstwę danych / cienką ORM)
Wayne Molina,
1
Naprawdę staram się zobaczyć, jak DataMapper jest „czymś zupełnie innym”.
pdr
Może się mylę - myślałem, że DataMapperwzorzec sam miał dostęp do bazy danych, podczas gdy ten „Mapper” nie pobiera danych z bazy danych, po prostu konwertuje jakiś zestaw wyników na obiekt.
Wayne Molina,
martinfowler.com/eaaCatalog/dataMapper.html W pewnym sensie rozumiem, co masz na myśli, ale czytając ten ostatni akapit, wszystko jest w porządku. Zobacz katalog PEAA. martinfowler.com/eaaCatalog/index.html . To, co opisujesz, jest z pewnością rodzajem Mapera i bardziej pasuje do DataMappera niż reszta.
pdr

Odpowiedzi:

23

Chociaż po raz pierwszy słyszę wzór Mappera, dla mnie to bardziej przypomina wzór Buildera niż Factory.

We wzorcu fabrycznym hermetyzujesz logikę tworzenia obiektów szeregu powiązanych klas. Pierwszym przykładem może być sytuacja, w której trzeba utworzyć obiekt określonej podklasy pewnej abstrakcyjnej klasy bazowej w zależności od niektórych parametrów. Tak więc fabryka zawsze zwraca wskaźnik lub referencję do klasy bazowej, ale w rzeczywistości tworzy obiekt odpowiedniej klasy pochodnej na podstawie podanych parametrów.

Natomiast klasa Builder zawsze tworzy obiekty tej samej klasy. Użyłbyś go, gdyby utworzenie obiektu było skomplikowane, np. Jego konstruktor przyjmuje wiele argumentów, z których nie wszystkie mogą być dostępne natychmiast. Tak więc obiekt konstruktora może być miejscem, w którym przechowywane są wartości argumentów konstruktora, dopóki nie będziesz mieć ich wszystkich i nie będziesz gotowy do utworzenia „produktu”, lub może dostarczyć rozsądne wartości domyślne i pozwolić ci tylko określić argumenty, których wartości potrzebujesz zmiana. Typowym przypadkiem użycia wzorca konstruktora jest tworzenie obiektów, które mogą być potrzebne w teście jednostkowym, aby uniknąć zaśmiecania kodu testowego całą logiką tworzenia.

Dla mnie Mapper brzmi jak wariant Konstruktora, w którym parametry konstruktora mają postać rekordu bazy danych lub innej „surowej” struktury danych.

Dima
źródło
Hmm, słyszałem o Builderze, ale nie byłem w 100% świadomy tego, co się z tym wiązało - pomogło mi to wyjaśnić! Wygląda na to, że główna różnica polega na tym, że Factory zwraca interfejs / klasę abstrakcyjną, która jest naprawdę konkretną klasą opartą na parametrach (jeśli ma to sens), podczas gdy Builder / Mapper bierze rzeczywiste dane i mapuje je na właściwości, bez dużej (jeśli w ogóle) logiki ?
Wayne Molina,
1
@Waine M: właściwie. Chociaż w Konstruktorze może być dużo logiki, ponieważ mapowanie danych na właściwości może wcale nie być proste. W rzeczywistości narzędzie do budowania może zawierać inne narzędzia do budowania parametrów. :)
Dima,
5
Wzorzec konstruktora pozwala umieścić wszystkie informacje wymagane do zbudowania złożonego (zwykle niezmiennego) obiektu w czasie, a następnie utworzyć instancję obiektu na końcu procesu. To nie dzieje się tutaj.
pdr
+1 Zgodził się, że jest to rozsądne podejście, mając oddzielną klasę „mapper”, która działa jako mediator między obiektami DTO i obiektami modelu domeny. Przydaje się również, gdy obiekty modelu domeny muszą zostać wprowadzone w specjalny stan podczas budowy (tj. Interfejs ISupportInitialize .NET).
MattDavey,
@pdr, zgadzam się, że Mapper nie jest dokładnie taki jak konstruktor, ponieważ wszystkie dane są dostępne jednocześnie. Ale jest bardzo podobny, ponieważ buduje tylko jeden typ obiektów.
Dima
8

Jedyną powszechną rzeczą w Mapper, Builder i Factory jest to, że dostarczają one „skonstruowany produkt” - i obiektową instancję typu. Aby uniknąć nieporozumień, odnoszę się do poniższych dyskusji dotyczących ich odpowiedniej definicji.

  1. Mapper - zbliżony do wyjaśnienia tutaj: http://www.codeproject.com/KB/library/AutoMapper.aspx . Nie jest to dokładnie to samo co powyżej - ale najbliższe, jakie znalazłem na temat mapera.

  2. Konstruktor - jak zdefiniowano tutaj: http://www.oodesign.com/builder-pattern.html

  3. Fabryka - jest zdefiniowana tutaj: http://www.oodesign.com/factory-pattern.html

Maper jest zasadniczo konstruktorem wywróconym na lewą stronę. Załóżmy, że przez chwilę, jeśli nie masz mapera - kiedy i tak potrzebujesz wielu zestawów parametrów, wszystkie są argumentami konstruktora. W miarę ewolucji niektóre aplikacje nie są świadome dodatkowych atrybutów, które muszą przejść do konstruktora, w którym albo go oblicza, albo używa domyślnego. Najważniejsze jest to, że program mapujący może to zrobić za Ciebie - i byłoby bardziej użyteczne, gdyby miało to powiązanie z obiektami zewnętrznymi w celu podjęcia decyzji o takim algorytmie konstrukcyjnym.

Konstruktor różni się bardzo od mapera. Konstruktor jest niezbędny, gdy kompletny obiekt składa się z wielu części obiektu. To rodzaj łączenia obiektów przez połączenie wielu części. Nawet inicjalizacja poszczególnych obiektów części związana jest z istnieniem innych części.

Fabryczne wzory na początku wyglądają bardzo prosto. Zwraca nowo zbudowane obiekty. Czy jakikolwiek zwykły konstruktor może dać mi w pełni funkcjonalną instancję za pomocą operatora takiego jak new ()? dlaczego miałbym potrzebować fabryki, która da mi takie same wyniki?

Jednak użycie wzorca fabrycznego jest zazwyczaj bardzo specyficzne; chociaż najczęściej nie są wyświetlane w literaturze, gdzie jest to stosowane. Zwykle aplikacja tworzy fabrykę, która jest udostępniana różnym obiektom, które muszą tworzyć takie obiekty produktu podczas wykonywania. Gdy powstaje wiele takich produktów, metoda Factory pozwala na tworzenie obiektów na podstawie pewnych zasad, na przykład, można wymusić określony szablon, który jest używany przez metodę fabryczną, gdy wszystkie obiekty są tworzone. To nie jest ani metoda budowniczego; tutaj produkt jest tylko jeden (i niezależny). To również nie przypomina mapera; tutaj zewnętrzny zestaw danych do stworzenia obiektu według klienta jest w rzeczywistości minimalny. Wzorzec fabryczny naprawdę zapewnia (jak sama nazwa wskazuje) konsekwentnie podobne obiekty produktu!

Dipan.

Dipan Mehta
źródło
4

Przeglądając odpowiedzi widzę, że wszyscy respondenci udzielają semantycznie niepoprawnych odpowiedzi. Myślę, że dzieje się tak, ponieważ zbytnio skupiasz się na pytaniu, które koncentruje się na powiązaniu mapera z fabryką lub konstruktorem.

Kiedy w rzeczywistości Mapper NIE jest podobny do fabryki lub konstruktora. Mapper najbardziej przypomina wzorzec adaptera (używając języka GoF). Wzorzec adaptera zapewnia funkcjonalność konwersji jednej reprezentacji na inną. OP odwoływał się do DataSet i DataReader w ADO.NET - a co z SqlDataAdapter? Odpowiedź jest w imieniu. Mapper to nowa nazwa czegoś, o czym od dawna znali się programiści: adaptery.

Mapper konwertuje jedną reprezentację na drugą - samą definicję wzorca adaptera.

Reuben Soto
źródło
1

To, co tutaj robisz, to w rzeczywistości pewnego rodzaju konwersja typu (konwertujesz surowe dane na obiekt biznesowy). Możesz użyć Wzorca Fabrycznego do robienia dokładnie tego (tj. Konwersji typów), więc tak, w pewnym sensie, twoja klasa jest fabryką (chociaż do tego użyłbym Statycznej Fabryki ).

Oliver Weiler
źródło
0

Konstruktorzy obudowują złożoną logikę biznesową, aby zbudować obiekt. Z drugiej strony Mapper powinien po prostu skopiować pola z jednego do drugiego.

Na przykład mapa z obiektu pracownika bazy danych do obiektu pracownika domeny, mapa z obiektu pracownika domeny do umowy z klientem.

Z drugiej strony konstruktorzy podejmują wiele decyzji biznesowych dotyczących budowy obiektu. Z punktu widzenia pojedynczej odpowiedzialności ma to sens.

użytkownik95940
źródło