دانستنی‌ها

به‌روش‌ها در طراحی کلاس استثنا در جاوا

پیش‌تر در این مطلب در جاواکاپ، در مورد به‌روش‌ها در استفاده از کلاس‌های استثنا خواندیم، اما این مطلب فقط در مورد قسمت دریافت و پرتاب استثنا بود. اگر این مطلب را خوانده باشید، احتمالا می‌دانید که باید بیشترین اطلاعات ممکن را درباره موقعیت و شرایط بروز استثنا فراهم کنیم و هیچ اطلاعاتی را نباید حذف کنیم، چرا که ممکن است این اطلاعات برای متد فراخوانی‌کننده ارزشمند باشد.

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

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

به‌روش‌ها در استفاده از استثنا‌های شخصی

این ۴ توصیه اصلی را باید زمان پیاده‌سازی یک استثنای شخصی در نظر بگیرید. آن‌ها کمک می‌کنند که کد و API شما بهتر فهمیده شود، میزان داکیومنت موردنیاز کم‌تر باشد. همچنین اثرشان در زمانی که برنامه‌نویس‌های جدید به تیم اضافه‌شوند و یا بخواهند از APIهای شما استفاده کنند، محسوس‌تر می‌شود.

۱- همیشه اضافه کردن استثنا باید فایده‌ای داشته باشد

کلاس استثنایی که تعریف می‌کنید باید حتما اطلاعات یا قابلیتی اضافه بر کلاسِ استانداردِ جاوا داشته باشد.
در صورتی که استثنای شما هیچ افزوده‌ای بر استثنا‌ی استاندارد نداشته باشد، بهتر است از یکی از کلاس‌های استثنای خود جاوا مثل UnsupportedOperationException و یا IllegalArgumentException استفاده کنید. همه برنامه‌نویسان جاوا از پیش با این استثناها آشنا هستند و اینکار کمک می‌کند API شما راحت‌تر فهمیده شود.

۲- قواعد نام‌گذاری را رعایت کنید

وقتی با دقت به نام کلاس‌های استثنای خودِ جاوا نگاه کنیم، متوجه می‌شویم که نام همه آن‌ها با Exception تمام می‌شود. این قاعده نام‌گذاری در  سراسر اکوسیستمِ جاوا دیده می‌شود. شما هم در نام‌گذاری کلاس استثنای خود این نکته را مد نظر داشته باشید.

۳- برای کلاس استثنای خود، جاواداک بنویسید

البته این موضوع احتیاجی به ذکر ندارد ولی بسیاری از کلاس‌های استثنا، بدون جاواداک مشاهده می‌شوند.

یک قاعده و به‌روش کلی وجود دارد که برای همه‌ی کلاس‌ها، فیلد‌ها و سازنده‌ها و متد‌های API خود، داکیومنت بنویسید. اگر تا کنون محبور شده‌اید از یک API داکیومنت‌نشده استفاده کنید، این موضوع را بهتر درک می‌کنید که چرا استفاده از API‌های داکیومنت‌نشده بسیار سخت و دشوار است.

شاید به نظر برسد که کلاس استثنا، قسمت مهمی برای داکیومنت نوشتن نیست و تنها اکتفا به متدها کافی باشد ولی اینگونه نیست. زمانی که استثنای مورد بحث پرتاب شود، استثنا هم جرو API خواهد بود و نیاز به داکیومنتِ خوب خواهد داشت.

جاواداک باید معنای کلی این استثنا و شرایطی که رخ می‌دهد را نشان دهد، با این هدف که توسعه‌دهندگان دیگر، API را درک کنند.

/**
 * The MyBusinessException wraps all checked standard Java exception and enriches them with a custom error code.
 * You can use this code to retrieve localized error messages and to link to our online documentation.
 * 
 * @author TJanssen
 */
public class MyBusinessException extends Exception { ... }

۴- سازنده‌ای بنویسید که «پرتاب‌شونده» دیگری را به عنوان «علت» قبول کند

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

در مثال پیش رو، استثنای NumberFormatException، اطلاعات دقیقی درباره‌ علت خطا فراهم می‌کند. اگر همین شی استثنای دریافتی را به عنوان علت (cause) استثنای شخصی قرار ندهیم، اطلاعات مفید آن را از دست می‌دهیم.

public void wrapException(String input) throws MyBusinessException {
	try {
		// do something
	} catch (NumberFormatException e) {
		throw new MyBusinessException("A message that describes the error.", e, ErrorCode.INVALID_PORT_CONFIGURATION);
	}
}

خود کلاس‌های Exception و RuntimeException سازنده‌ای دارند که یک Throwable به عنوان cause گرفته که علت بروز استثنا را مشخص می‌کند. کلاس استثنای شما هم باید دقیقا همین کار را انجام دهد. باید حداقل یک سازنده وجود داشته باشد که پارامتر Throwable بگیرد و آن را در ابرکلاس تنظیم کند.

public class MyBusinessException extends Exception {

	public MyBusinessException(String message, Throwable cause, ErrorCode code) {
		super(message, cause);
		this.code = code;
	}

	...
}

پیاده‌سازی یک استثنای شخصی‌سازی شده

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

برای پیاده‌سازی یک استثنای چک‌شده باید کلاس ما از کلاس Exception ارث‌بری کند. همچنین لازم است سازنده‌‌ای اضافه کنیم که «علت» را تنظیم کند.

