Properti yang dapat mewakili tanggal tunggal dan rentang tanggal: Bagaimana memodelkannya dengan benar?

8

Saya bekerja dalam sistem yang dapat mewakili "perkiraan pengiriman" dalam dua cara:

  1. Tanggal tertentu: Barang dijamin dikirim pada tanggal itu
  2. Interval hari: Item akan dikirim hari "X ke Y" mulai hari ini

Informasi pada model secara semantik sama, itu adalah "perkiraan pengiriman". Ketika saya mendapatkan informasi tentang perkiraan pengiriman dari sistem, saya dapat mengetahui apakah perkiraan itu dari bentuk pertama atau bentuk kedua.

Model saat ini untuk ini mirip dengan yang berikut:

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

Range adalah kelas sederhana untuk membungkus "awal -> akhir" dari bilangan bulat, agak seperti ini:

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

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

Saya tidak suka pendekatan ini karena hanya satu dari properti pada model estimasi yang akan diisi, dan saya perlu menguji nol pada salah satu dari mereka dan menganggap yang lain memiliki data.

Masing-masing properti ditampilkan secara berbeda kepada pengguna tetapi di tempat yang sama di UI, menggunakan MVT DisplayTemplate kustom, di mana logika switching saat ini berada:

@Model EstimattedShippingDateDetails

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

Bagaimana saya bisa memodelkan ini untuk membuatnya lebih representatif dari persyaratan aktual sambil tetap menjaga logika tampilan tetap sederhana dalam aplikasi MVC?

Saya berpikir tentang menggunakan antarmuka dan dua implementasi, satu untuk setiap "jenis" estimasi, tetapi saya tidak bisa membungkus kepala saya di sekitar antarmuka umum untuk keduanya. Jika saya membuat antarmuka tanpa anggota, maka saya tidak dapat mengakses data secara terpadu dan ini adalah desain IMHO yang buruk. Saya juga ingin menjaga viewmodel sesederhana mungkin. Saya akan mendapatkan kode "benar berdasarkan konstruksi" dengan pendekatan ini, karena tidak perlu ada nullable lagi: setiap implementasi akan memiliki properti yang tidak dapat dibatalkan, baik a DateTimeatau a Range.

Saya juga dianggap hanya menggunakan satu Rangedan ketika situasi # 1 terjadi, cukup gunakan yang sama DateTimeuntuk keduanya Startdan End, tetapi ini akan menambah komplikasi tentang cara memancarkan nilai-nilai ke UI karena saya kemudian harus mendeteksi apakah itu statis atau interval oleh membandingkan nilai-nilai dan memformat rentang dengan benar untuk ditampilkan sebagai tanggal tunggal atau interval yang diformat.

Tampaknya apa yang saya butuhkan adalah konsep yang mirip dengan serikat pekerja Script: pada dasarnya properti yang bisa dari dua jenis. Tidak ada hal seperti itu ada di C # tentu saja (hanya dynamicakan dekat dengan itu).

julealgon
sumber
Ini lebih merupakan konsep "Bagaimana saya ...?" pertanyaan daripada permintaan kritik terbuka. Migrasikan pertanyaan ini ke Programmer.
200_success
@ 200_sukses Maaf tentang itu. Saya pikir ini akan sesuai untuk codereview tetapi Anda benar, itu lebih cocok di sini. Terima kasih atas bantuannya;)
julealgon
Apakah pemformatan diatur dalam batu, atau bisakah Anda misalnya memformat interval sebagai "akan dikirimkan antara $ date1 dan $ date2"?
svick
@vick Sayangnya sudah diatur untuk saat ini.
julealgon

Jawaban:

17

Gunakan rentang tanggal (yaitu dua tanggal) untuk semua perkiraan pengiriman.

Untuk satu kencan, jadikan X dan Y sama dengan.

Robert Harvey
sumber
Saya menyebutkan ini dalam pertanyaan saya sendiri, dapatkah Anda menguraikan kelemahan yang saya ajukan karena menggunakan pendekatan ini? Perhatikan bahwa skenario interval adalah interval hari tetap dan bukan interval tanggal penuh juga.
julealgon
2
Mengisi kedua bidang harus selalu menghilangkan keberatan Anda terhadap nilai nol. Apa yang Anda maksud dengan "interval hari tetap dan bukan interval hari penuh?"
Robert Harvey
Data yang saya dapatkan dari server adalah DateTimeobjek, mewakili tanggal pengiriman yang diharapkan, atau dua nilai integer dengan hari minimum dan maksimum mulai hari ini bahwa barang akan dikirim. Jika saya membakukan tanggal, saya harus mengubah bilangan bulat tersebut menjadi DateTimes yang dibangun dengan benar, yang akan sedikit menambah kerumitan karena segala sesuatu yang perlu diperhitungkan, seperti tanggal klien vs server, UTC, waktu musim panas perbedaan dll. Saya ingin menghindari menciptakan kompleksitas semacam ini pada saat ini.
julealgon
Poin bagus tentang kemungkinan tidak pernah memiliki nilai nol, itu akan sangat bagus.
julealgon
Apa yang salah dengan cara Anda melakukannya sekarang, menggunakan sakelar di template MVC?
Robert Harvey
2

