首页 > 解决方案 > 实例变量的验证依赖于另一个实例变量是不好的做法吗?

问题描述

让一个变量依赖于另一个变量的状态是不是很糟糕?

例如,我有一个Date具有属性daymonth和的类year。月份和年份可以在其各自的设置器中独立验证,但day取决于month和/或year即一个月/闰年期间的最大天数。我的方法是确保我的构造函数需要所有三个字段,并且它将为dayaftermonth和调用 setter year

不鼓励这样的状态依赖吗?我的类不是不可变的,所以我需要某种验证,我想将验证封装在类本身中,而不是在外部进行。

以下是我当前的代码:

   class Date {
      private int day;
      private int month;
      private int year;

      public String toString() {
         return String.format("%02d/%02d/%04d", month, day, year);
      }

      public int getMonth() {
         return month;
      }

      public void setMonth(int month) {
         // Check if month is valid
         if (month < 1 || month > 12) {
            throw new IllegalArgumentException(
               String.format("%02d is not a valid month", month)
               );
         } else {
               this.month = month;
         }
      }

      public int getYear() {
         return year;
      }

      public void setYear(int year) {
         // Check if year is valid
         if (year < 1900 || year > 2020) {
            throw new IllegalArgumentException(
               String.format("%04d is not a valid year", year)
               );
         } else {
            this.year = year;
         }
      }

      public int getDay() {
         return day;
      }

      public void setDay(int day) {
         // Check if day is valid
         if (day < 1 || day > getMaxNumDaysInMonth(this.getMonth(), this.getYear())) {
            throw new IllegalAccessException(
               String.format("%02d is not a valid day", day)
            );
         } else {
            this.day = day;
         }
      }

      Date(int month, int day, int year) {
         setMonth(month);
         setYear(year);
         setDay(day);
      }

      private final int[] MAX_MONTH_DAYS = {
         0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
      };

      /**
       * Gets the maximum number of days in a month
       * Depends on the month and whether the year is a leap year
       * @param month
       * @param year
       * @return
       */
      private int getMaxNumDaysInMonth(int month, int year) {

         // Check if date is valid
         if (year < 1 || month < 1 || month > 12) {
            throw new IllegalArgumentException(
               String.format("%04d-%02d is not a valid date", year, month)
            );
         } else {
            // Adjust February if year is a leap year
            if (month == 2) {
               if (isLeapYear(year)) {
                  return MAX_MONTH_DAYS[month];
               } else {
                  return MAX_MONTH_DAYS[month] - 1;
               }
            } else {
               return MAX_MONTH_DAYS[month];
            }
         }
      } // end getNumDaysInMonth


      /**
       * Returns true if year is a Leap year
       * Returns false otherwise
       * @param year
       * @return
       */
      private boolean isLeapYear(int year) {
         return (year % 4) == 0 && (year % 100 !=0 || year % 400 == 0);
      }
   } // end Date class

标签: java

解决方案


参数验证总是一个好主意,实际上也有专门为你做这种事情的类,让它变得更容易。

前提条件

      public void setMonth(int month) {
         Preconditions.checkArgument(month >= 1 && month <= 12, String.format("%s is not a valid month.", month)); 
         this.month = month.
      }

关于可变性的无关说明

如果您希望此类是不可变的,但仍允许使用 setMonth 之类的方法,则您始终可以将字段设置为 final 并返回一个带有新月份的新 Date 。

class Date {

    private final int month;

    private final int day;

    private final int year;

    // constructor

    public Date setMonth(int month) {
        // check argument is valid
        return new Date(day, month, year);
    }
}
Date date = new Date(2020, 3, 7);

date = date.setMonth(4);

你知道这已经完成了吗?

你需要创建一个管理日期的类吗?这已经存在,并且在过去几年中得到了极大的改进。

        LocalDate localDate = LocalDate.now();

        localDate = localDate.withMonth(4).withDayOfMonth(8).withYear(2021);

        LocalDateTime localDateTime = LocalDateTime.now();

        localDateTime = localDateTime.withHour(9).withMinute(45).withSecond(30).withDayOfMonth(8);

        ZonedDateTime zonedDateTime = ZonedDateTime.now();

        zonedDateTime = zonedDateTime.withZoneSameLocal(ZoneId.of("UTC"));

推荐阅读