همان طور که می‌بینید،‌ مثال زیر همه این کارها را انجام داده است، جاواداک مناسبی دارد که استثنا را توصیف می‌کند، سازنده‌ای دارد که «علت» را تنظیم می‌کند و همچنین افزوده‌ای بر استثنای استاندارد دارد. کلاس استثنای MyBusinessException، از یک داده‌ی شمارشی (enum) برای نگهداری ارورکُد استفاده می‌کند. استفاده‌کنندگان می‌توانند از این کُد خطا استفاده کنند تا پیغام خطاهای مناسب نمایش دهند و مثلا می‌توانیم از کاربر بخواهیم تا کد خطا را در تیکت پشتیبانی درج کند.

/**
 * The MyBusinessException wraps all checked standard Java exception and enriches them with a custom error code.
 * You can use this code to retrieve localized error messages and to link to our online documentation.
 * 
 * @author TJanssen
 */
public class MyBusinessException extends Exception {

	private static final long serialVersionUID = 7718828512143293558L;
	
	private final ErrorCode code;

	public MyBusinessException(ErrorCode code) {
		super();
		this.code = code;
	}

	public MyBusinessException(String message, Throwable cause, ErrorCode code) {
		super(message, cause);
		this.code = code;
	}

	public MyBusinessException(String message, ErrorCode code) {
		super(message);
		this.code = code;
	}

	public MyBusinessException(Throwable cause, ErrorCode code) {
		super(cause);
		this.code = code;
	}
	
	public ErrorCode getCode() {
		return this.code;
	}
}

حال برای استفاده از این استثنا کافیست متدی داشته باشیم که این استثنا را پرتاب می‌کند و در تعریفش هم مشخص کند که استثنای MyBusinessException را پرتاب می‌کند.

public void handleExceptionInOneBlock() {
	try {
		wrapException(new String("99999999"));
	} catch (MyBusinessException e) {
		// handle exception
		log.error(e);
	}
}

private void wrapException(String input) throws MyBusinessException {
	try {
		// do something
	} catch (NumberFormatException e) {
		throw new MyBusinessException("A message that describes the error.", e, ErrorCode.INVALID_PORT_CONFIGURATION);
	}
}

پیاده‌سازی یک استثنای چک‌نشده

پیاده‌سازی یک استثنای چک‌نشده تقریبا مشابه استثنای چک‌شده است و شما باید توصیه‌های مشابهی را رعایت کنید. تنها تفاوتی که وجود دارد این است که استثنای چک‌نشده باید از RuntimeException به جای Exception ارث‌بری کند.

/**
 * The MyUncheckedBusinessException wraps all unchecked standard Java exception and enriches them with a custom error code.
 * You can use this code to retrieve localized error messages and to link to our online documentation.
 * 
 * @author TJanssen
 */
public class MyUncheckedBusinessException extends RuntimeException {

	private static final long serialVersionUID = -8460356990632230194L;

	private final ErrorCode code;

	public MyUncheckedBusinessException(ErrorCode code) {
		super();
		this.code = code;
	}

	public MyUncheckedBusinessException(String message, Throwable cause, ErrorCode code) {
		super(message, cause);
		this.code = code;
	}

	public MyUncheckedBusinessException(String message, ErrorCode code) {
		super(message);
		this.code = code;
	}

	public MyUncheckedBusinessException(Throwable cause, ErrorCode code) {
		super(cause);
		this.code = code;
	}
	
	public ErrorCode getCode() {
		return this.code;
	}
}

استفاده از استثنای چک‌نشده هم مشابه چک‌شده است با این تفاوت که لازم نیست متدِ ما تصریح کند که این استثنا را پرتاب می‌کند.

private void wrapException(String input) {
	try {
		// do something
	} catch (NumberFormatException e) {
		throw new MyUncheckedBusinessException("A message that describes the error.", e, ErrorCode.INVALID_PORT_CONFIGURATION);
	}
}

خلاصه

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

علاوه بر این، رعایت چند به‌روش کمک می‌کند که کد شما راحت‌تر خوانده شود و استفاده از APIتان راحت‌تر شود. این چهار تا، مهم‌ترین به‌روش‌ها در زمینهٔ طراحی‌ِ‌ کلاس‌های شخصی‌سازی‌شده‌ٔ استثنا در جاوا هستند:

  • شما تنها زمانی باید یک کلاس استثنای شخصی پیاده‌سازی کنید که فایده‌ و افزوده‌ای بیش‌تر از کلاس استثنای اصلی جاوا داشته باشد.
  • اسم کلاس استثنا باید با Exception پایان یابد.
  • اگر در متدی از APIتان، ممکن است استثنایی پرتاب شود، این استثنا جزوی از API شما محسوب می‌شود و باید به خوبی داکیومنت شود.
  • شما باید حداقل یک سازنده فراهم کنید که «علت» بروز استثنا را در ابرکلاس تنظیم کند.

 

همچنین شما ممکن است به مطالعه این مطلب در زمینه استثناهای شخصی علاقه‌مند باشید.

این مطلب ترجمه‌ای با تغییر و تلخیص از مقاله‌ی why, when and how to implement Custom Exceptions in java است.

.

.

.

.

با ما همراه باشید


آدرس کانال تلگرام: JavaCupIR@

آدرس اکانت توییتر: JavaCupIR@

آدرس صفحه اینستاگرام: javacup.ir

آدرس گروه لینکدین: Iranian Java Developers

نوشته های مشابه

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

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا