مسیرها
/
Java
Java
/
تمرین‌ها
/
مشتریان همیشگی سالن زیبایی
مشتریان همیشگی سالن زیبایی

مشتریان همیشگی سالن زیبایی

تمرین یادگیری

مقدمه

تنظیم‌کننده‌های زمانی

وقتی با اشیایی از نوع java.time مانند LocalDate کار می‌کنید، اغلب لازم است تنظیم‌های پیچیده‌ای روی تاریخ انجام دهید، مثل پیدا کردن «دومین سه‌شنبه‌ی ماه». به‌جای اینکه این کار را دستی حساب کنید، می‌توانید از TemporalAdjusters استفاده کنید.

کلاس کمکی java.time.temporal.TemporalAdjusters (به حرف جمع «s» در انتهای اسم دقت کنید) متدهای ایستایی برای بسیاری از تنظیم‌های رایج تاریخ در اختیارتان می‌گذارد. این تنظیم‌ها را با متد .with() روی یک شیء زمانی (مانند LocalDate) اعمال می‌کنید.

Note

استفاده از with(TemporalAdjuster) یک نمونه‌ی تازه برمی‌گرداند و نمونه‌ی موجود را به‌روزرسانی نمی‌کند، چون اشیای java.time تغییرناپذیرند.

import java.time.LocalDate;
import java.time.Month;
import java.time.DayOfWeek;
import java.time.temporal.TemporalAdjusters;

LocalDate date = LocalDate.of(2024, Month.AUGUST, 15); // 2024-08-15 (a Thursday)

// Use a predefined adjuster: find the last day of the month
LocalDate lastDay = date.with(TemporalAdjusters.lastDayOfMonth());
// => 2024-08-31

در ادامه چند تنظیم‌کننده‌ی کلیدی را می‌بینید که برای محاسبات زمان‌بندی مفیدند:

یافتن روز مشخص هفته در ماه

از dayOfWeekInMonth(int ordinal, DayOfWeek dayOfWeek) استفاده کنید تا nاُمین بار تکرار یک روز هفته را در ماه پیدا کنید.

// Find the second Tuesday of August 2024
LocalDate secondTuesday = date.with(TemporalAdjusters.dayOfWeekInMonth(2, DayOfWeek.TUESDAY));
// => 2024-08-13

یافتن اولین/آخرین روز هفته در ماه

از firstInMonth(DayOfWeek dayOfWeek) یا lastInMonth(DayOfWeek dayOfWeek) استفاده کنید.

// Find the last Monday of August 2024
LocalDate lastMonday = date.with(TemporalAdjusters.lastInMonth(DayOfWeek.MONDAY));
// => 2024-08-26

تنظیم‌های نسبی (بعدی/قبلی)

تنظیم‌کننده‌هایی مثل next()، previous()، nextOrSame() و previousOrSame() نزدیک‌ترین وقوع بعدی یا قبلی یک روز مشخص از هفته را نسبت به تاریخ جاری پیدا می‌کنند.

  • next()/previous(): اگر تاریخ جاری با آن روز مطابقت داشته باشد، آن را در نظر نمی‌گیرد.
  • nextOrSame()/previousOrSame(): اگر تاریخ جاری با آن روز مطابقت داشته باشد، آن را هم در نظر می‌گیرد.
LocalDate thursday = LocalDate.of(2024, Month.AUGUST, 15); // A Thursday

// Find the next Thursday (exclusive)
LocalDate nextThursday = thursday.with(TemporalAdjusters.next(DayOfWeek.THURSDAY));
// => 2024-08-22

// Find the next Thursday (inclusive) - returns the same date
LocalDate nextOrSameThursday = thursday.with(TemporalAdjusters.nextOrSame(DayOfWeek.THURSDAY));
// => 2024-08-15

// Find the previous Monday (exclusive)
LocalDate previousMonday = thursday.with(TemporalAdjusters.previous(DayOfWeek.MONDAY));
// => 2024-08-12

تنظیم‌کننده‌های سفارشی

TemporalAdjuster یک رابط تابعی است، پس می‌توانید تنظیم‌کننده‌ی خودتان را به‌صورت یک لامبدا بنویسید که شیء زمانی موردنظر برای تنظیم را می‌گیرد و نمونه‌ی تنظیم‌شده را برمی‌گرداند. دقیقاً مانند تنظیم‌کننده‌های از پیش تعریف‌شده با .with() اعمال می‌شود و می‌تواند در داخل خودش از آن‌ها استفاده کند.

import java.time.temporal.TemporalAdjuster;

// An adjuster that moves a weekend date to the following Monday
TemporalAdjuster skipWeekend = temporal -> {
    DayOfWeek day = DayOfWeek.from(temporal);
    if (day == DayOfWeek.SATURDAY || day == DayOfWeek.SUNDAY) {
        return temporal.with(TemporalAdjusters.next(DayOfWeek.MONDAY));
    }
    return temporal;
};

LocalDate saturday = LocalDate.of(2024, Month.AUGUST, 17);
LocalDate monday = saturday.with(skipWeekend);
// => 2024-08-19

استفاده از این تنظیم‌کننده‌های کلاس TemporalAdjusters می‌تواند کارهای دستکاری تاریخ را بسیار ساده‌تر کند.

دستورالعمل‌ها

