مسیرها
/
Java
Java
/
تمرین‌ها
/
معمای ماشین‌حساب
معمای ماشین‌حساب

معمای ماشین‌حساب

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

مقدمه

استثناها

زبان برنامه‌نویسی Java برای رسیدگی به خطاها و سایر رویدادهای استثنایی از «استثنا»ها استفاده می‌کند.

استثنا چیست

استثنا رویدادی است که در حین اجرای یک برنامه رخ می‌دهد و جریان عادی دستورها را مختل می‌کند. استثناها در Java به‌صورت صریح ایجاد می‌شوند و به عمل ایجاد یک استثنا «پرتاب استثنا» می‌گویند. به عمل رسیدگی به یک استثنا «گرفتن استثنا» می‌گویند.

در Java، همه‌ی استثناها زیرکلاس‌های کلاس Exception هستند که خودش زیرکلاسی از Throwable است.

Java دو نوع استثنا را از هم تفکیک می‌کند:

  1. استثناهای بررسی‌شده
  2. استثناهای بررسی‌نشده
استثناهای بررسی‌شده

«استثناهای بررسی‌شده» شرایط استثنایی‌ای هستند که یک برنامه باید آن‌ها را پیش‌بینی کند و از آن‌ها بهبود یابد. نمونه‌ای از استثنای بررسی‌شده، FileNotFoundException است که وقتی رخ می‌دهد که یک متد در تلاش است فایلی را بخواند که وجود ندارد.

این نوع استثنا در زمان کامپایل بررسی می‌شود: متدهایی که استثنای بررسی‌شده پرتاب می‌کنند باید این را در امضای متد خود مشخص کنند، و کدی که متدی را فراخوانی می‌کند که ممکن است استثنای بررسی‌شده پرتاب کند، ملزم است آن را رسیدگی کند، وگرنه کد کامپایل نمی‌شود.

همه‌ی استثناهای بررسی‌شده زیرکلاس‌های Exception هستند که RuntimeException را گسترش نمی‌دهند.

استثناهای بررسی‌نشده

«استثناهای بررسی‌نشده» شرایط استثنایی‌ای هستند که یک برنامه معمولاً نمی‌تواند آن‌ها را پیش‌بینی کند یا از آن‌ها بهبود یابد. نمونه‌ای از استثنای بررسی‌نشده، NullPointerException است که وقتی رخ می‌دهد که متدی در انتظار مقداری غیر از null است اما null دریافت می‌کند.

این نوع استثنا در زمان کامپایل بررسی نمی‌شود: متدهایی که استثنای بررسی‌نشده پرتاب می‌کنند ملزم نیستند این را در امضای متد خود مشخص کنند، و کدی که متدی را فراخوانی می‌کند که ممکن است استثنای بررسی‌نشده پرتاب کند، ملزم نیست آن را رسیدگی کند.

همه‌ی استثناهای بررسی‌نشده از RuntimeException ارث می‌برند که خودش گسترشی از Exception است.

پرتاب استثناها

یک متد در Java می‌تواند با استفاده از دستور throw یک استثنا پرتاب کند.

پرتاب یک استثنای بررسی‌شده

وقتی استثنای بررسی‌شده‌ای از یک متد پرتاب می‌شود، لازم است این در امضای متد با استفاده از کلیدواژه‌ی throws مشخص شود، همان‌طور که در مثال زیر نشان داده شده است. این کار کد فراخوان را مجبور می‌کند پیش‌بینی کند که ممکن است استثنایی پرتاب شود و بر همان اساس آن را رسیدگی کند.

public class InsufficientBalanceException extends Exception {

}

public class BankAccount {
  public void withdraw(double amount) throws InsufficientBalanceException {
    if (balance < amount) {
      throw new InsufficientBalanceException();
    }

    // rest of the method implementation
  }
}
پرتاب یک استثنای بررسی‌نشده

وقتی استثنای بررسی‌نشده‌ای از یک متد پرتاب می‌شود، لازم نیست این در امضای متد مشخص شود، هرچند پشتیبانی می‌شود.

public class BankAccount {
  public void withdraw(double amount) {
    if (amount < 0) {
      throw new IllegalArgumentException("Cannot withdraw a negative amount");
    }

    // rest of the method implementation
  }
}

رسیدگی به استثناها

رسیدگی به استثناها در Java با کلیدواژه‌های try، catch و finally انجام می‌شود.

  • دستورهای کدی که ممکن است استثنایی پرتاب کنند باید در یک بلوک try پیچیده شوند.
  • پس از بلوک try یک یا چند بلوک catch می‌آید که استثناهای پرتاب‌شده در بلوک try را می‌گیرند.
  • پس از بلوک‌های catch به‌صورت اختیاری یک بلوک finally می‌آید که همیشه پس از بلوک try اجرا می‌شود، صرف‌نظر از اینکه استثنایی پرتاب شده باشد یا نه.

مثال زیر نشان می‌دهد که این کلیدواژه‌ها چگونه کار می‌کنند:

