2010-07-12 68 views
2

我正在嘗試找到問卷調查問題的整齊解決方案。讓我們說,我有一個Questionnaire類,它有一個集合Answer,例如在NHibernate中提供不同類型的問題的答案

public class Questionnaire 
{ 
    public virtual ISet<Answer> Answers {get;set;} 
} 

答案根據問題需要是不同的類型,例如,出生日期,標記出十,爲什麼你認爲等

我首先想到的是這樣的:

public class Question 
{ 
    public virtual QuestionType TypeOfQuestion {get;set;} 
    public virtual string PromptText {get;set;} 
} 

public class Answer 
{ 
    public virtual Question Question {get;set;} 
} 

public class DateTimeAnswer : Answer 
{ 
    public virtual DateTime Response {get;set;} 
}   

public class IntegerAnswer : Answer 
{ 
    public virtual int Response {get;set;} 
}   
// etc. 

明顯的問題是,從調查問卷,還有就是非訪問Response property:

questionnaire.Answers[0].Response; // compile error 

這同樣適用於接口。這將是更好的使用通用接口,如:

public interface IAnswer<T> 
{ 
    public virtual Question Question {get;set;} 
    public virtual T Response {get;set;} 
} 

public class DateTimeAnswer : IAnswer<DateTime> {} 

然後問題就在Questionnaire類,IAnswer的類型必須提供:

public class Questionnaire 
{ 
    public virtual ISet<IAnswer<???>> Answers {get;set;} 
} 

顯然,我不想要擁有許多不同類型的IAnswer集合。我可以使用

ISet<IAnswer<dynamic>> 

但是然後NHibernate不會喜歡它。

我意識到需要妥協某處,但我不確定哪個是最漂亮的。你會怎麼做?

回答

4

我認爲子類問題以及答案是一個很好的計劃 - 擺脫QuestionType枚舉。

然後,您可以在Question上爲MakeAnswer(string)抽象方法,它爲您封裝了大量邏輯,並且可以根據需要進行類型轉換等。

我對通用的問題的解決上Rob's answer將是這樣的:

public interface IAnswer 
{ 
    Question Question { get; } 
    void Respond(IAnswerFormatter formatter); 
} 

IAnswerFormatter看起來是這樣的:

public interface IAnswerFormatter 
{ 
    void Format(string value); 
    void Format(DateTime value); 
    ... 
} 

而且DateTimeAnswer應該是這樣的:

public class DateTimeAnswer : IAnswer 
{ 
    public DateTimeAnswer(Question question, DateTime value) 
    { 
     Question = question; 
    } 
    public Question Question { get; protected set; } 

    protected DateTime Response {get; set;} 

    public virtual void Respond(IAnswerFormatter formatter) 
    { 
     formatter.Write(Response); 
    } 
} 

而DateTimeQuestion可能如下所示:

public class DateTimeQuestion : Question 
{ 
    public IAnswer MakeAnswer(string value) 
    { 
     // Ignoring error handling here 
     return new DateTimeAnswer(this, DateTime.Parse(value)); 
    } 
} 

這樣,你的回答能夠保持自己的方式的價值,和你NH映射可以指定它的外觀在數據庫中(我建議使用映射鑑)你的問題可以負責從創建答案一般反應。

1

有趣的問題..

我的意見/想法:

  • 作爲Steve said - 擺脫討厭的QuestionType枚舉的!
  • 取出ISet<T> - 我不認爲它增加任何價值..

我會沿着類似的線路在想:

public class Questionnaire 
{ 
public AnswerCollection Answers { get; set; } 
} 

public class AnswerCollection : Collection<Answer> 
{ 
    // Subclassed Collection<T> for Add/Remove Semantics etc. 
} 

public abstract class Answer : IAnswer<object> 
{ 
    public override object Response { get { // Base Impl. Here }; } 

    public abstract string CommonOperation() 
{ 
    // This is the key, the "common operation" - likely ToString? 
    // (for rendering the answer to the screen) 
    // Hollywood Principle - let the answers figure out how they 
    // are to be displayed... 
} 
} 

public class DateTimeAnswer : Answer, IAnswer<DateTime> 
{ 
public override DateTime Response { get { // Do Stuff }; } 
public override string CommonOperation() { return "I can haz DateTime"; } 
} 

的想法是在這裏,我們需要深入瞭解你正在做的事情的實質所有對象的,這可能只是顯示答案。我們通過泛型添加類型安全性,所以我們可以確定,如果沒有類型,我們不能創建新的響應參數..

然後,我們可以確定進出的內容僅限於我們實現的答案類型。 NHib應該沒有真正的問題處理,因爲它知道它需要什麼。

雖然很爛,我們有Answer來從收集回來的「object」版本,即的集合是什麼,答案

這是否幫助? :)

+1

我同意一般結構,但我認爲你已經暴露了將IAnswer作爲泛型類的問題。 在您的示例DateTimeAnswer不是答案,因此無法進入AnswersCollection。你必須讓它繼承自Answer,並且實現Response屬性的對象版本以及你的強類型對象。 從問題你只會遍歷答案強類型DateTime響應似乎是一種浪費。 – spmason 2010-07-13 08:25:46

+0

它確實有幫助,謝謝。我會考慮一下並嘗試一下。 – harriyott 2010-07-13 08:28:37

+0

啊,史蒂夫 - 好點,答案應該子類答案和實現接口。我會糾正的。強大的打字更多的是爲了創作的和平,你不能創建「對象」版本,因爲它是抽象的。這是一個經典的問題,「想要強大的打字,但實際上只有*真的需要它10%的時間「.. – 2010-07-13 08:42:02

-1

是否真的有意義將數據模型存儲在完整的答案中?你將與​​他們一起做什麼?

最有可能的候選人似乎是報告在這種情況下,您可能希望查看CQRS(命令查詢責任分離)樣式。你將會得到一個QuestionnaireCompletedCommand,它將包含一個答案清單,然後你會以某種方式堅持這樣的答案,以使報告可能會遭遇。

當您有業務邏輯時(您可能會),數據模型非常棒,但如果您沒有任何業務邏輯,您可能會不必要地使解決方案複雜化。說到看到CQRS時,我並不一定指事件採購部分。這是極少數人需要的巨大併發症。

相關問題