پادالگوی 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