2015-05-29 49 views
4

我開發包含幾個層的應用程序。我們有返回模型對象的DAO層。我們也有映射器實例化DTO對象並將它們發送給客戶端。實體映射到控制器層中的DTO。我在幾個實體類中引入了繼承。讓我們假設在圖像某事像下面多態性和DTO對象創建

class diagram (not enough reputation points to past image directly)

我問DAO動物的名單從具體的動物園。然後我得到List動物列表,但它們是具體的類型,因爲動物是抽象的,我們不能在數據庫中擁有Animal。我想從這個模型對象創建DTO。我不得不使用映射器在我,如果.. 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與所有可能的屬性

回答

0

我不確定你是否真的有一個領域模型 - 他們真的有領域邏輯,還是他們只是數據容器?

無論如何,這種映射邏輯應該位於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中,你似乎沒有擴展方法,但你也可以編寫普通的靜態類。見here。而且它似乎也有類似automapper for java

+0

它們相當容器。你是否推薦使用'instanceof'邏輯並按類型創建適當的DTO? – soulcoder

+0

我個人並不認爲這有什麼問題。我添加了一個例子。 –

+0

目前我有幾乎相同的解決方案,因爲你已經發布。但是我想以更好的方式做到這一點,以避免在添加新的動物類型和新的DTO類型時添加新的instanceof案例和類型檢查。我想了一會兒,創建了一些工廠,使用instanceof似乎是最好的方法。完美的是迭代動物列表並調用一些接口/抽象方法來創建適當的DTO。 – soulcoder

0

使用instanceof您無法使用代理處理正在處理的對象。當你使用像Hibernate這樣的ORM解決方案時,這可能是一個問題,因爲你總是需要從數據庫中讀取實例的完整狀態,以確保在需要時可以在用例中檢查它的類型。

一個替代的將是聲明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運營商的(這意味着你一般獲得更多的面向對象編程的靈活性,例如使用代理,裝飾器等)。

由於AnimalType很可能將是一個enum,那麼你也可以得到你的DTO工廠和類似的地方,這是一個有點比if...else ifinstanceof更具可讀性使用它在switch報表的能力。

+0

感謝您的回答。在我的繼承結構中,我使用與getType幾乎相同的鑑別器。我可以將列映射到屬性,而不必在每個類中重寫getType方法。但我仍然需要檢查某處的類型並創建適當的DTO;) – soulcoder

2

您想爲不同類型的對象選擇不同的轉換。基本上有兩種方法解決這個問題在Java中:

  1. 使用instanceof或從資料庫得到它映射對象到一個專門的Transformer對象的類(可以是簡單地圖<類,主變差動>) 。這是框架建議here使用的方法。

  2. 使用訪客模式。這種方法的想法是,最簡單的方法是在所有域對象中都有toDTO()方法,但這會在DTO的域對象中引入不需要的依賴項。另一種方法是將所有這些類放在一個對象中,但不幸的是,Java中的單一調度功能意味着要使用它,您仍然必須找出要變換的對象的類型並調用正確的方法:虛擬機無法爲您做到這一點。訪問者模式是解決這個問題的設計模式。

訪問者模式的基本設計是這樣的:

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); 
    } 

} 

當你介紹一個新的動物類型,你需要一個新的visit方法添加到AnimalVisitor類。您將被提示執行此操作,因爲您必須在新的動物類型中實施accept方法。這也會提示您更新DTOTransformer以及AnimalVisitor的任何其他實施。

正如你所看到的,這種方法需要更多的代碼,然後是簡單的instanceof風格的方法。它確實爲您提供了一個很好的可擴展機制,用於其他類似的操作,您希望在您的域中執行此操作。

我想這真的取決於你的情況哪種方法最好。但是,我總是推薦使用像DozerModdelMapper這樣的框架來自己編寫if (.. instanceof ...) else if (...

+0

感謝您的回答,我考慮過第二種選擇,但是按照您的說法,它將模型層與通信層耦合在一起,甚至比在我看來使用instanceof更糟糕。隨着變換器的地圖,我還需要填充數據(添加鍵/類和適當的映射器)。但也許它會看起來更好,並且會更加可重用。我會考慮的。 – soulcoder

+0

@ user3201879編輯完成後請重新閱讀我的答案。 –

+1

@ user3201879使用訪問者模式可以很好地分離各層。 –