Anda dapat memodelkan ini menggunakan enkapsulasi.

Biarkan kelas memiliki 2 konstruktor, satu untuk satu tanggal dan satu lagi untuk rentang tanggal.

Dalam metode ToString () menentukan 'status' kelas dan menghasilkan string yang diformat yang sesuai.

Tambahkan metode lain yang sesuai untuk kebutuhan Anda yang lain.

hocho
sumber
Itu akan bekerja dengan baik pada sistem yang lebih sederhana, tetapi perhatikan bahwa saya perlu menampilkan ini dengan benar menggunakan tampilan MVC, dan Rangejenisnya dapat digunakan di tempat lain pada sistem dan dalam hal ini ditampilkan dengan cara yang sama. Itu sebabnya saya perlu memusatkan Rangetampilan menjadi DisplayTemplate yang dapat digunakan kembali. Jika saya menggunakan ToStringmetode ini untuk merangkum hal itu, saya kehilangan banyak fleksibilitas yang disediakan oleh view.
julealgon
Juga, bagaimana Anda mengusulkan saya tahu "keadaan" objek? Variabel enum lokal semacam? Boolean? Saya kira itu akan diatur ke nilai yang berbeda tergantung pada konstruktor mana yang disebut benar? Untuk membuat ini bekerja, saya mungkin juga perlu membuat kelas tidak berubah juga, atau saya bisa mendapatkan masalah jika seseorang menetapkan nilai lainnya dll. Apa yang harus saya lakukan jika seseorang menggunakan konstruktor tanggal tunggal dan mencoba mengakses Rentang properti juga? Apakah saya akan melemparkan pengecualian dalam kasus itu atau hanya mengembalikan nol? Soalnya, kegunaan ini masih belum ideal.
julealgon
Ya, pada dasarnya kelas tidak akan berubah dengan 2 tanggal dibaca dan ditugaskan pada konstruktor. Negara dapat dipastikan dengan bool dan / atau memiliki tanggal yang sama untuk mewakili satu tanggal. Fungsi anggota Anda yang lain dapat menggunakan negara dengan tepat dan Anda juga dapat mengekspos negara, jika perlu. Maaf tidak dapat lebih spesifik karena saya tidak tahu semua fungsi yang Anda butuhkan untuk mendukung.
hocho
Yang dikatakan @hocho adalah bahwa semua tanggal pengiriman dapat dimodelkan sebagai rentang, hanya saja beberapa rentang memiliki tanggal mulai dan berakhir yang sama. Ini sepertinya cukup masuk akal. Kode klien menggunakan Ranges harus siap untuk berurusan dengan tanggal mulai dan berakhir pada tanggal yang sama, dan dalam hal itu mungkin mencetak informasi yang berbeda (yaitu tanggal tunggal vs rentang tanggal) di email.
Erik Eidt
pilih untuk ..ToString () [untuk] menentukanstate . ToString()seharusnya hanya "melaporkan" negara. Negara ditentukan dalam konstruktor, properti setter, dan sebagainya. Dan, saya hanya tidak melihat apa pun di OP yang menunjukkan ada atau harusnya "ringkasan negara"
radarbob
1

Ini adalah masalah yang cukup umum, Nullables misalnya menyelesaikan masalah umum tempat tanggal berakhirnya hal-hal yang belum berakhir.

Tapi saya pikir Anda telah memilih contoh yang buruk.

Dengan kasus persis dua kencan Anda, rentang tanggal yang dimulai di pagi hari dan berakhir di malam hari tampaknya menjadi solusi yang sempurna. Atau mungkin tanggal awal dan bilangan bulat hari tambahan mungkin?

Namun, mari kita pertimbangkan kasus yang lebih sulit. Saya memiliki dua jenis pengiriman, pos dan pengambilan dari toko. Ini adalah obvs yang jauh lebih berbeda, toko membutuhkan nama dan alamat, pos memiliki biaya, mungkin sejumlah opsi pengiriman, kode pelacakan dll.

Pendekatan standar adalah untuk mencari hal-hal umum yang membuat kedua 'opsi pengiriman' ini dan menempatkan mereka di kelas dasar. Subkelas dua kasus khusus dengan rincian tambahan yang mereka miliki / butuhkan.

Dalam hampir semua kasus, Anda akan memiliki setidaknya Id, Jenis, dan Deskripsi yang umum untuk kedua jenis. Begitu:

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;
}
Ewan
sumber
... rentang tanggal yang dimulai di pagi hari dan berakhir di malam hari tampaknya menjadi solusi yang sempurna. Cukup gunakan DataTime.Dateproperti dan jangan khawatir tentang waktu sama sekali.
radarbob
1

