単一の日付と日付範囲の両方を表すことができるプロパティ:適切にモデル化する方法は?


8

私は2つの方法で「送料見積もり」を表すことができるシステムで働いています。

  1. 特定の日付:アイテムはその日付で出荷されることが保証されています
  2. 日間隔:アイテムは今日から「X to Y」日で発送されます

モデルに関する情報は、意味的には同じで、「発送予定」です。配送見積もりの​​情報をシステムから取得すると、見積もりが最初の形式か2番目の形式かがわかります。

このための現在のモデルは次のようになります。

class EstimattedShippingDateDetails
{
    DateTime? EstimattedShippingDate {get; set;}
    Range? EstimattedShippingDayRange {get; set;}
}

Range 整数の「始まり->終わり」をラップする単純なクラスです。

struct Range
{
    int Start {get; set}
    int End {get; set}

    public override ToString()
    {
        return String.Format("{0} - {1}", Start, End);
    }
}

推定モデルのプロパティの1つだけが入力されるため、このアプローチは好きではありません。一方のプロパティでnullをテストし、もう一方のプロパティにデータがあると仮定する必要があります。

各プロパティは、ユーザーには異なる方法で表示されますが、現在のスイッチングロジックが存在するカスタムMVC DisplayTemplateを使用して、UIの同じ場所に表示されます。

@Model EstimattedShippingDateDetails

@if (Model.EstimattedShippingDate.HasValue)
{
    Html.DisplayFor(m => Model.EstimattedShippingDate)
}
else
{
    Html.DisplayFor(m => Model.EstimattedShippingDayRange)
}

MVCアプリケーションで表示ロジックをシンプルに保ちながら、これを実際の要件をよりよく表すようにモデル化するにはどうすればよいですか?

インターフェースと、推定の「タイプ」ごとに1つずつ、2つの実装を使用することを考えましたが、両方の共通インターフェースに頭を抱えているようには見えません。メンバーなしでインターフェースを作成すると、統一された方法でデータにアクセスできなくなり、設計が良くありません。また、viewmodelをできるだけシンプルにしたかったのです。ただし、このアプローチでは「構築による修正」コードが得られます。これは、null値を設定する必要がないためです。各実装には、n DateTimeまたはnullを設定できないプロパティがありますRange

私はまた、単一の使用と考えRange、状況#1が発生したときに、ちょうど同じを使用してDateTime両方のためにStartEnd、私はそれは、静的または間隔によってだかどうかを検出しなければならないとして、これはUIに値を放出する方法についての合併症を追加しますが値を比較し、範囲を単一の日付またはフォーマットされた間隔として表示されるように適切にフォーマットします。

私が必要としているのは、Typescriptの共用体に似た概念です。基本的には、2つの型のいずれかになるプロパティです。もちろん、C#にはネイティブにそのようなものは存在しません(dynamicそれに近いだけです)。


これはより概念的な「どうすればよいのですか…」です。オープンエンドの批評の要求よりも質問。この質問をプログラマーに移行する。
200_成功2016年

@ 200_success申し訳ありません。これはコードレビューに適していると思いましたが、あなたは正しいです。助けてくれてありがとう;)
julealgon 2016年

書式設定は厳しく設定されていますか、または、たとえば「$ date1と$ date2の間に出荷される」のように間隔を書式設定できますか?
2016年

@svick残念ながら今のところ問題は解決されています。
julealgon 2016年

回答:


17

すべての配送見積もりには、日付範囲(2つの日付)を使用してください。

単一の日付の場合、XとYを等しくします。


私自身の質問でこれについて述べましたが、このアプローチを使用するために私が提起した欠点について詳しく説明できますか?間隔のシナリオは固定日間隔であり、完全な日付間隔でもないことに注意してください。
julealgon 2016年

2
両方のフィールドにデータを入力すると、常にnull値に対する反対意見が排除されます。「1日の間隔ではなく一定の日間隔」とはどういう意味ですか?
Robert Harvey

サーバーから取得するデータは、DateTime配送予定日を表すオブジェクト、または商品が配送される今日からの最小日数と最大日数を示す2つの整数値です。日付で標準化する場合、これらの整数を適切に構築されたDateTimesに変換する必要があります。これにより、クライアントとサーバーの日付、UTC、夏時間など、考慮に入れる必要があるすべてのものが複雑になるため、かなり複雑になります。現時点では、このような複雑さを作り出さないようにしたいと思います。
julealgon 2016年

