Czy powinienem tworzyć interfejsy dla obiektów transferu danych?

19

Czy to dobry pomysł, czy zły pomysł na stworzenie interfejsu dla obiektów do przesyłania danych? Zakładając, że obiekt jest zwykle zmienny.

Chociaż mój przykład jest w Javie, powinien mieć zastosowanie do każdego innego języka, który ma podobne pojęcia.

interface DataTransferObject  {
    String getName();
    void setName(String name);
}

class RealDataTransferObject  implements DataTransferObject {

    String name;
    String getName() {
        return name;
    } 
    void setName(String name) {
        this.name = name;
    }
}

Oczywiście jest to uproszczony przykład, w prawdziwym życiu może być więcej pól.

Archimedes Trajano
źródło

Odpowiedzi:

24

Ogólna odpowiedź brzmi: nie , ponieważ nigdy nie należy dodawać kodu bez konkretnego, konkretnego powodu i nie ma ogólnego powodu takiego interfejsu.

Biorąc to pod uwagę, czasami może być dobry powód. Ale we wszystkich przypadkach, które widziałem, interfejsy te były częściowe, obejmując tylko jedną lub kilka właściwości wspólnych dla wielu klas, których chciałem używać polimorficznie, nie dając im wspólnej nadklasy. Typowi kandydaci są Idwłaściwością do wykorzystania w jakimś rejestrze lub Namewłaściwością do wyświetlenia użytkownikowi. Ale może być przydatny w każdym przypadku, gdy chcesz, aby jakiś kod obsługiwał wszystko, co ma X - po prostu stwórz XSourceinterfejs, który zawiera getX(i tylko w razie potrzeby setX) metody.

Ale osobny interfejs dla każdej klasy modelu, zawierający wszystkie właściwości? Nie wyobrażam sobie dobrego powodu, aby to zrobić. Złym powodem byłyby źle zaprojektowane ramy, które tego wymagają; EJB jednostki właśnie to zrobiły, jeśli dobrze pamiętam. Na szczęście były tak złe, że nigdy nie zyskały dużej przyczepności i są przestarzałe od EJB 3.0

Uwaga: unikaj używania terminu „obiekt wartości” do opisania komponentów Java za pomocą tylko trywialnych programów pobierających i ustawiających - jest to sprzeczne z bardziej powszechną definicją obiektu wartości jako czegoś bez tożsamości, która zwykle jest niezmienna. Lepszym terminem byłoby DTO lub klasa modelu - choć w tym drugim przypadku należy zauważyć, że anemiczne modele domenowe są uważane za antypattern.

Michael Borgwardt
źródło
3
„anemiczne modele domenowe są uważane za antypattern” - Martin Fowler, który w PEAA przyznaje, że zna ludzi, którzy pracowali w ten sposób od dziesięcioleci „bardzo skutecznie”. Sugeruję zatem, by nie było to anty-wzór, a bardziej nie-jak-Fowler-lubi-pracować. Niemniej jednak dobra odpowiedź, +1.
pdr
4
@pdr: Fowler mógł ukuć ten termin, ale w żadnym wypadku nie jest jedynym, który uważa ich za antypattern. Nie sądzę, aby ktokolwiek, kto faktycznie zrozumiał, OO, uważa, że ​​dobrym pomysłem jest wykluczenie logiki specyficznej dla domeny z modelu domeny.
Michael Borgwardt
Dzięki za termin korekty, DTO było słowem, którego szukałem zeszłej nocy, ale nie mogłem zapamiętać mojego życia. Zmieniając nazwę, projekt zyskał sens.
Archimedes Trajano,
@pdr Myślę, że najlepszym sposobem na wyrażenie tego jest to, że jest to antypattern w kontekście programowania OO. Istnieje wiele innych paradygmatów, w których jest całkowicie w porządku, i możesz nawet z powodzeniem pracować w ten sposób w języku OO (po prostu nie byłby szczególnie zorientowany obiektowo).
Daniel B
3
@MichaelBorgwardt: Zgadzam się, nie jest w tym sam. Ale też nie są ludzie, którzy się nie zgadzają. Gdybyś powiedział, że „anemiczne modele domenowe są przez niektórych uważane za anty-wzór”, nie powiedziałbym nic. Szczerze mówiąc, myślę, że i tak cała twoja odpowiedź byłaby lepsza bez ostatniego akapitu. Pierwsza połowa byłaby lepszym komentarzem; druga połowa nie oferuje nic na to pytanie.
pdr
2

Tworzenie interfejsu jest w porządku, ale NIE tak, jak działa twój przykład. Powinieneś usunąć seter z interfejsu i wszystko będzie dobrze:

interface ValueObject {
  String getName();
};