public class ATM {
  public void withdraw(BankAccount bankAccount, double amount) {
    try {
      System.out.println("Withdrawing " + amount);
      bankAccount.withdraw(amount);
      System.out.println("Withdrawal succeeded");
    } catch (InsufficientBalanceException) {
      System.out.println("Withdrawal failed: insufficient balance");
    } catch (RuntimeException e) {
      System.out.println("Withdrawal failed: " + e.getMessage());
    } finally {
      System.out.println("Current balance: " + bankAccount.getBalance());
    }
  }
}

در این مثال، وقتی هیچ استثنایی پرتاب نمی‌شود، این خروجی چاپ می‌شود:

Withdrawing 10.0
Withdrawal succeeded
Current balance: 5.0

اما اگر دستور bankAccount.withdraw(amount) یک InsufficientBalanceException پرتاب کند، این خروجی چاپ می‌شود:

Withdrawing 10.0
Withdrawal failed: insufficient balance
Current balance: 5.0

یا اگر استثنای بررسی‌نشده‌ای توسط bankAccount.withdraw(amount) پرتاب شود، این خروجی چاپ می‌شود:

Withdrawing -10.0
Withdrawal failed: Cannot withdraw a negative amount
Current balance: 5.0

خطاها

Java دسته‌ی جداگانه‌ای هم دارد به اسم «خطا»ها که مسائل جدی‌ای خارج از یک برنامه هستند. نمونه‌ای از خطا، OutOfMemoryError است که وقتی رخ می‌دهد که یک برنامه در تلاش است حافظه‌ای بیشتر از حافظه‌ی موجود در سیستم استفاده کند.

همانند استثناهای بررسی‌نشده، خطاها در زمان کامپایل بررسی نمی‌شوند. تفاوت این است که آن‌ها نماینده‌ی مسائل سطح سیستم هستند و عموماً توسط ماشین مجازی Java یا محیط پرتاب می‌شوند، نه توسط برنامه. برنامه‌ها عموماً نباید تلاش کنند آن‌ها را بگیرند یا رسیدگی کنند.

همه‌ی خطاها در Java از کلاس Error ارث می‌برند.

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

در این تمرین، مدیریت خطا را برای یک ماشین‌حساب ساده‌ی اعداد صحیح پیاده‌سازی می‌کنید. برای اینکه کار ساده‌تر شود، متدهایی برای محاسبه‌ی جمع، ضرب و تقسیم در اختیار شما قرار داده شده است.

هدف این است که یک ماشین‌حساب کارآمد داشته باشید که با دریافت آرگومان‌های 16، 51 و +، یک String با الگوی زیر برگرداند: 16 + 51 = 67.

CalculatorConundrum calculator = new CalculatorConundrum();

calculator.calculate(16, 51, "+");
// => returns "16 + 51 = 67"

calculator.calculate(32, 6, "*");
// => returns "32 * 6 = 192"

calculator.calculate(512, 4, "/");
// => returns "512 / 4 = 128"

1. پیاده‌سازی متد calculate برای پشتیبانی از چند عملیات پایه

متد اصلی که در این تمرین باید پیاده‌سازی کنید، متد CalculatorConundrum.calculate() است. این متد سه آرگومان می‌گیرد. دو آرگومان اول یعنی operand1 و operand2 اعداد صحیحی هستند که عملیات روی آن‌ها انجام می‌شود. آرگومان سوم یعنی operation از نوع String است و در این تمرین باید عملیات زیر را پیاده‌سازی کنید:

  • جمع با استفاده از رشته‌ای که + است
  • ضرب با استفاده از رشته‌ای که * است
  • تقسیم با استفاده از رشته‌ای که / است

2. مدیریت عملیات غیرمجاز

متد CalculatorConundrum.calculate() را به‌روزرسانی کنید تا عملیات غیرمجاز را مدیریت کند:

  • وقتی آرگومان operation برابر null باشد، باید یک IllegalArgumentException با پیام Operation cannot be null پرتاب شود.
  • وقتی آرگومان operation برابر "" باشد، باید یک IllegalArgumentException با پیام Operation cannot be empty پرتاب شود.
  • وقتی آرگومان operation هر عملیاتی به‌جز +، * یا / باشد، باید یک IllegalOperationException با پیام Operation '{operation}' does not exist پرتاب شود.
calculator.calculate(10, 1, null);
// => throws IllegalArgumentException with message "Operation cannot be null"

calculator.calculate(10, 1, "");
// => throws IllegalArgumentException with message "Operation cannot be empty"

calculator.calculate(10, 1, "-");
// => throws IllegalOperationException with message "Operation '-' does not exist"

3. مدیریت استثنایی که هنگام تقسیم بر صفر پرتاب می‌شود

در Java، تلاش برای تقسیم بر صفر یک ArithmeticException پرتاب می‌کند. متد CalculatorConundrum.calculate() را به‌روزرسانی کنید تا این استثنا را بگیرد و سپس یک IllegalOperationException با پیام Division by zero is not allowed و ArithmeticException گرفته‌شده به‌عنوان علت آن پرتاب کند.

calculator.calculate(512, 0, "/");
// => throws IllegalOperationException with message "Division by zero is not allowed"
ویرایش از طریق GitHub این لینک در پنجره یا زبانه‌ی جدیدی باز می‌شود
Java Exercism

آماده‌اید معمای ماشین‌حساب را شروع کنید؟

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