ただし、null値を持たない可能性については、それはかなり良いことです。
julealgon 2016年

MVCテンプレートでスイッチを使用している現在の方法の何が問題になっていますか?
Robert Harvey

2

カプセル化を使用してこれをモデル化できます。

クラスに2つのコンストラクターを持たせます。1つは単一の日付用、もう1つは日付範囲用です。

ToString()メソッドで、クラスの「状態」を判別し、適切なフォーマットされた文字列を生成します。

他のニーズに応じて、他のメソッドを追加します。


それはより単純なシステムではうまく機能しますが、MVCビューを使用してこれを適切に表示する必要があること、およびRangeタイプはシステムの他の場所で使用でき、その場合は同じ方法で表示できることに注意してください。そのため、Range再利用可能なDisplayTemplateに表示を集中させる必要があります。ToStringメソッドを使用してそれをカプセル化すると、ビューが提供する多くの柔軟性が失われます。
julealgon 2016年

また、オブジェクトの「状態」を知ることをどのように提案しますか?ソートのローカル列挙変数?ブール?どのコンストラクタが正しく呼び出されたかによって、異なる値に設定されると思いますか?これを機能させるには、おそらくクラスも不変にする必要があります。そうでない場合、他の値を設定した場合などに問題が発生する可能性があります。単一の日付コンストラクタを使用してRangeにアクセスしようとするとどうなりますか?プロパティも?その場合、例外をスローするのでしょうか、それとも単にnullを返すのでしょうか。ご覧のとおり、これの使いやすさはまだ理想的ではありません。
julealgon 2016年

はい、基本的にクラスは不変で、2つの日付は読み取り専用で、コンストラクターで割り当てられます。状態はブール値で確認でき、単一の日付を表すために同じ日付を持つこともできます。他のメンバー関数は状態を適切に使用でき、必要に応じてさらに状態を公開できます。申し訳ありませんが、サポートが必要なすべての機能を知っているわけではないため、具体的な説明はできません。
hocho 2016年

@hochoが言っていることは、すべての配達日は範囲としてモデル化できるということです。それは、一部の範囲が同じ開始日と終了日を持つということです。これはかなり賢明なようです。Rangesを使用するクライアントコードは、同じ日付の開始日と終了日を処理できるように準備する必要があります。その場合は、おそらく電子メールに異なる情報(つまり、単一の日付と日付範囲)を印刷します。
Erik Eidt 2016年

..ToString()へのstate反対票[to]を決定します。 ToString()状態を「報告」するだけです。状態は、コンストラクター、プロパティセッターなどで決定されます。そして、私はそこにあるか、「要約状態」である必要があります示唆してOPに何を見ていないよ
radarbob

1

これはかなり一般的な問題です。たとえば、Nullablesは、終了していないもののendDatesの一般的な問題を解決します。

しかし、あなたは悪い例を選んだと思います。

2つの日付の正確なケースでは、朝から夕方までの日付範囲が最適な解決策のようです。または、perhpasは開始日とそれがあるかもしれない余分な日数の整数ですか?

ただし、より難しいケースを考えてみましょう。配送方法は、ポストと店舗からの受け取りの2種類があります。これらははるかに異なるobvsで、ショップには名前と住所が必要です。投稿には費用がかかります。おそらく、いくつかの配送オプション、追跡コードなどがあります。

標準的なアプローチは、これらの「配信オプション」の両方を作成する一般的なものを探し、それらを基本クラスに配置することです。2つの特定のケースをサブクラス化し、それらが持っている/必要とする詳細を追加します。

ほとんどすべての場合、少なくとも両方のタイプに共通のID、タイプ、および説明があります。そう:

public class DeliveryOption 
{
     Public string Id;
     Public typeEnum Type;
     public string Description;
}


Public class Collection : DeliveryOption
{
     Public string ShopName;
}

Public class Post : DeliveryOption
{
     Public DateTime EstDelivery;
}

...朝から夕方までの日付範囲が最適なソリューションのようです。DataTime.Dateプロパティを 使用するだけで、時間を気にする必要はありません。
レーダーボブ2016年

1

「開始」と「終了」はDateTimeではなく、オフセットです

MVCアプリケーションで表示ロジックをシンプルに保ちながら、これを実際の要件をよりよく表すようにモデル化するにはどうすればよいですか?