Pozwala to na wiele różnych jego implementacji, takich jak nazwa może być pobierana z bazy danych ... Setter powinien być w innym interfejsie.

tp1
źródło
2

Interfejsy definiują umowę między klasami wdrażającymi interfejsy a ich klientami. Są używane jako mechanizm abstrakcyjny, dzięki czemu klienci mogą manipulować „rzeczami o danym zachowaniu”.

Tak więc ogólna odpowiedź na pytanie „powinienem utworzyć i używać tego interfejsu?” to: Tak, jeśli możesz powiązać (pojedynczą) koncepcję istotną semantycznie dla twoich klientów.

Na przykład Porównywalny jest dobrym interfejsem, ponieważ wyjaśnia, że ​​rzeczy można porównywać dzięki jednej z ich metod, a jako klient interesuję się porównywalnymi obiektami (np. W celu ich sortowania). Przeciwnie , CoolStuff nie jest dobrym interfejsem, jeśli przyznasz, że fajne obiekty nie mają określonego zachowania (w rzeczywistości możesz sobie wyobrazić oprogramowanie, w którym radzenie sobie z fajnymi obiektami ma sens, ponieważ mają one wspólne zachowanie, takie jak beCool metoda).

W twoim szczególnym przypadku uważam, że twój interfejs jest bezużyteczny. Kto go użyje, jak i kiedy? Nie można utworzyć interfejsu dla każdej z wartości zmiennych. Więc zadaj sobie pytanie, jaka jest istotna i interesująca właściwość twoich metod.

Jeśli chcesz mieć do czynienia z obiektami, których wszystkie zmienne wartości są dostępne za pomocą kilku metod, spójrz na pojęcie bean Java i sposób, w jaki możesz zmusić swoje klasy do przyjęcia ich konwencji.

mgoeminne
źródło
2

Jak powiedział Michael, nie dodawaj interfejsu, chyba że masz konkretną potrzebę.

Testowanie jest przykładem dobrego powodu. Chociaż wolę używać prawdziwych współpracowników, jeśli są one po prostu „obiektami wartościowymi”, jak je nazywacie, dla prawdziwej izolacji testu jednostkowego może być konieczne utworzenie fałszywego obiektu do testowania, w którym to przypadku interfejs jest bardzo pomocny.

jhewlett
źródło
1
Interfejsy nie były konieczne do korzystania z frameworku przez wieki.
Michael Borgwardt
Wygląda na to, że popierasz monkeypatching. Byłem na tej drodze z Moles w C # i to nie jest ładne. Lepiej przekazać współpracowników, jeśli to możliwe.
jhewlett
1
Chociaż dwie szydzące frameworki, z których korzystam Mockito i easyMock, nie mogą kpić z końcowych klas, gdy są niezmienne.
Archimedes Trajano
1

Jeśli chcesz, aby ten obiekt miał sprawdzanie poprawności pól w przyszłości, powinieneś zapakować je na wczesnych etapach.

goodfella
źródło
0

„Interfejs dla obiektu wartości” jest tym, co nazywam akcesorem.

Doświadczyłem różnych zasad dotyczących potrzeb w zakresie akcesoriów. Niektórzy ludzie opowiadają się za, gdy jest to możliwe, inni zabraniają wtedy zmniejszania ilości kodu do napisania.

Niektóre uzasadnienia dotyczące akcesoriów (lub bezpośredniego wykorzystania wartości) są następujące:

  • Akcesoria umożliwiają późniejszą zmianę sposobu przechowywania wartości
  • Akcesoria umożliwiają dodawanie dziennika dostępu, gdy chcesz debugować oprogramowanie (dodając wywołanie dziennika w jednej metodzie, rejestrujesz każdą zmianę wartości)
  • Akcesoria są bardziej zgodne z programowaniem obiektowym, a każda zmienna jest enkapsulowana metodami
  • Akcesoria zmniejszają ekspresję kodu (więcej SLOC dla tego samego wyniku)
  • Akcesoria wymagają trochę procesora

Osobiście zalecam zmniejszenie liczby akcesorów i używanie ich, gdy spodziewacie się, że setter (setName) stanie się później czymś więcej niż zwykłą zmianą.

Marc-Emmanuel Coupvent des Gra
źródło
0

Ten rodzaj obiektu wartości jest dość niski. Sugerowałbym popchnięcie go w jednym z dwóch kierunków: (1) uczyń obiekt wartości niezmiennym, czyli jak wartość rzeczywistą , lub (2) przenieś zmienność do funkcji biznesowej zorientowanej na domenę wyższego poziomu, co oznacza, że ​​powinniśmy ujawnić interfejsy pod względem jednostek funkcjonalności istotnych dla domeny.

Erik Eidt
źródło