در آستانهی زمستان، از شما میخواهیم سیستم الکترونیکی یک بخاری برقی را طراحی کنید که در حالتهای مختلف میتواند عمل کند و در شرایط همروند (concurrent) عملکرد مناسبی دارد!
این بخاری تنها یک دکمه دارد که قابل کلیک کردن توسط کاربر است و به واسطهی آن کاربر میتواند آن را روشن کند (ON)، خاموش کند (OFF) و یا به حالت کم مصرف ببرد (DIM). با کلیک کردن بر روی دکمهی بخاری، بخاری به حالت بعدی میرود. هر بخاری در زمان ساخت لیستی از حالتهای عملکردی را در قالب یک آرایه دریافت میکند و در زمان کلیک شدن بر روی دکمه به حالت بعدی میرود.
برای کلیک کردن روی دکمه، دو حالت خاص وجود دارد.
- حالت خاص اول حالتی است که کاربر با فاصلهی بسیار کم از کلیک کردن قبلی مجدد کلیک کند. این حالت از این نظر حائز اهمیت است که دکمهی بخاری به گونهای طراحی شده که با نگه داشتن آن سیگنال کلیک چندین بار به برد اصلی ارسال میشود. در این حالت باید تنها اولین کلیک در نظر گرفته شود و باقی کلیکها در نظر گرفته نشوند.
- حالت خاص دوم زمانی است که کاربر در زمان روشن (ON و یا DIM) پس از مدت خیلی زیادی اقدام به کلیک کردن دکمه کند. در این حالت فرض میشود که کاربر به سادگی میخواهد بخاری را خاموش کند و نمیخواهد حالت آن را تغییر دهد. برای این حالت، مطلوب این است که بخاری به حالت خاموش برود.
این بخاری برای تامین انرژی خود به یک منبع تغذیه نیاز دارد که این منبع تغذیه قابلیت تامین انرژی با توانهای متفاوت را دارد و تنها کافی است که بخاری مقدار توان مورد نیاز خود را به آن اعلام کند. بخاری به ازای هر حالت میداند که به چه توانی نیاز دارد و به همین ترتیب میتواند بر اساس حالت کنونیاش مقدار توان مورد نیاز خود را به منبع تغذیه اعلام کند.
پس از ساخت مدار بخاری، برای ارزیابی آن در حالتهای خاص شما متوجه میشوید که به یک ابزار کمکی نیاز دارید تا سناریوهای متفاوت را بتواند به راحتی شبیهسازی کند. هدف اصلی این شبیهساز، بررسی صحت جا به جا شدن بین حالتهای مختلف با وارد کردن تعداد کلیکهای همزمان در زمانهای مختلف است. مثلا یک سناریو میتواند به این شکل باشد که: ۳ کلیک در زمان $t=100ms$ و سپس ۱۰ کلیک در زمان $t=400ms$ که انتظار داریم کلیکهای اول از حالت خاموش به روشن و کلیکهای دوم بخاری را از حالت روشن به حالت کم مصرف تغییر حالت دهند.
کلاسها
پیادهسازی این سوال در قالب چندین کلاس انجام میشود که شما باید آنها را پیادهسازی کنید. در این قسمت هر کلاس به شکل جداگانه توضیح داده میشود.
کلاس Heater
import java.time.Duration;
public class Heater {
private final Mode[] modes;
private final Duration timeout;
private final Duration closeThreshold;
private final PowerSource powerSource;
public Heater(Mode[] modes, Duration timeout, Duration closeThreshold, PowerSource powerSource) {
// TODO
}
public void click() {
// TODO
}
public Mode getCurrentMode() {
// TODO
}
}
این کلاس اولین کلاسی است که باید پیادهسازی کنید. برای پیادهسازی این کلاس لازم است سه متد شامل «سازنده»، «کلیک» و «گرفتن وضعیت فعلی» را پیادهسازی کنید.
- سازنده: این کلاس تنها یک سازنده دارد که در آن باید معتبر بودن آرایهی modes را بررسی کنید و ویژگیهای کلاس را مطابق نیاز مقداردهی کنید.
- کلیک: متد کلیک وظیفه اعمال سیگنال کلیک را بر عهده دارد. در نتیجهی اجرای این تابع باید (در صورت نیاز) حالت کنونی بخاری عوض شود و این موضوع به منبع تغذیه هم اعلام شود. همچنین مهم است که این متد به شکل ترد-سیف پیادهسازی شود چرا که ممکن است تعداد زیادی سیگنال کلیک به شکل همروند دریافت شوند.
اعتبارسنجی حالتها
آرایهای که در ورودی سازنده دریافت میشود باید اعتبارسنجی شود تا اطمینان حاصل شود که ویژگیهای زیر را دارد. اگر هر یک از ویژگیهای گفته شده را نداشت باید یک استثنا از نوع InvalidModesException
پرتاب شود.
- همهی اعضای آرایه باید غیر
null
باشند. - عضو تکراری مجاز است اما دو عضو پشت سر هم نباید تکراری باشند.
- وضعیت
OFF
تنها یک بار و آن هم دقیقا در اول آرایه مجاز است. - طول آرایه باید حداقل ۲ باشد. (یک حالت روشن یا نیمه روشن و یک حالت خاموش)
اینام Mode
برای بازنمایی حالتهای مختلف کارکردی بخاری از یک اینام استفاده شده است. این اینام سه حالت متفاوت دارد که شامل روشن و خاموش و کممصرف هستند. با استفاده از متد getPowerConsumption
میتوانید به توان مصرفی بخاری در هر یک از حالتها دست پیدا کنید که برای تنظیم منبع تغذیه ضروری است.
public enum Mode {
OFF("Heater is off", 0),
ON("Heater is on", 100),
DIM("Heater is dimmed", 50);
private final String description;
private final int powerConsumption;
Mode(String description, int powerConsumption) {
this.description = description;
this.powerConsumption = powerConsumption;
}
public String getDescription() {
return this.description;
}
public int getPowerConsumption() {
return this.powerConsumption;
}
}
اینترفیس PowerSource
public interface PowerSource {
void set(int amount);
}
این اینترفیس یک متد دارد که مقدار توان مصرفی را دریافت میکند. این اینترفیس پیادهسازیهای مختلفی بسته به تست دارد که برای اطمینان از کارکرد صحیح بخاری استفاده میشوند. همچنین یک پیادهسازی سادهی آن نیز همراه با تستهای نمونه در اختیار شما قرار گرفته است.
نکته قابل توجه این است که نباید با مقدار تکراری متدset
صدا زده شود و تنها زمانی مجاز به صدا کردن این متد هستید که یک مقدار (amount
) جدید (متفاوت با قبلی) برای تنظیم کردن داشته باشید.
کلاس Simulator
import java.time.*;
import java.util.*;
public class Simulator {
public static record Event(Instant time, int clickCount, Mode oldMode, Mode newMode) {}
public static enum Strategy {
THREAD_POOL,
THREAD,
SEQUENTIAL;
}
private final Heater heater;
private final List<Event> events;
private final Strategy strategy;
public Simulator(Heater heater, Strategy strategy, List<Event> events) {
// TODO
}
public boolean run() {
// TODO
}
}
این کلاس همانطور که توضیح داده شد، وظیفه اعمال کلیک در تعدادها و زمانهای مختلف بر روی بخاری مورد تست را دارد. همانطور که مشخص است در سازندهی خود یک بخاری برای اعمال تستها دریافت میکند که در ادامه متد کلیک آن صدا زده میشود.
رکورد Event
این رکورد برای نگهداری یک رویداد کلیک کردن استفاده میشود. یک رویداد شامل یک زمان است که با یک شی از نوع Instant
نگهداری میشود. همچنین یک عدد شامل تعداد کلیکهای همزمان نیز دریافت میشود که مشخص میکند در زمان معین چه تعداد بار باید متد کلیک صدا زده شود.
در ادامه دو حالت وضعیت نیز دریافت میشود که وضعیت قبل از کلیک و وضعیت بعد از کلیک را مشخص میکند. این وضعیتها در زمان اجرای شبیهسازی باید با وضعیت واقعی (actual) بخاری تطبیق داده شوند تا دقیقا برابر هم باشند.
اینام Strategy
در این برنامه، شبیهساز قادر به انجام شبیهسازی با سه مکانیسم متفاوت است.
- سادهترین مکانیسم به شکل
SEQUENTIAL
است که در این حالت هیچ ترد جدیدی ساخته نمیشود و همهی عملیات در همان تردmain
انجام میشود. - مکانیسم بعدی با کمک
THREAD
هاست که برای هر عملیات کلیک یک ترد مجزا ساخته میشود تا در زمان معین متد کلیک را اجرا کند. - در نهایت مکانیسم آخر با کمک
THREAD_POOL
است. در این روش با کمک یک تردپول، تسکهای مورد نظر ساخته و سابمیت میشوند. در این روش تعداد تردهای تردپول را برابر عدد ۵ (به شکل ثابت) بگذارید تا بیشتر از این ترد ساخته نشود.
توجه داشته باشید که برای پیادهسازی شبیهساز، باید تمام استراتژیها پیادهسازی شوند و قابل استفاده باشند در غیر این صورت به تناسب استراتژیهای درست پیادهسازی شده نمره دریافت میشود. انتخاب استراتژی نیز بر اساس استراتژی انتخاب شده در سازندهی شبیهسازی انجام میشود.
متد run
این متد، متد اصلی شبیهساز است که شبیهسازی را بسته به استراتژی انتخاب شده اجرا می کند. این متد یک خروجی از نوع boolean
دارد که نشان میدهد آیا شبیهسازی مطابق انتظار پیش رفت یا خیر. به بیان دیگر اگر همهی فرضیات «حالت قدیمی» و «حالت جدید»ها درست از آن در آمده باشند (که به معنی پیادهسازی صحیح بخاری است) مقدار true
و در غیر این صورت مقدار false
برمیگردد.
پروژه اولیه
پروژهی اولیهی این سؤال را میتوانید از این لینک دانلود کنید. این پروژه ساختاری مشابه زیر دارد:
initial.zip
├── Heater.java
├── InvalidModesException.java
├── Mode.java
├── PowerSource.java
├── SampleTest.java
└── Simulator.java
آنچه باید آپلود کنید.
پس از پیادهسازی و اجرای تست نمونه (موجود در فایل SampleTest.java
)، فایل های Heater.java
و Simulator.java
را در قالب یک فایل زیپ آپلود کنید. انتظار میرود باقی فایلها را تغییر ندهید و میتوانید فرض کنید که همین فایلها به همین شکل در زمان اجرای پروژه نیز در کنار برنامهی شما موجودند.
ارسال پاسخ برای این سؤال