「開始」と「終了」はへのオフセットEstimatedShipDateです。彼らDateTime自身ではありません。これにより、何が起こっているかがわかりやすくなり、複雑さが大幅に軽減されます。

の必要はありませんinterfaceRangeクラスは必要ありません。必要になるまで、複雑にしないでください。3つのオプションパラメーターコンストラクターを持つ単一のクラスは、物事をずっと単純に保つと強く思います。


サーバーから取得するデータは、配送予定日を表すDateTimeオブジェクト、または商品が配送される今日からの最小日数と最大日数を示す2つの整数値です。

オプションのパラメーターを介して3つすべての値を渡す単一のコンストラクター使用します。これにより、必要なすべてのコンテキストが提供されます。単一のすべてのバリエーションが正しく初期状態を設定するためにコンストラクタのロジックは、評価することができます。また、必要に応じて、コンストラクター呼び出しで名前付きパラメーターを使用して、明確にする必要があります。

public class ShipDate {
    public ShipDate (Datetime? shipDate = null, int earliestOffset = 0, latestOffset = 0) {
        EstShipDate = (DateTime) shipDate ?? DateTime.Now.Date;
        start = earliestOffset < 0 ? 0 : earliestOffset;
        end   = latestOffset < 0 ? 0 : latestOffset;
        // That's all, folks!
    }
}

日付で標準化する場合、これらの整数を適切に構築されたDateTimeに変換する必要があります。

いいえ、これを行わないでください。代わりに、DateTime.AddDays() `を使用してください。

public DateTime EstShipDate      {get; protected set;}
public DateTime EarliestShipDate { get { return EstShipDate.AddDays(start).Date; } }
public DateTime LatestShipDate   { get { return EstShipDate.AddDays(end).Date; } }

日付とオフセット値の変更が許可されている場合、これは潜在的により柔軟です。


これは、クライアントとサーバーの日付、UTC、夏時間の差など、考慮に入れる必要があるすべてのもののために、かなり複雑さを引き起こします。この時点で、この種の複雑さを作成しないようにしたいと思います。

タイムゾーンの処理については、こちらをご覧ください。

DateTime.DateTimeKindとりあえず、(列挙型)コンストラクタパラメータを含めて、後で処理するだけです。


答えの1つから:

2つの日付の正確なケースでは、朝から夕方までの日付範囲が最適な解決策のようです。または、perhpasは開始日とそれがあるかもしれない余分な日数の整数ですか?

DateTime.Dateプロパティを使用し、時間を完全に無視します。


0

のようなものはどうですか

class EstimattedShippingDateDetails
{
    DateTime EarliestShippingDate {get; set;}
    DateTime LatestShippingDate {get; set;}
    TimeSpan Range 
    {
        get 
        { 
            return LatestShippingDate - EarliestShippingDate; 
        } 
    }
}

特定の日付:アイテムはその日付で出荷されることが保証されています

どちらEarliestShippingDateLatestShippingDate保証出荷日に設定されています。

日間隔:アイテムは今日から「X to Y」日で発送されます

EarliestShippingDate今日に設定され、LatestShippingDateに設定されていますtoday + (Y - X)


0

単一の出荷日と1日で構成される範囲の違い(ある場合)を決定する必要があります。「単一の出荷日」がたまたま1日で構成される場合は、すべてを開始日と終了日としてモデル化します。「単一の出荷日」と、開始日と終了日が同じ日付の範囲を異なる方法で処理する場合は、2つのケースのいずれかを格納します。 。


0

基本的に2つのオプションがあります。

  • 2つの日付。オブジェクトが単一の日付を表す場合は、両方を同じ値に設定するか、2番目の日付をnull値に設定します。

  • 1つの日付とタイムスパン。日付は開始を表し、タイムスパンは範囲が将来どのくらいになるかを示します。単一の日付の範囲を0に設定します。

発送については、朝/夕方の配達をモデル化したいと思うので、日付ではなく日時を指定します。2つのオプションのどちらが最適かは、表示の計算方法によって異なります。「xとyの間」を表示している場合、最初のオプションの方が使いやすいかもしれません。「yから最大x日」を表示している場合、後者の方が使いやすいでしょう。

null値が嫌いの場合は、後者のオプションの方が適しています。元の日付とタイムスパンの計算は、タイムスパンに値があるか0に設定されているかに関係なく実行できるためです。ヌルをチェックしなくても、常に正しい結果が得られます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.