Một thuộc tính có thể đại diện cho cả một ngày và một phạm vi ngày: Làm thế nào để mô hình hóa đúng?


8

Tôi làm việc trong một hệ thống có thể đại diện cho "ước tính vận chuyển" theo hai cách:

  1. Một ngày cụ thể: Các mặt hàng được đảm bảo để vận chuyển vào ngày đó
  2. Một khoảng thời gian trong ngày: Các mặt hàng sẽ được vận chuyển "X đến Y" ngày kể từ hôm nay

Thông tin trên mô hình giống nhau về mặt ngữ nghĩa, đó là "ước tính vận chuyển". Khi tôi nhận được thông tin về ước tính vận chuyển từ hệ thống, tôi có thể biết liệu ước tính đó là dạng thứ nhất hay dạng thứ hai.

Mô hình hiện tại cho điều này tương tự như sau:

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

Range là một lớp đơn giản để bao bọc "bắt đầu -> kết thúc" các số nguyên, hơi giống như thế này:

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

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

Tôi không thích cách tiếp cận này vì chỉ một trong số các thuộc tính trên mô hình ước tính sẽ được đưa vào và tôi cần kiểm tra null cho một trong số chúng và giả sử rằng thuộc tính kia có dữ liệu.

Mỗi thuộc tính được hiển thị khác nhau cho người dùng nhưng ở cùng một vị trí trên giao diện người dùng, sử dụng MVC DisplayTemplate tùy chỉnh, nơi logic chuyển đổi hiện tại nằm trong:

@Model EstimattedShippingDateDetails

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

Làm thế nào tôi có thể mô hình hóa điều này để làm cho nó đại diện hơn cho yêu cầu thực tế trong khi vẫn giữ logic hiển thị đơn giản trong một ứng dụng MVC?

Tôi đã nghĩ về việc sử dụng một giao diện và hai triển khai, một cho mỗi "loại" ước tính, nhưng dường như tôi không thể quấn đầu quanh một giao diện chung cho cả hai. Nếu tôi tạo một giao diện mà không có bất kỳ thành viên nào, thì tôi không thể truy cập dữ liệu theo cách thống nhất và đó là một thiết kế xấu IMHO. Tôi cũng muốn giữ cho viewmodel đơn giản nhất có thể. Tuy nhiên, tôi sẽ nhận được mã "chính xác bằng cách xây dựng" với cách tiếp cận này, vì sẽ không cần phải có bất kỳ giá trị null nào nữa: mỗi triển khai sẽ có một thuộc tính không thể rỗng, a DateTimehoặc a Range.

Tôi cũng đã cân nhắc chỉ sử dụng một lần duy nhất Rangevà khi tình huống # 1 xảy ra, chỉ sử dụng tương tự DateTimecho cả hai StartEnd, nhưng điều này sẽ làm tăng thêm sự phức tạp về cách phát ra các giá trị cho UI khi tôi sẽ phải phát hiện xem đó là tĩnh hay khoảng so sánh các giá trị và định dạng đúng phạm vi sẽ được hiển thị dưới dạng một ngày hoặc một khoảng thời gian được định dạng.

Dường như những gì tôi cần là một khái niệm tương tự như các hiệp hội của Typecript: về cơ bản là một thuộc tính có thể thuộc bất kỳ loại nào. Tất nhiên không có một thứ như vậy tồn tại trong C # (chỉ dynamiccó thể gần với điều đó).


Đây là nhiều hơn một khái niệm "Làm thế nào tôi có thể đào?" câu hỏi hơn một yêu cầu cho phê bình kết thúc mở. Di chuyển câu hỏi này để lập trình viên.
200_success 10/03/2016

@ 200_success Xin lỗi về điều đó. Tôi nghĩ rằng điều này sẽ phù hợp với codereview nhưng bạn đã đúng, nó phù hợp hơn ở đây. Cảm ơn sự giúp đỡ;)
julealgon

Là định dạng được đặt trong đá, hoặc bạn có thể ví dụ khoảng định dạng là "sẽ được giao giữa $ date1 và $ date2"?
Svick

