هنر بکارگیری پیشوندهای بولی: چرا نباید متغیرهای خود را "Flag" نامگذاری کنید
نویسنده: وحید نصیری
تاریخ: ۱۴۰۴/۰۹/۱۸ ۱۰:۳۰
آدرس: www.dntips.ir
| مطالب | ۳۶۹۴ |
| نویسندگان | ۲۷۶ |
| گروههای مطالب | ۱۰۲۴ |
| نقشههای راه | ۱۱۹ |
| دورهها | ۱۴ |
| اشتراکها | ۱۷۹۱۴ |
open، flag یا done روبرو میشوید. این نامها در نگاه اول ساده به نظر میرسند، اما در عمل، ابهام آنها میتواند منجر به باگهای پنهان و سردرگمی شود؛ بهویژه در ساعات نیمهشب هنگام رفع عیب. متغیرهای بولی (boolean) به دلیل طبیعت دوگانهشان (بله/خیر)، مانند مینهای زمینی عمل میکنند؛ اگر نام آنها واضح نباشد، تفسیر اشتباه آنها آسان است. در این مقاله، به بررسی مشکل نامگذاری ضعیف متغیرهای بولی میپردازیم و راهحلهایی عملی برای بهبود خوانایی کد ارائه میدهیم. این رویکرد نه تنها کد شما را حرفهایتر میکند، بلکه همکاری تیمی را تسهیل کرده و از بروز خطاها جلوگیری مینماید.flag، status یا done تعریف میشوند که هیچ اطلاعاتی در مورد معنای واقعیشان ارائه نمیدهند. برای مثال، کد زیر را در نظر بگیرید:public class OrderProcessor
{
private bool open;
private bool flag;
private bool done;
private bool status;
public void Process(bool check)
{
if (open && flag)
{
flag = false;
status = true;
}
if (done)
{
// چه اتفاقی اینجا میافتد؟
}
}
}open به چه معناست؟ آیا فروشگاه باز است یا فایل باید باز شود؟ flag چیست؟ این نام مانند یک یادداشت موقت است که میگوید "بعداً درستش میکنم". و done آیا یک سؤال است ("آیا تمام شده؟") یا یک دستور ("آن را تمامشده علامت بزن")؟if (done) را بر اساس حدس تفسیر کند، و یک نگهدارنده ممکن است flag = false را تغییر دهد و منطق را ناخواسته معکوس کند. سؤالهایی مانند if (!flag)... آیا این به معنای روشن کردن است یا خاموش کردن؟ متغیرهای بولی مانند سؤالاتی هستند که کد میپرسد، و مقدار بولی پاسخ "بله" یا "خیر" است. اگر نام متغیر سؤال واضحی نباشد، پاسخ بیمعنی میشود. این مشکل در تیمهای بزرگتر تشدید میشود، جایی که توسعهدهندگان مختلف با زمینههای متفاوت کد را میخوانند. علاوه بر این، ابزارهای خودکار مانند refactoring IDEها ممکن است این ابهامها را بدتر کنند، زیرا نمیتوانند مقصود انسانی را تشخیص دهند.isActive (آیا فعال است؟)، isDeleted (آیا حذفشده است؟)، isEmpty (آیا خالی است؟).isAccess (ناهماهنگی گرامری؛ از hasAccess استفاده کنید).hasAccess (آیا دسترسی دارد؟)، hasChildren (آیا فرزند دارد؟)، hasValidationErrors (آیا خطاهای اعتبارسنجی دارد؟).hasActive (ناهماهنگی گرامری؛ از isActive استفاده کنید).canEdit (آیا میتواند ویرایش کند؟)، canDelete (آیا میتواند حذف کند؟)، canRetry (آیا میتواند دوباره امتحان کند؟).canAdmin (مبهم؛ از isAdmin یا canAdminister استفاده کنید).shouldRetry (آیا باید دوباره امتحان کند؟)، shouldCacheResponse (آیا باید پاسخ را کش کند؟).shouldUser (ناقص؛ shouldCreateUser؟).isAccess یا hasActive اجتناب کنید، زیرا خواننده را مجبور به توقف و بازخوانی میکند. این اصطکاک جایی است که باگها وارد میشوند.| پیشوند | حوزه | عملکرد | مثال |
| IS | هویت / وضعیت | توصیف وضعیت فعلی شیء | isActive, isEmpty |
| HAS | مالکیت | توصیف وجود ویژگی یا مالکیت | hasChildren, hasAccess |
| CAN | قابلیت | توصیف مجوز یا اقدام ممکن | canEdit, canRetry |
| SHOULD | قصد / منطق | توصیف توصیه قوانین کسبوکار | shouldCache, shouldRetry |
is_active استفاده کنید. isNotEnabled، hasNoAccess یا isDisabled اجتناب کنید.if (!isDisabled) مغز را مجبور به محاسبهی دو منفی میکند: "اگر غیرفعال نیست...". در مقابل، if (isEnabled) واضح و فوری است.isDisabled را به isNotDisabled تغییر دهند و درک کد را مشکلتر کنند.noValidate همخوانی داشته باشد. حتی در این موارد، آن را در مرز جدا کنید: bool shouldValidate = !request.noValidate;.// کد واقعی از NHibernate schemaExport.Execute(false, true, false);
true چه میکند؟ هیچ ایدهای ندارید و باید کد منبع کتابخانه را باز کنید.Execute(bool, bool, bool) دیدید، با یک نقص طراحی روبرو هستید.// بد email.Send(message, true); // true یعنی "فوری"؟ "اولویت بالا"؟ // خوب email.SendImmediately(message); email.SendQueued(message);
// بد file.Write(data, true); // خوب file.Write(data, WriteMode.Append);
// بد
export.Execute(false, true, false);
// خوب
export.Execute(new ExportOptions {
Script = false,
Export = true,
JustDrop = false
});flag، done یا check هیچ اطلاعاتی نمیدهند.if (check)if (isPaymentVerified)if (user.hasActive)if (user.isActive)! کنار Not یا No.if (!isNotEnabled)if (isEnabled)isValid (در واقع بررسی وجود کاربر، ایمیل و فعال بودن).bool isReadyForBilling = user.Exists && user.HasEmail && user.IsActive;bool error = false; if (!Save()) error = true; if (!error && !SendEmail()) error = true; return error;
flag نامگذاری میکنید، برای لحظه فعلی خودتان مینویسید. اما وقتی آن را isProcessed مینامید، برای کسی که شش ماه بعد باید باگی را در این فایل رفع کند، مینویسید که احتمالاً خودتان هستید. با آیندهی خود مهربان باشید! با رعایت پیشوندهای IS، HAS، CAN و SHOULD، اجتناب از منفیها و جلوگیری از تله بولی، کد شما نه تنها خواناتر میشود، بلکه قابل اعتمادتر و آسانتر برای نگهداری خواهد بود. در نهایت، این اصول کوچک میتوانند تفاوت بزرگی در کیفیت پروژههای شما ایجاد کنند. اگر این رویکرد را در کدهای خود پیاده کنید، متوجه کاهش زمان دیباگینگ و افزایش رضایت تیمی خواهید شد.SendEmail(message, true)، true ممکن است به معنای "فوری ارسال شود"، "اولویت بالا" یا چیز دیگری باشد. این ابهام زمانی تشدید میشود که چندین بولی در یک تابع وجود داشته باشد، مانند Execute(false, true, false) و خواننده کد باید مستندات یا کد منبع را بررسی کند تا بفهمد هر کدام چه میکند. جایگزینها مانند Enum و کلاس پیکربندی این مشکل را با نامگذاری صریح حل میکنند.Write(data, true) (که true یعنی append)، از Enum استفاده کنید: Write(data, WriteMode.Append).ProcessPayment(secure: true)، از ProcessPayment(mode: PaymentMode.Secure).// بد: با بولی
public void WriteFile(string data, bool append) {
if (append) { /* append logic */ } else { /* overwrite logic */ }
}
// خوب: با Enum
public enum WriteMode { Append, Overwrite }
public void WriteFile(string data, WriteMode mode) {
switch (mode) {
case WriteMode.Append: /* append logic */ break;
case WriteMode.Overwrite: /* overwrite logic */ break;
}
}Export(script: false, export: true, drop: false)، از یک شیء استفاده کنید.// بد: با چندین بولی
public void Export(bool script, bool export, bool justDrop) { /* logic */ }
// خوب: با کلاس پیکربندی
public class ExportOptions {
public bool Script { get; set; } = false;
public bool Export { get; set; } = true;
public bool JustDrop { get; set; } = false;
}
public void Export(ExportOptions options) { /* logic using options.Script, etc. */ }isActive).