Inheritance and LSP

Sorry in advance for the long question. Feedback is especially appreciated here.,.

In my work, we do a lot of things with date ranges (date periods, if you want). We need to take all kinds of measurements, compare the overlap between two dates, etc. I developed an interface, a base class, and several derived classes that serve my needs well:

  • IDatePeriod
  • DatePeriod
  • Calendarmonth
  • Calendarweek
  • Fiscalyear

Divided at its core, the superclass of the DatePeriod class looks like this (omits all the fun features that are the basis for why we need this set of classes ...):

(Java pseudo code):

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).
    {
}

. , , . CAN CalendarMonth , DatePeriod, . , , CalendarMonth.

CalendarMonth ( CalendarWeek ..) , IDatePeriod , , CalendarMonth, DatePeriod., ( , -, , - , CalendarMonth).

, LSP / ?

+5
4

. - , Rectangle, .

DatePeriod setStartDate() setEndDate(). DatePeriod , , , , , . CalendarMonth .

, , CalendarMonth DatePeriod, , , .

, , , . , , Joda time, . , - , .

: , CalendarMonth Calendar. .

+6

, , CalendarMonth . , , , factory .

CalendarMonth , Periods, , IDatePeriod.

final IDatePeriod period = Periods.wholeMonthBounding(Calendar day);

wholeMonthBounding() , IDatePeriod. Bikeshedding, wholeMonthContaining().


, "". " ", " ?", .

, ,

interface PeriodPredicate
{
  boolean containsMoment(Calendar day);
}

Periods — , bettern PeriodPredicates — ,

// First, some absolute periods:
PeriodPredicate allTime(); // always returns true
PeriodPredicate everythingBefore(Calendar end);
PeriodPredicate everythingAfter(Calendar start);
enum Boundaries
{
  START_INCLUSIVE_END_INCLUSIVE,
  START_INCLUSIVE_END_EXCLUSIVE,
  START_EXCLUSIVE_END_INCLUSIVE,
  START_EXCLUSIVE_END_EXCLUSIVE
}
PeriodPredicate durationAfter(Calendar start, long duration, TimeUnit unit,
                              Boundaries boundaries);
PeriodPredicate durationBefore(Calendar end, long duration, TimeUnit unit
                               Boundaries boundaries);

// Consider relative periods too:
PeriodPredicate inThePast();   // exclusive with now
PeriodPredicate inTheFuture(); // exclusive with now
PeriodPredicate withinLastDuration(long duration, TimeUnit unit); // inclusive from now
PeriodPredicate withinNextDuration(long duration, TimeUnit unit); // inclusive from now
PeriodPredicate withinRecentDuration(long pastOffset, TimeUnit offsetUnit,
                                     long duration, TimeUnit unit,
                                     Boundaries boundaries);
PeriodPredicate withinFutureDuration(long futureOffset, TimeUnit offsetUnit,
                                     long duration, TimeUnit unit,
                                     Boundaries boundaries);

. , - .

+2

, LSP, , .

, Java Collection add(E). :

.

, Set, - , LSP. , add(E) :

, ( ).

Collection , , .

, , . , , setStartDate() :

, .

- ? ,

, , , .

setEndDate() . LSP?

, LSP, .

+1

, , LSP, , Ellipse Circle.

CalendarMonth DatePeriod, DatePeriod .

Then you can either change all mutation methods to those that return a new one DatePeriod, and keep everything beautifully unchanged, or make an alternative mutable subclass that does not try to cope with years, months, weeks, etc.

+1
source

All Articles