@svick Thật không may, bây giờ nó được đặt trong đá.
julealgon

Câu trả lời:


17

Sử dụng phạm vi ngày (tức là hai ngày) cho tất cả các ước tính vận chuyển.

Đối với một ngày duy nhất, làm cho X và Y bằng nhau.


Tôi đã đề cập đến vấn đề này trong câu hỏi của riêng tôi, bạn có thể giải thích về những nhược điểm tôi đã nêu ra khi sử dụng phương pháp này không? Lưu ý rằng kịch bản khoảng là một khoảng thời gian ngày cố định và cũng không phải là một khoảng thời gian đầy đủ.
julealgon

2
Việc điền cả hai trường luôn phải loại bỏ sự phản đối của bạn thành giá trị null. Bạn có ý nghĩa gì bởi "khoảng thời gian ngày cố định và không phải là khoảng thời gian cả ngày?"
Robert Harvey

Dữ liệu tôi nhận được từ máy chủ là một DateTimeđối tượng, đại diện cho ngày giao hàng dự kiến ​​hoặc hai giá trị số nguyên với số ngày tối thiểu và tối đa kể từ hôm nay mà mặt hàng sẽ được vận chuyển. Nếu tôi chuẩn hóa theo ngày, tôi sẽ phải chuyển đổi các số nguyên đó thành DateTimes được xây dựng chính xác, điều này sẽ làm tăng độ phức tạp lên một chút do mọi thứ cần phải được tính đến, như ngày của khách hàng so với ngày của máy chủ, UTC, tiết kiệm thời gian ban ngày sự khác biệt, vv Tôi muốn tránh tạo ra loại phức tạp vào thời điểm này.
julealgon

Điểm tốt về khả năng không bao giờ có giá trị null mặc dù, điều đó sẽ khá tốt đẹp.
julealgon

Có gì sai với cách bạn đang làm bây giờ, sử dụng công tắc của bạn trong mẫu MVC?
Robert Harvey

2

Bạn có thể mô hình hóa bằng cách sử dụng đóng gói.

Để lớp có 2 hàm tạo, một cho một ngày và một cho phạm vi ngày.

Trong phương thức ToString () xác định 'trạng thái' của lớp và tạo chuỗi được định dạng thích hợp.

Thêm các phương pháp khác khi thích hợp cho các nhu cầu khác của bạn.


Điều đó sẽ hoạt động tốt trên một hệ thống đơn giản hơn, nhưng lưu ý rằng tôi cần hiển thị chính xác điều này bằng cách sử dụng các khung nhìn MVC và Rangeloại này có thể được sử dụng ở những nơi khác trên hệ thống và trong trường hợp đó được hiển thị theo cách tương tự. Đó là lý do tại sao tôi cần tập trung Rangemàn hình vào DisplayTemplate có thể được sử dụng lại. Nếu tôi sử dụng ToStringphương pháp để gói gọn điều đó, tôi sẽ mất rất nhiều tính linh hoạt mà các khung nhìn cung cấp.
julealgon

Ngoài ra, làm thế nào để bạn đề xuất tôi biết "trạng thái" của đối tượng? Một biến enum cục bộ của các loại? Một boolean? Tôi cho rằng nó sẽ được đặt thành các giá trị khác nhau tùy thuộc vào hàm tạo nào được gọi đúng không? Để thực hiện công việc này, có lẽ tôi cũng cần phải làm cho lớp không thay đổi hoặc tôi có thể gặp sự cố nếu ai đó đặt giá trị khác, v.v. Tôi phải làm gì nếu một người sử dụng hàm tạo ngày duy nhất và cố gắng truy cập Phạm vi tài sản quá? Tôi sẽ ném một ngoại lệ trong trường hợp đó hoặc chỉ trả về null? Bạn thấy đấy, khả năng sử dụng này vẫn chưa lý tưởng.
julealgon

Có, về cơ bản, lớp sẽ không thay đổi với 2 ngày được đọc và gán trong các hàm tạo. Trạng thái có thể được xác định bởi một bool và / hoặc có cùng ngày để đại diện cho một ngày duy nhất. Các chức năng thành viên khác của bạn có thể sử dụng trạng thái một cách thích hợp và bạn cũng có thể phơi bày trạng thái, nếu cần thiết. Xin lỗi không thể cụ thể hơn vì tôi không biết tất cả các chức năng bạn cần hỗ trợ.
hocho