"start" dan "end" bukan DateTime, Itu Offsets

Bagaimana saya bisa memodelkan ini untuk membuatnya lebih representatif dari persyaratan aktual sambil tetap menjaga logika tampilan tetap sederhana dalam aplikasi MVC?

"start" dan "end" adalah offset ke EstimatedShipDate. Mereka bukan milik DateTimemereka sendiri . Ini lebih baik menggambarkan apa yang terjadi dan secara dramatis akan mengurangi kompleksitas.

Tidak perlu untuk interface. Tidak perlu Rangekelas. Jangan membuat kerumitan sampai Anda yakin Anda membutuhkannya. Saya sangat curiga bahwa satu kelas dengan konstruktor 3-opsional-parameter akan menjaga hal-hal lebih sederhana.


Data yang saya dapatkan dari server adalah objek DateTime, mewakili tanggal pengiriman yang diharapkan, atau dua nilai integer dengan hari minimum dan maksimum dari hari ini bahwa barang akan dikirim.

Gunakan satu konstruktor yang melewatkan ketiga nilai melalui parameter opsional. Ini memasok semua konteks yang dibutuhkan. The tunggal logika konstruktor kemudian dapat mengevaluasi untuk semua variasi untuk mengatur keadaan awal dengan benar. Juga gunakan parameter bernama dalam panggilan konstruktor jika diinginkan untuk membuatnya sejernih kristal.

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!
    }
}

Jika saya membakukan tanggal, saya harus mengubah bilangan bulat itu menjadi DateTimes yang dibangun dengan benar,

Tidak. Jangan lakukan ini dan semuanya lebih sederhana; alih-alih gunakan 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; } }

Ini berpotensi lebih fleksibel jika tanggal dan nilai offset diizinkan untuk berubah.


yang akan meningkatkan kompleksitas sedikit karena segala sesuatu yang perlu dipertimbangkan, seperti tanggal klien vs server, UTC, perbedaan waktu musim panas dll. Saya ingin menghindari menciptakan kompleksitas semacam ini pada saat ini.

Baca tentang penanganan zona waktu di sini.

Untuk saat ini cukup sertakan DateTime.DateTimeKindparameter konstruktor (enum) dan tangani nanti.


Dari salah satu jawaban:

Dengan kasus persis dua kencan Anda, rentang tanggal yang dimulai di pagi hari dan berakhir di malam hari tampaknya menjadi solusi yang sempurna. Atau mungkin tanggal awal dan bilangan bulat hari tambahan mungkin?

Gunakan DateTime.Dateproperti dan abaikan waktu sepenuhnya.

radarbob
sumber
0

Bagaimana dengan sesuatu

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

Tanggal tertentu: Barang dijamin dikirim pada tanggal itu

Keduanya EarliestShippingDatedan LatestShippingDatediatur ke tanggal pengiriman yang dijamin.

Interval hari: Item akan dikirim hari "X ke Y" mulai hari ini

EarliestShippingDatediatur ke hari ini dan LatestShippingDatediatur ketoday + (Y - X)

Tutul
sumber
0

Anda perlu memutuskan apa perbedaan (jika ada) antara tanggal pengiriman tunggal, dan rentang yang terdiri dari satu hari. Jika "tanggal pengiriman tunggal" hanya rentang hari yang terdiri dari satu hari, maka modelkan semuanya sebagai tanggal mulai dan tanggal berakhir. Jika "tanggal pengiriman tunggal" dan rentang hari dengan tanggal mulai dan berakhir yang sama harus diperlakukan secara berbeda maka simpan salah satu dari dua kasus, baik tanggal tunggal untuk tanggal pengiriman tunggal, dan dua tanggal untuk rentang hari .

gnasher729
sumber
0

Pada dasarnya Anda memiliki 2 opsi:

  • 2 tanggal. Jika objek mewakili satu tanggal, atur keduanya ke nilai yang sama, atau atur tanggal ke-2 menjadi nilai nol.

  • 1 tanggal dan jangka waktu. Tanggal mewakili awal, dan rentang waktu menunjukkan seberapa banyak rentang di masa depan. Tetapkan rentang ke 0 untuk satu tanggal.

Untuk pengiriman saya akan memiliki datetime daripada tanggal karena Anda tidak akan ragu ingin memodelkan pengiriman pagi / malam. Yang mana dari 2 opsi yang terbaik tergantung bagaimana Anda ingin menghitung tampilan. Jika Anda menampilkan "antara x dan y" maka opsi pertama mungkin lebih mudah digunakan, jika Anda menampilkan "hingga x hari dari y" maka yang terakhir lebih mudah digunakan.

Jika Anda tidak menyukai nilai nol maka opsi terakhir lebih baik, karena menghitung tanggal asli ditambah rentang waktu dapat dilakukan terlepas dari apakah rentang waktu memiliki nilai atau diatur ke 0. Anda akan selalu mendapatkan hasil yang benar tanpa memeriksa nol.

gbjbaanb
sumber