簡體   English   中英

在財產中強制執行適當價值的可接受方式是什么,這取決於同一類別的其他財產?

[英]What is the accepted way to enforce proper value in property which depends on other properties of the same class?

假設我有一個具有以下三個屬性的類。

public class Travel
{
  public int MinAirportArrival { get; set; }
  public int MinFlightTime { get; set; }
  public int TotalTravelTime { get; set; }
}

TotalTravelTime必須至少是MinAirportArrival和MinFlightTime的總和,但如果有中途停留或類似的事情,也可能更多。

我很清楚,我可以將邏輯放在TotalTravelTime的setter中。

我的問題是關於更改MinFlightTime和MinAirportArrival。 期望TotalTravelTime首先增加是否正確,如果沒有其他人將其總和大於TotalTravelTime?

我以合理的方式控制這個的其他選擇是什么?

我應該將此保留給負責保存狀態的對象以檢查類上的有效屬性嗎? 我也可能有其他邏輯。

編輯
如果有的話,我不會在額外的時間存儲任何數量,所以這不僅僅是添加一些屬性的問題。 僅僅澄清這個課程只是我面臨的問題的一個人為的例子,但我認為它很好地匹配問題。

框架設計指南建議屬性不應相互依賴。 始終可以按任意順序設置多個屬性的值

以下是可以看到的選項:

  1. 在這個特定情況下:

     public class Travel { public int MinAirportArrival { get; set; } public int MinFlightTime { get; set; } public int AdditionalTravelTime { get; set; } public int TotalTravelTime { get { return MinAirportArrival + MinFlightTime + AdditionalTravelTime; } } } 

    這利用了可以從其他三個值重建 TotalTravelTime的效果,這三個值可以在沒有依賴性的情況下進行單獨設置。

  2. 一般情況的解決方案是接受任何值並在例如將實例發送到存儲時驗證值。

     public class Travel { public int MinAirportArrival { get; set; } public int MinFlightTime { get; set; } public int TravelTime { get; set; } public void Save() { // validate TravelTime > MinAirportArrival + MinFlightTime } } 
  3. 另一個選項是使屬性為只讀,並提供批量更新屬性值的方法。

     public class Travel { public int MinAirportArrival { get; private set; } public int MinFlightTime { get; private set; } public int TravelTime { get; private set; } public void UpdateTimes( int minAirportArrival, int minFlightTime, int travelTime) { // validate travelTime > minAirportArrival + minFlightTime MinAirportArrival = minAirportArrival; MinFlightTime = minFlightTime; TravelTime = travelTime; } } 
  4. 或者,您可以使Travel對象不可變,並使用構造函數,工廠方法或可變構建器對象來創建實例。

    構造函數:

     public class Travel { public Travel(int minAirportArrival, int minFlightTime, int travelTime) { // validate travelTime > minAirportArrival + minFlightTime } public int MinAirportArrival { get; } public int MinFlightTime { get; } public int TravelTime { get; } } 

    工廠方法:

     public class Travel { public static Travel CreateTravel( int minAirportArrival, int minFlightTime, int travelTime) { // validate travelTime > minAirportArrival + minFlightTime return new Travel(minAirportArrival, minFlightTime, travelTime); } private Travel(int minAirportArrival, int minFlightTime, int travelTime); public int MinAirportArrival { get; } public int MinFlightTime { get; } public int TravelTime { get; } } 

    Builder類:

     public class TravelBuilder { public int MinAirportArrival { get; set; } public int MinFlightTime { get; set; } public int TravelTime { get; set; } public Travel BuildTravel() { // validate TravelTime > MinAirportArrival + MinFlightTime return new Travel(MinAirportArrival, MinFlightTime, TravelTime); } } 

    可以在.NET框架中找到所有三個選項的示例。

由於TotalTravelTime是從其他兩個派生的,所以我不會為它實現一個公共setter,而是由setter為其他兩個更新的私有setter。

要允許以任何順序設置屬性,但保留已建立的驗證API(某些API自動使用,包括DataGridView和ASP.NET MVC驗證),一個選項是IDataErrorInfo 這不是最簡單的實現接口,但可行(下圖)。 您可以將驗證概括為:

IDataErrorInfo dei = obj as IDataErrorInfo;
if(dei != null) {
    string err = dei.Error;
    if(!string.IsNullOrEmpty(err)) throw new SomeTypeOfException(err);
}

這是一個實現:

public class Travel : IDataErrorInfo
{
    public int MinAirportArrival { get; set; }
    public int MinFlightTime { get; set; }
    public int TotalTravelTime { get; set; }
    string IDataErrorInfo.this[string property] {
        get {
            switch (property) {
                case "TotalTravelTime":
                    if (TotalTravelTime < MinAirportArrival + MinFlightTime) {
                        return "not enough time blah";
                    }
                    break;
            }
            return "";
        }
    }
    string IDataErrorInfo.Error {
        get {
            StringBuilder sb = new StringBuilder();
            AppendError(ref sb, "MinAirportArrival");
            AppendError(ref sb, "MinFlightTime");
            AppendError(ref sb, "TotalTravelTime");
            return sb == null ? "" : sb.ToString();
        }
    }
    void AppendError(ref StringBuilder builder, string propertyName) {
        string error = ((IDataErrorInfo)this)[propertyName];
        if (!string.IsNullOrEmpty(error)) {
            if (builder == null) {
                builder = new StringBuilder(error);
            } else {
                builder.Append(error);
            }
        }
    }
}

我認為最好將自定義驗證邏輯放入私有幫助器方法,然后從每個屬性設置器調用此方法,如下所示:

public class Travel
{
    private int minAirportArrival;
    private int minFlightTime;
    private int additionalTravelTime;

    public int MinAirportArrival
    { 
        get
        {
            return this.minAirportArrival;
        }
        set
        {
            ThrowOnImpossibleTimes(value, this.minFlightTime, this.additionalTravelTime);
            this.minAirportArrival = value;
        }
    }

    // other properties...

    private static void ThrowOnImpossibleTimes(int minAirportArrival, int minFlightTime, int additionalTravelTime)
    {
        // do check and eventually throw...
    }
}

一種方法是實現檢查條件的驗證方法。 該方法將從每個屬性的setter調用,如果不滿足條件則拋出異常

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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