پادالگوی Primitive Obsession

با توسعه تدریجی کد در گذر زمان، کدها بو میگیرند. کدهایی که به خوبی آغاز میشوند بعد از چند تصحیح و نگهداری در شرایطی که بهبود جدی در طراحی نداشته باشد یا به عبارتی Refactor نشده باشد، وارد روند نزولی میشوند.
به جز شرکتهایی که به طور منظم بازبینی کد را انجام میدهند بخشهای مشکلزا مانند توابع طولانی و لیست طولانی از پارامترها به مدت طولانی در کد باقی میمانند و کیفیت آن را پایین آورده و کد را از لحاظ تکنیکی زیر سوال میبرند.
پادالگویی که بیشتر از سایرین در کد باقی میماند، Primitive Obsession است. این اصطلاح از زبان افراد مختلف معانی متفاوتی دارد اما همه در یک مفهوم مشترک هستند و آن استفاده از نوع داده اولیه درجایی است که یک شئ به نحو بهتری میتوانست جایگزین آن شود.
یک مثال قدیمی آن استفاده از عدد صحیح برای ذخیره کد پستی آمریکا در تابعی است که آدرسها را چاپ میکند. کدپستی آمریکا یک عدد ۵ رقمی میباشد اما اگر در آینده، برنامه بخواهد کدپستی چهاررقمی بپذیرد، یک منطق جدید برای فرمت کدپستی مورد نیاز خواهد بود. حال اگر نیاز باشد از یک بارکد مبتنی بر کد پستی استفاده شود، مجددا منطق جدیدی باید تعریف شود. اگر این سورس کد که چندین بار تغییر پیدا کرده است در همان تابع چاپ آدرس باقی بماند خیلی بزرگ و سنگین خواهد شد. تصور کنید که الان اگر بخواهیم کد پستی بریتانیا را اضافه کنیم که شامل کاراکترحرفی نیز هست، primitive obsession واضح تر خواهد شد. در این شرایط است که میفهمیم بهبود کد برای جایگزینی داده با شئ دست کم گرفته شده است.
خوب است هرزمان یک متغیر محلی، منطقی متناسب با عملکرد یا فرمت خود را در بردارد، آن را به درون کلاس خودش منتقل کنیم. این کار کلاس پدر را کوچکتر کرده، از پیچیدگی آن میکاهد و قانون تک وظیفهای (Single Responsibilty Principle) که یکی از اصول کلیدی شئگرایی است را اعمال میکند. به علاوه کلاس جدید برای تست کردن و اعتبارسنجی کد نیز کار را سادهتر میکند. به این ترتیب به دلیل مخفی کردن جزئیات پیادهسازی، تغییر متغیر کد پستی از عدد صحیح به رشته برای تطابق با کدپستی بریتانیا تنها تغییری است که در یک بخش کوچک کد اعمال میشود.
پنهان کردن داده در شرایطی که داده یک collection به جای یک نوع داده اصلی باشد نیز مزایای قابل توجهی دارد و میتواند دسترسی به المانهای مجموعه را نیز پنهان کند. مثلا اگر به جای کار با یک کلاس از یک مجموعه در جاوا استفاده کنیم هر متدی میتواند المانی اضافه کند یا متدهایی مثل reverse() و swap() را اجرا کند. اما درصورتی که یک کلاس آن را پوشش دهد میتوان دقیقا مشخص کرد که چه کارهایی با آن داده میتواند انجام شود.
به علاوه، وقتی یک داده را در خروجی برمیگردانیم تقریبا میتوان گفت غیر قابل تغییر است و در رویدادی که درخواست یک المان میکنیم که وجود ندارد یک رشته خالی یا آرایه خالی یا حتی null برگردانده میشود. این مراحل نشان میدهد که دادههای واضحتری وارد کد شده و احتمال رخداد NullPointerException نیز کمتر میشود.
پس در صورتی که دادهای چه به صورت نوع داده اصلی و چه مجموعه وجود داشته باشد که منطقی را در بردارد، بهتر است آن را به یک شئ منتقل کنیم. مطمئن باشید در آینده از این کارتان راضی خواهید بود.
در ادامه مثالی از این پادالگو در جاوا آمده است:
public class primitiveObsession {
public static void main(String args[]) {
Integer[] cityPopulations = {
13000000, // London
21903623, // New York
12570000, // Tokyo
1932763, // Stockholm
1605602, // Barcelona
4119190 // Sydney
};
for (Integer cityPopulation:cityPopulations)
{
System.out.println(cityPopulation);
}
}
}
در ادامه این پادالگو حذف شده است و کلاسهای لازم نیز تعریف گردیدهاند.
//city.java
public class City {
private final String name;
private final int population;
private final Continent continent;
public String getName() {
return name;
}
public int getPopulation() {
return population;
}
public Continent getContinent() {
return continent;
}
public City(String name, int population, Continent continent) {
this.name = name;
this.population = population;
this.continent = continent;
}
public String toString() {
return String.format( “%s has a popluation of %s and is located in %s”,
name, population, continent);
}
public static final City[] ALL_CITIES={
new City(“London”,13000000,Continent.EUROPE),
new City(“New York”,21903623,Continent.AMERICA),
new City(“Tokyo”,12570000,Continent.ASIA),
new City(“Stockholm”,1932763,Continent.EUROPE),
new City(“Barcelona”,1605602,Continent.EUROPE),
new City(“Sydney”,4119190,Continent.AUSTRALIA)
};
}
//Continent.java
public enum Continent {
AMERICA,
EUROPE,
AFRICA,
ASIA,
AUSTRALIA
}
// withOutPrimitiveObsession.java
public class withOutPrimitiveObsession {
public static void main(String args[]) {
for (City city:City.ALL_CITIES) {
System.out.println(city.toString());
}
}
}
منابع:
http://curiousjava.blogspot.com/2012/09/primitive-obsession-with-example.html