Заранее извиняюсь за многословный вопрос. Отзывы особенно ценятся здесь. . .
В моей работе мы много чего делаем с диапазонами дат (дата периоды , если хотите). Нам нужно провести всевозможные измерения, сравнить перекрытие между двумя периодами дат и т. Д. Я разработал интерфейс, базовый класс и несколько производных классов, которые хорошо удовлетворяют мои потребности на сегодняшний день:
Урезанный до сути суперкласс DatePeriod выглядит следующим образом (опускает все интересные особенности, которые являются основой того, почему нам нужен этот набор классов ...):
(Псевдокод 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 совпадает с конструктором суперкласса (в том смысле, что он имеет параметры 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).
{
}
Прошу прощения за длинную преамбулу. Учитывая вышеизложенную ситуацию, может показаться, что эта структура классов нарушает принцип подстановки Лискова. Хотя можно использовать экземпляр CalendarMonth в любом случае, когда можно использовать более общий класс DatePeriod, поведение вывода ключевых методов будет другим. Другими словами, нужно знать, что в данной ситуации используется экземпляр CalendarMonth.
Хотя CalendarMonth (или CalendarWeek и т. Д.) Придерживается контракта, установленного посредством использования IDatePeriod базовым классом, результаты могут сильно искажаться в ситуации, когда использовался CalendarMonth, а поведение обычного старого DatePeriod ожидалось. . . (Обратите внимание, что ВСЕ другие фанковые методы, определенные в базовом классе, работают правильно - в реализации CalendarMonth различаются только настройки начальной и конечной дат.)
Есть ли лучший способ структурировать это так, чтобы обеспечить надлежащее соблюдение в LSP без ущерба для удобства использования и / или без дублирования кода?