簡體   English   中英

多態性和DTO對象創建

[英]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中,基本上有兩種方法可以解決此問題:

  1. 通過從存儲庫中獲取對象,可以使用instanceof或將對象的類映射到專用的Transformer對象(可以是簡單的Map <Class,Transfomer>)。 這是此處建議的框架使用的方法。

  2. 使用訪客模式。 這種方法的想法是,最簡單的方法是在所有域對象中都具有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樣式方法需要更多的代碼。 它確實為您提供了很好的可擴展機制,可用於您希望在域上執行的其他類似操作。

我想這真的取決於您的情況,哪種方法最好。 但是,我總是建議使用DozerModdelMapper之類的框架來代替 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM