[英]Polymorphism and DTO object creation
我開發的應用程序包含幾層。 我們有DAO層,它返回模型對象。 我們也有實例化DTO對象並將其發送給客戶端的映射器。 實體映射到控制器層中的DTO。 我在幾個實體類中介紹了繼承。 讓我們假設像下面的圖片一樣
我要求DAO列出具體動物園的動物清單。 然后我得到了列表動物列表,但是它們是具體類型的,因為動物是抽象的,我們不能只在數據庫中有動物。 我想從此模型對象創建DTO。 我必須使用mapper,如果我有if .. else語句,則檢查每個動物的類型,然后創建適當的DTO,例如
if (animal instanceof Dog) {
.. create dog dto
} else if (animal instance of Cat) {
.. create cat dto
} .. and so on
這段代碼看起來不太好。 使用多態性並在每只動物上調用某種方法來產生DTO會很好,但是在域模型中創建僅用於通信的DTO對象的邏輯就不好了。 您如何解決這種情況?
編輯:更具體地說,我想擁有DTO,例如1. DogDTO僅包含顏色和名稱字段2. FishDTO僅包含numberOfFins不是一個具有所有可能屬性的大AnimalDTO
您想為不同類型的對象選擇不同的變換。 在Java中,基本上有兩種方法可以解決此問題:
通過從存儲庫中獲取對象,可以使用instanceof
或將對象的類映射到專用的Transformer對象(可以是簡單的Map <Class,Transfomer>)。 這是此處建議的框架使用的方法。
使用訪客模式。 這種方法的想法是,最簡單的方法是在所有域對象中都具有toDTO()
方法,但這會在域對象中對DTO引入不必要的依賴性。 另一種方法是將所有這些類都放在單個對象toDTO(Dog d)
toDTO(Fish f)
,但是不幸的是,Java中的單調度功能意味着要使用此功能,您仍然必須找出要轉換的對象的類型並調用正確的方法:VM無法為您執行此操作。 訪客模式是解決此問題的設計模式。
訪客模式的基本設計如下:
public abstract class AnimalVisitor<T> {
public abstract T visit (Dog d);
public abstract T visit (Fish f);
...
}
public abstract class Animal {
public <T> abstract T accept (AnimalVisitor<T> v);
...
}
public class Dog extends Animal {
public <T> T accept (AnimalVisitor<T> v) {
return v.visit(this);
}
...
}
public class Fish extends Animal {
public <T> T accept (AnimalVisitor<T> v) {
return v.visit(this);
}
...
}
public class DTOTransformer extends AnimalVisitor<DTO> {
public DogDTO visit (Dog d) {
return new DogDTO(d);
}
public FishDTO visit (Fish f) {
return new FishDTO(f);
}
}
引入新的Animal類型時,您需要向AnimalVisitor
類添加新的visit
方法。 系統將提示您執行此操作,因為您必須在新的Animal類型中實現accept
方法。 這也將提示您更新DTOTransformer
和任何其他實現AnimalVisitor
。
如您所見,此方法比簡單的instanceof
樣式方法需要更多的代碼。 它確實為您提供了很好的可擴展機制,可用於您希望在域上執行的其他類似操作。
我想這真的取決於您的情況,哪種方法最好。 但是,我總是建議使用Dozer或ModdelMapper之類的框架來代替 if (.. instanceof ...) else if (...
我自己。
我不確定您是否真的有域模型-它們是否確實具有域邏輯,或者它們僅僅是數據容器?
無論如何,這種映射邏輯應該在DTO暴露的外層,而不是域層。 整個想法是使域不依賴於外層。
只需在控制器層中創建可重復使用的映射器(例如,作為擴展方法)。 例:
public class AnimalDto
{
public string Sound { get; set; }
}
public class CatDto : AnimalDto
{
}
public class DogDto : AnimalDto
{
public bool IsTrained {get; set;}
}
public class AnimalDo
{
public string Sound { get; set; }
}
public class CatDo : AnimalDo
{
}
public class DogDo : AnimalDo
{
public bool IsTrained {get; set;}
}
public static class MappingExtensions
{
public static AnimalDto Map(this AnimalDo animalDo)
{
if (animalDo is CatDo) return (animalDo as CatDo).Map();
if (animalDo is DogDo) return (animalDo as DogDo).Map();
return null;
}
public static DogDto Map(this DogDo dogDo)
{
return new DogDto() { IsTrained = dogDo.IsTrained, Sound = dogDo.Sound };
}
}
用法:
AnimalDo animal = new DogDo() { IsTrained = true, Sound = "bwarf"};
var dogDto = animal.Map();
或選擇使用AutoMapper為您完成映射。
編輯:注意到您剛剛添加了JAVA標記,而我的代碼是一個C#示例。 在Java中,似乎沒有擴展方法,但您也可以編寫普通的靜態類。 看這里 。 而且似乎還有類似java的automapper之類的東西。
使用instanceof
您不能為正在使用的對象使用代理。 當您使用諸如Hibernate之類的ORM解決方案時,這可能是一個問題,因為您始終需要從DB讀取實例的完整狀態,以確保可以在需要時在用例中檢查其類型。
一種選擇是聲明getType()
方法並在每個子類中重寫它:
abstract class Animal {
abstract AnimalType getType();
}
class Dog extends Animal {
@Override
AnimalType getType() {
return AnimalType.DOG;
}
}
class Cat extends Animal {
@Override
AnimalType getType() {
return AnimalType.CAT;
}
}
這樣,您就可以擺脫instanceof
運算符(這意味着您通常會獲得更大的OOP靈活性,例如使用代理,裝飾器等)。
由於AnimalType
可能是一個enum
,因此您還可以在DTO工廠和類似場所的switch
語句中使用它,這比if...else if
instanceof
更具可讀性。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.