getter/setter: منفورترین practice در جاوا

شما باید از getter/setter استفاده کنید. اگر این کار را انجام نمیدهید برنامهنویس خوبی نیستید. واقعا؟!
واقعا اینطور نیست، ساختن getter و setter میتوانتد به سادگی منجر به آلوده کردن کد شما شوند. بیشتر مواقع شما هیچکاری با متغیرها در توابع getter و setter انجام نمیدهید. پس چرا باید اصلا وجود داشته باشند؟ اگر شرکتتان بر اساس تعداد خطوط کد درآمدتان را محاسبه میکند، مسلما شکایتی ندارید(و احتمالا ترجیح میدهید بعد از تولید میلیونها خط کد بیاستفاده شرکت را ترک کنید)
در حقیقت توابع getter و setter اصلا مفید نیستند در صورتی که واقعا مطمئن هستید که کلاسی که تعریف کردید به نحو دیگری که مقصود اولیه شما بوده است مورد استفاده قرار نمیگیرد. باید به این مساله دقت کنید که کد در طول عمر نرمافزار مدام تکامل پیدا میکند و به همین دلیل است که یک روش خوب این است که به جای دسترسی مستقیم به متغیرها از توابع getter و setter استفاده کنیم! به کد زیر نگاه کنید:
class YourClass { public int myVar; } class SomeoneElseClass { public void doSomethingWithYourClass(YourClass yc){ yc.myVar = getValueFromBlackhole(); transmitToWhitehole(yc); } }
یک روز به این نتیجه میرسید که یک کد برای اعتبارسنجی متغیر myVar اضافه کنید چرا که بقیه مردم مدام مقادیر اشتباه به سیستم شما میفرستند. بدون setter خودتان نمیتوانید این مساله را حل کنید. لازم است از بقیه مردم بخواهید که کدشان را تغییر دهند و متغیر public را به private تغییر دهند و از تابع setter استفاده کنند. اگر تنها کسی که از کد شما استفاده میکند، بغل دستیتان در شرکت است، چندان مساله خاصی نیست و مودبانه از ایشان خواهش خواهید کرد. راه حل وحشتناک آن این است که از مردم بخواهید کد اعتبارسنجی را قبل مقداردهی متغیر myVar در کدشان قرار دهند. به همین دلیل است که مدام به شما توصیه میکنند از توابع getter وsetter استفاده کنید.
این روش خوب، در شرایطی که از زبانی استفاده میکنید که میتواند به طور ضمنی توابع getter و setter را به واسط کاربری موجود پیوند بزند، نامربوط است. پس لازم نیست که خودتان این توابع را بنویسید.
در ادامه نشان میدهیم که چرا نباید در بعضی زبانها این توابع را تعریف کنید.
پایتون
در پایتون شما میتوانید متغیرهای عمومی را تعریف کنید که به کلاسهای دیگر اجازه میدهد مستقیما به آن دسترسی داشته باشند. این مساله برای برنامهنویسان جاوا بد به نظر میرسد اما برای پایتونکارها خیلی عادی است. چرا که آنها میتوانند در آینده یک تابع property تعریف کنند بدون اینکه واسط کاربری آنها تغییری کند.
//Your class class YourClass(object): my_var = 0 class SomeoneElseClass(object): def do_something(self, yc): yc.my_var = get_value_from_blackhole() transmit_to_whitehole(yc)
اگر بخواهید در آینده برای متغیر my_var تابع setter تعریف کنید میتوانید به شکل زیر عمل کنید:
class YourClass(object): my_var = 0 @property def my_var(self): return self._my_var @my_var.setter def my_var(self, value): print(“Easy peasy”) self._my_var = value
یا راه دیگر این است که از property function استفاده کنید
class YourClass(object): def get_my_var(self): return self._my_var def set_my_var(self, value): print(“Easy peasy”) self._my_var = value my_var = property(get_my_var, set_my_var)
سی شارپ.نت
سی شارپ برخلاف پایتون که از annotation یا توابع استفاده میکند، یک سینتکس خاصی برای property دارد. ممکن است اول فکر کنید کد زیر به زبان جاواست. درست است این کد به جاوا هم کامپایل میشود. پس در ادامه سعی میکنیم اندکی تغییر بدهیم که بیشتر شبیه .نت شود 🙂
class YourClass { public int MyVar; } class SomeoneElseClass { public void DoSomethingWithYourClass(YourClass yc) { yc.MyVar = GetValueFromBlackhole(); TransmitToWhitehole(yc); } }
وقتی میخواهید یک تابع setter اضافه کنید میتوانید با تعریف یک property با استفاده از نامی مشابه متغیر عمومی و تغییر اسم آن متغیر به اسمی دیگر این کار را انجام دهید.
دقت کنید که اگر می خواهید mutator (یا همان setter) اضافه کنید لازم است که accessor (یا getter) هم اضافه کنید. وگرنه کامپایل نخواهد شد اما برعکس میتوانید تنها accessor داشته باشید.
class YourClass { //rename MyVar to myVar and use it as backing variable private int myVar; //add property MyVar to replace MyVar variable public int MyVar { get { return myVar; } set { System.Console.WriteLine(“Easy peasy”); myVar = value; } } }
اسکالا
اسکالا هیچ کلمه کلیدی، annotation یا تابعی برای تعریف property ندارد. اسکالا از naming convention برای این کار استفاده میکند و کامپایلر میداند که چگونه آن را مدیریت کند.
class YourClass() { var myVar = "" } class SomeoneElseClass() { def doSomething(yc: YourClass) = { yc.myVar = getValueFromBlackhole() transmitToWhitehole(yc) } }
اگر بخواهید که تابع setter برای متغیر myVar تعریف کنید باید نام آن را به _myVar (یا هر اسمی که با myVar تداخل نداشته باشد) تغییر دهید و آن را private قرار دهید.سپس تابعی به اسم myVar تعریف میکنید برای accessor و به اسم myVar_= برای mutator. توجه کنید که لازم است هردوی این توابع را تعریف کنید.
class YourClass() { //rename myVar variable to _myVar private var _myVar = 0 //getter def myVar = _myVar //setter def myVar_= (value: Int): Unit = _myVar = value }
اما واقعا چه کسی از این ویژگی در اسکالا استفاده میکند؟! (:پی) خیلی از برنامه نویسان اسکالا نمیدانند که اصلا چنین ویژگی در آن وجود دارد. شما میتوانید مشابه جاوا با اسکالا هم برنامه بنویسید، هرچند توصیه نمیشود. بعضی از برنامهنویسان اسکالاکار از نداشتن وضعیت mutable در برنامه خود لذت میبرند و کدشان را تاجای ممکن functional میکنند. وضعیتهای mutable فقط برای مواقع استثنایی مثل I/O سطح پایین است.
اما جاوا چطور؟
متاسفانه جاوا این چنین ویژگی ندارد و به نظر نمیرسد در آینده نزدیک، حداقل در جاوا ۹ یا ۱۰ بخواهد اضافه کند. اما در عوض شما میتوانید از چارچوبهایی که این توابع را به صورت خودکار تولید میکنند استفاده کنید. اگر علاقهمند هستید نگاهی به project lombok بیندازید. این یک چارچوب تولید کد است که در پشت صحنه برایتان توابع getter/setter را تولید میکند. به علاوه میتواند توابع دیگری مثل toString()، equals() و hashCode() را نیز تولید کند. زمانی که کلاسهای دادهای زیادی دارید استفاده از این چارچوب خیلی عالی است.
وقتی از این چارچوب استفاده کنید تنها کافی است که کلاس خود را به عنوان کلاس دادهای annotate کنید.
public class @Data YourClass { private int myVar; }
شما کدی که تولید میشود را مشاهده نخواهید کرد اما چیزی مشابه زیر است:
public class YourClass { private int myVar; public int getMyVar(){ return myVar; } public void setMyVar(String myVar){ this.myVar = myVar; } @Override public String toString(){ return "YourClass(" + this.getMyVar() + ")"; } protected boolean canEqual(Object other) { return other instanceof YourClass; } @Override public boolean equals(Object o) { if (o == this) return true; if(! (o instanceof YourClass)) return false; YourClass other = (YourClass)o; if(!other.canEquals((Object)this)) return false; if(this.getMyVar() != other.getMyVar()) return false; return true; } @Override public int hashCode() { final int PRIME = 59; int result = 1; result = (result * PRIME) + this.geMyVar(); return result; } }
خوب نیست!؟ زبانهای دیگر به طور پیش فرض این ویژگی را دارند اما اگر میخواهید از جاوا استفاده کنید میتوانید از lombok استفاده کنید.
منبع: