事先为一个长期待解决的问题表示歉意。反馈特别感谢在这里。 。 。
在我的工作中,我们做了很多关于日期范围(日期周期,如果可以的话)的事情。我们需要进行各种测量,比较两个日期周期之间的重叠程度,等等。我设计了一个Interface,一个基类和几个派生类,这些类可以满足我迄今为止的需求:
除去其本质,DatePeriod父类(super class)如下(省略了所有令人着迷的功能,这是我们为什么需要这套类的基础...):
(Java伪代码):
class datePeriod implements IDatePeriod
protected Calendar periodStartDate
protected Calendar periodEndDate
public DatePeriod(Calendar startDate, Calendar endDate) throws DatePeriodPrecedenceException
{
periodStartDate = startDate
. . .
// Code to ensure that the endDate cannot be set to a date which
// precedes the start date (throws exception)
. . .
periodEndDate = endDate
{
public void setStartDate(Calendar startDate)
{
periodStartDate = startDate
. . .
// Code to ensure that the current endDate does not
// precede the new start date (it resets the end date
// if this is the case)
. . .
{
public void setEndDate(Calendar endDate) throws datePeriodPrecedenceException
{
periodEndDate = EndDate
. . .
// Code to ensure that the new endDate does not
// precede the current start date (throws exception)
. . .
{
// a bunch of other specialty methods used to manipulate and compare instances of DateTime
}
基类包含大量用于操作日期期间类的相当专门的方法和属性。派生类仅更改设置有关期间的起点和终点的方式。例如,对我来说,CalendarMonth对象确实是“是-” DatePeriod才有意义。但是,出于明显的原因,日历月是固定持续时间的,并且具有特定的开始和结束日期。实际上,尽管CalendarMonth类的构造函数与父类(super class)的构造函数匹配(因为它具有startDate和endDate参数),但实际上这是简化构造函数的重载,该构造函数仅需要单个Calendar对象。
对于CalendarMonth,提供任何日期都将导致CalendarMonth实例开始于相关月份的第一天,并于该月的最后一天结束。
public class CalendarMonth extends DatePeriod
public CalendarMonth(Calendar dateInMonth)
{
// call to method which initializes the object with a periodStartDate
// on the first day of the month represented by the dateInMonth param,
// and a periodEndDate on the last day of the same month.
}
// For compatibility with client code which might use the signature
// defined on the super class:
public CalendarMonth(Calendar startDate, Calendar endDate)
{
this(startDate)
// The end date param is ignored.
}
public void setStartDate(Calendar startDate)
{
periodStartDate = startDate
. . .
// call to method which resets the periodStartDate
// to the first day of the month represented by the startDate param,
// and the periodEndDate to the last day of the same month.
. . .
{
public void setEndDate(Calendar endDate) throws datePeriodPrecedenceException
{
// This stub is here for compatibility with the superClass, but
// contains either no code, or throws an exception (not sure which is best).
{
}
很长的序言道歉。在上述情况下,该类结构似乎违反了Liskov替代原则。尽管在可能使用更通用的DatePeriod类的任何情况下,一个CAN都可以使用CalendarMonth的实例,但是关键方法的输出行为将有所不同。换句话说,必须知道在给定情况下正在使用CalendarMonth的实例。
虽然CalendarMonth(或CalendarWeek等)遵守通过基类使用IDatePeriod建立的契约(Contract),但在使用CalendarMonth且预期使用普通的DatePeriod的情况下,结果可能会严重扭曲。 。 。 (请注意,在基类上定义的所有其他时髦方法都可以正常工作-只是开始日期和结束日期的设置与CalendarMonth实现的不同)。
有没有更好的方法来构造这种结构,从而可以在不影响可用性和/或复制代码的情况下保持对LSP的正确遵循?
最佳答案
这似乎与关于“正方形”和“矩形”的通常讨论类似。尽管正方形是矩形,但对于Square来说,从矩形继承是没有用的,因为它不能满足矩形的预期行为。
您的DatePeriod具有setStartDate()和setEndDate()方法。使用DatePeriod,您希望可以以任何顺序调用两者,而不会互相影响,并且可能它们的值将精确地指定开始和结束日期。但是对于CalendarMonth实例,这不是事实。
也许不是让CalendarMonth扩展DatePeriod,而是两者都可以扩展一个公共(public)的抽象类,该类仅包含与两者兼容的方法。
顺便说一句,根据您问题的体贴程度,我想您已经考虑过寻找现有的日期库。以防万一,请务必查看Joda time库,该库包含可变和不可变期间的类。如果现有的库解决了您的问题,则您可以专注于自己的软件,并让其他人承担设计,开发和维护时间库的费用。
编辑:注意,我已经将CalendarMonth类称为Calendar。固定为清楚起见。