Điều @hocho đang nói là tất cả các ngày giao hàng có thể được mô hình thành các phạm vi, chỉ là một số phạm vi có cùng ngày bắt đầu và ngày kết thúc. Điều này có vẻ khá hợp lý. Mã khách hàng sử dụng Phạm vi phải được chuẩn bị để đối phó với ngày bắt đầu và ngày kết thúc của cùng một ngày và trong trường hợp đó có thể in thông tin khác nhau (tức là một ngày so với phạm vi ngày) trong email.
Erik Eidt

bỏ phiếu cho ..ToString () [để] xác địnhstate . ToString()chỉ nên "báo cáo" nhà nước. Nhà nước được xác định trong các nhà xây dựng, setters tài sản, vv. Và, tôi chỉ không thấy bất cứ điều gì trong OP cho thấy có hoặc nên có một "trạng thái tóm tắt"
radarbob 13/03/2016

1

Đây là một vấn đề khá phổ biến, ví dụ Nullables giải quyết vấn đề phổ biến về endDates cho những thứ chưa kết thúc.

Nhưng tôi nghĩ bạn đã chọn một ví dụ xấu.

Với trường hợp chính xác của bạn là hai ngày, một phạm vi ngày bắt đầu vào buổi sáng và kết thúc vào buổi tối dường như là giải pháp hoàn hảo. Hoặc perhpas một ngày bắt đầu và số nguyên của ngày thêm có thể là?

Tuy nhiên, hãy xem xét một trường hợp khó khăn hơn. Tôi có hai loại giao hàng, gửi và nhận từ cửa hàng. Đây là những obv khác nhau nhiều, cửa hàng cần tên và địa chỉ, bài đăng có chi phí, có thể có một số tùy chọn giao hàng, mã theo dõi, vv

Cách tiếp cận tiêu chuẩn là tìm kiếm những điều phổ biến tạo ra cả hai 'tùy chọn phân phối' này và đưa những thứ đó vào một lớp cơ sở. Lớp con hai trường hợp cụ thể với các chi tiết bổ sung mà họ có / cần.

Trong hầu hết các trường hợp, bạn sẽ có ít nhất một Id, Loại và Mô tả chung cho cả hai loại. Vì thế:

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

... một phạm vi ngày bắt đầu vào buổi sáng và kết thúc vào buổi tối dường như là giải pháp hoàn hảo. Chỉ cần sử dụng DataTime.Datetài sản và không lo lắng về thời gian.
radarbob

1

"bắt đầu" và "kết thúc" không phải là DateTime, chúng là Offsets

Làm thế nào tôi có thể mô hình hóa điều này để làm cho nó đại diện hơn cho yêu cầu thực tế trong khi vẫn giữ logic hiển thị đơn giản trong một ứng dụng MVC?

"bắt đầu" và "kết thúc" là sự bù đắp cho EstimatedShipDate. Họ không phải DateTimelà chính họ . Điều này mô tả tốt hơn những gì đang xảy ra và nó sẽ làm giảm đáng kể sự phức tạp.

Không cần một interface. Không cần một Rangelớp học. Đừng phức tạp cho đến khi bạn chắc chắn rằng bạn cần nó. Tôi nghi ngờ rằng một lớp duy nhất với hàm tạo 3 tham số tùy chọn sẽ giữ mọi thứ đơn giản hơn nhiều.


Dữ liệu tôi nhận được từ máy chủ là đối tượng DateTime, đại diện cho ngày giao hàng dự kiến ​​hoặc hai giá trị số nguyên với số ngày tối thiểu và tối đa kể từ hôm nay mà mặt hàng sẽ được vận chuyển.