سالن زیبایی‌ای در نیویورک که در تمرین «Booking Up For Beauty» به آن کمک کردید، کارش گرفته و بسیاری از مشتریانش به مشتری ثابت تبدیل شده‌اند. مشتری ثابت به جای اینکه هر بار جداگانه نوبت بگیرد، یک قرار همیشگی دارد: «یک سه‌شنبه در میان»، «آخرین جمعه‌ی ماه» و از این قبیل. صاحب سالن از شما ابزاری کمکی خواسته که تاریخ‌های پشت این قرارها و همچنین تاریخ‌های برنامه‌ی ماهانه‌ی خود سالن را حساب کند.

1. قرار ثابت یک مشتری ثابت را پیدا کنید

متد SalonCalendar.standingAppointment() را پیاده‌سازی کنید که یک سال، یک ماه، یک تکرار (1 برای اولین، 2 برای دوم و به همین ترتیب) و یک روز از هفته می‌گیرد. این متد تاریخ آن تکرار از آن روز هفته را در آن ماه برمی‌گرداند:

SalonCalendar calendar = new SalonCalendar();
calendar.standingAppointment(2024, Month.AUGUST, 2, DayOfWeek.TUESDAY);
// => LocalDate.of(2024, 8, 13)

2. قرار آخر ماه یک مشتری ثابت را پیدا کنید

بعضی از مشتری‌های ثابت آخرین تکرار روز مورد نظرشان در ماه را ترجیح می‌دهند. متد SalonCalendar.lastAppointment() را پیاده‌سازی کنید که یک سال، یک ماه و یک روز از هفته می‌گیرد و تاریخ آخرین تکرار آن روز هفته را در آن ماه برمی‌گرداند:

SalonCalendar calendar = new SalonCalendar();
calendar.lastAppointment(2024, Month.AUGUST, DayOfWeek.FRIDAY);
// => LocalDate.of(2024, 8, 30)

3. نزدیک‌ترین روز آزاد را برای مشتری بدون نوبت پیدا کنید

مشتری‌ای پشت پذیرش می‌پرسد «شنبه‌ی بعد». اگر روزی که مشتری پرسشش را مطرح می‌کند خودش شنبه باشد، منظورش همان روز است. متد SalonCalendar.nextAvailable() را پیاده‌سازی کنید که یک تاریخ و یک روز از هفته می‌گیرد و اولین تاریخی را برمی‌گرداند که در آن روز یا بعد از آن قرار دارد و به آن روز هفته می‌افتد:

SalonCalendar calendar = new SalonCalendar();
calendar.nextAvailable(LocalDate.of(2024, 8, 15), DayOfWeek.SATURDAY);
// => LocalDate.of(2024, 8, 17)
calendar.nextAvailable(LocalDate.of(2024, 8, 17), DayOfWeek.SATURDAY);
// => LocalDate.of(2024, 8, 17)

4. روز بستن حساب‌ها را پیدا کنید

سالن حساب‌هایش را در آخرین روز هر ماه می‌بندد. اما سالن در آخر هفته تعطیل است، پس وقتی آخرین روز ماه به شنبه یا یکشنبه بیفتد، حساب‌ها در جمعه‌ی پیش از آن بسته می‌شوند. متد SalonCalendar.bookkeepingDay() را پیاده‌سازی کنید که یک سال و یک ماه می‌گیرد و روز بستن حساب‌های آن ماه را برمی‌گرداند:

SalonCalendar calendar = new SalonCalendar();
calendar.bookkeepingDay(2024, Month.SEPTEMBER);
// => LocalDate.of(2024, 9, 30)
calendar.bookkeepingDay(2024, Month.AUGUST);
// => LocalDate.of(2024, 8, 30)

5. قاعده‌ی آخر هفته را برای روز پرداخت حقوق دوباره به کار ببرید

حقوق کارکنان پانزدهم هر ماه پرداخت می‌شود، با همان قاعده‌ی آخر هفته: وقتی پانزدهم به شنبه یا یکشنبه بیفتد، روز پرداخت جمعه‌ی پیش از آن است. به جای اینکه این قاعده را بار دوم بنویسید، آن را به یک تنظیم‌کننده‌ی قابل استفاده‌ی مجدد تبدیل کنید.

متد SalonCalendar.precedingWorkday() را پیاده‌سازی کنید که یک TemporalAdjuster برمی‌گرداند؛ این تنظیم‌کننده روزهای هفته را بدون تغییر می‌گذارد و شنبه یا یکشنبه را به جمعه‌ی پیش از آن برمی‌گرداند. سپس متد SalonCalendar.payday() را پیاده‌سازی کنید که یک سال و یک ماه می‌گیرد و از همان تنظیم‌کننده استفاده می‌کند:

SalonCalendar calendar = new SalonCalendar();
LocalDate.of(2024, 6, 15).with(calendar.precedingWorkday());
// => LocalDate.of(2024, 6, 14)
calendar.payday(2024, Month.JUNE);
// => LocalDate.of(2024, 6, 14)
calendar.payday(2024, Month.AUGUST);
// => LocalDate.of(2024, 8, 15)

اگر خواستید، می‌توانید برگردید و از این تنظیم‌کننده در bookkeepingDay() هم استفاده کنید.

ویرایش از طریق GitHub این لینک در پنجره یا زبانه‌ی جدیدی باز می‌شود
Java Exercism

آماده‌اید مشتریان همیشگی سالن زیبایی را شروع کنید؟

در Exercism ثبت‌نام کنید تا Java را همراه با 27 مفهوم159 تمرین و مربی‌گری انسانی واقعی یاد بگیرید و در آن استاد شوید، همه‌ی این‌ها رایگان.