Sử dụng một hàm tạo duy nhất chuyển cả 3 giá trị qua các tham số tùy chọn. Điều này cung cấp tất cả các bối cảnh cần thiết. Sau đó, logic xây dựng đơn có thể đánh giá tất cả các biến thể để đặt trạng thái ban đầu chính xác. Cũng sử dụng các tham số được đặt tên trong lệnh gọi hàm tạo nếu muốn làm cho nó rõ ràng.

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

Nếu tôi chuẩn hóa theo ngày, tôi sẽ phải chuyển đổi các số nguyên đó thành DateTimes được xây dựng chính xác,

Không. Đừng làm điều này và mọi thứ đơn giản hơn; thay vì sử dụng 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; } }

Điều này có khả năng linh hoạt hơn nếu ngày và giá trị bù được phép thay đổi.


điều này sẽ làm tăng độ phức tạp lên một chút do mọi thứ cần phải tính đến, như máy khách so với ngày máy chủ, UTC, chênh lệch thời gian ban ngày, v.v. Tôi muốn tránh tạo ra sự phức tạp này vào thời điểm này.

Đọc về xử lý múi giờ ở đây.

Bây giờ chỉ cần bao gồm một DateTime.DateTimeKindtham số hàm tạo (enum) và xử lý nó sau.


Từ một trong những câu trả lời:

Với trường hợp chính xác của bạn là hai ngày, một phạm vi ngày bắt đầu vào buổi sáng và kết thúc vào buổi tối dường như là giải pháp hoàn hảo. Hoặc perhpas một ngày bắt đầu và số nguyên của ngày thêm có thể là?

Sử dụng DateTime.Datetài sản và hoàn toàn bỏ qua thời gian.


0

Thế còn một cái gì đó như

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

Một ngày cụ thể: Các mặt hàng được đảm bảo để vận chuyển vào ngày đó

Cả hai EarliestShippingDateLatestShippingDateđược thiết lập để ngày vận chuyển được đảm bảo.

Một khoảng thời gian trong ngày: Các mặt hàng sẽ được vận chuyển "X đến Y" ngày kể từ hôm nay

EarliestShippingDateđược đặt thành ngày hôm nay và LatestShippingDateđược đặt thànhtoday + (Y - X)


0

Bạn cần quyết định sự khác biệt (nếu có) giữa một ngày giao hàng và phạm vi bao gồm một ngày. Nếu "ngày giao hàng duy nhất" chỉ là một phạm vi ngày xảy ra bao gồm một ngày, thì hãy mô hình hóa mọi thứ là ngày bắt đầu và ngày kết thúc. Nếu một "ngày vận chuyển duy nhất" và một phạm vi ngày có cùng ngày bắt đầu và ngày kết thúc sẽ được đối xử khác nhau thì hãy lưu trữ một trong hai trường hợp, một ngày duy nhất cho một ngày vận chuyển và hai ngày trong một ngày .


0

Về cơ bản, bạn có 2 tùy chọn:

  • 2 ngày. Nếu đối tượng đại diện cho một ngày duy nhất, hãy đặt cả hai thành cùng một giá trị hoặc đặt ngày thứ 2 thành giá trị null.

  • 1 ngày và một khoảng thời gian. Ngày đại diện cho sự bắt đầu và khoảng thời gian cho thấy phạm vi có thể là bao nhiêu trong tương lai. Đặt phạm vi thành 0 cho một ngày.

Đối với vận chuyển tôi sẽ có một thời gian chứ không phải là một ngày vì bạn chắc chắn sẽ muốn mô hình giao hàng buổi sáng / buổi tối. Cái nào trong số 2 tùy chọn là tốt nhất phụ thuộc vào cách bạn muốn tính toán màn hình. Nếu bạn đang hiển thị "giữa x và y" thì tùy chọn đầu tiên có thể dễ sử dụng hơn, nếu bạn hiển thị "tối đa x ngày từ y" thì tùy chọn thứ hai sẽ dễ sử dụng hơn.

Nếu bạn không thích các giá trị null thì tùy chọn thứ hai sẽ tốt hơn, vì việc tính ngày ban đầu cộng với thời gian có thể được thực hiện bất kể thời gian có giá trị hay được đặt thành 0. Bạn sẽ luôn nhận được kết quả chính xác mà không cần kiểm tra null.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.