
8. פטיש זהב
פטיש זהב הוא אנטי דפוס המוגדר על ידי ביטחון שפתרון מסוים ישים באופן אוניברסלי. דוגמאות:-
לאחר שנתקל בבעיה ומצא דפוס לפתרון המושלם, מתכנת מנסה להדביק את הדפוס הזה בכל מקום, מיישם אותו על כל הפרויקטים הנוכחיים וכל הפרויקטים העתידיים, במקום לחפש את הפתרונות המתאימים למקרים ספציפיים.
-
חלק מהמפתחים יצרו פעם גרסה משלהם של מטמון עבור מצב ספציפי (כי שום דבר אחר לא התאים). מאוחר יותר, בפרויקט הבא שלא כלל היגיון מיוחד במטמון, הם השתמשו שוב בגרסה שלהם במקום להשתמש בספריות מוכנות (לדוגמה, Ehcache). התוצאה הייתה חבורה של באגים וחוסר התאמה, כמו גם הרבה זמן מבוזבז ועצבים מטוגנים.
כל אחד יכול ליפול על האנטי-דפוס הזה. אם אתה מתחיל, ייתכן שאינך בקי בדפוסי עיצוב. זה עשוי להוביל אותך לנסות לפתור את כל הבעיות בדרך היחידה שבה שלטת. אם אנחנו מדברים על אנשי מקצוע, אז אנחנו קוראים לזה דפורמציה מקצועית או ראייה חנונית. יש לך דפוסי עיצוב מועדפים משלך, ובמקום להשתמש נכון, אתה משתמש במועדף שלך, בהנחה שהתאמה טובה בעבר מבטיחה את אותה תוצאה בעתיד.
המהמורת הזו יכולה להניב תוצאות עצובות מאוד - מיישום רע, לא יציב וקשה לתחזוקה ועד לכישלון מוחלט של הפרויקט. כמו שאין גלולה אחת לכל המחלות, אין תבנית עיצובית אחת לכל האירועים.
9. אופטימיזציה מוקדמת
אופטימיזציה מוקדמת היא אנטי דפוס ששמו מדבר בעד עצמו.10. קוד ספגטי
קוד ספגטי הוא אנטי-דפוס המוגדר על ידי קוד שאינו בנוי בצורה גרועה, מבלבל וקשה להבנה, המכיל כל מיני הסתעפות, כגון חריגים, תנאים ולולאות. בעבר, מפעיל ה-goto היה בעל הברית העיקרי של האנטי-דפוס הזה. כבר לא ממש נעשה שימוש בהצהרות Goto, מה שמבטל בשמחה מספר קשיים ובעיות נלווים.public boolean someDifficultMethod(List<String> XMLAttrList) {
...
int prefix = stringPool.getPrefixForQName(elementType);
int elementURI;
try {
if (prefix == -1) {
...
if (elementURI != -1) {
stringPool.setURIForQName(...);
}
} else {
...
if (elementURI == -1) {
...
}
}
} catch (Exception e) {
return false;
}
if (attrIndex != -1) {
int index = attrList.getFirstAttr(attrIndex);
while (index != -1) {
int attName = attrList.getAttrName(index);
if (!stringPool.equalNames(...)){
...
if (attPrefix != namespacesPrefix) {
if (attPrefix == -1) {
...
} else {
if (uri == -1) {
...
}
stringPool.setURIForQName(attName, uri);
...
}
if (elementDepth >= 0) {
...
}
elementDepth++;
if (elementDepth == fElementTypeStack.length) {
...
}
...
return contentSpecType == fCHILDRENSymbol;
}
}
}
}
}
זה נראה נורא, לא? לרוע המזל, זהו האנטי-דפוס הנפוץ ביותר :( אפילו מי שכותב קוד כזה לא יוכל להבין אותו בעתיד. מפתחים אחרים שיראו את הקוד יחשבו, "ובכן, אם זה עובד, אז בסדר - עדיף לא לגעת בזה". לעתים קרובות, שיטה היא בהתחלה פשוטה ושקופה מאוד, אבל ככל שמתווספות דרישות חדשות, השיטה מאוכפת בהדרגה עם עוד ועוד הצהרות מותנות, והופכות אותה למפלצתיות כמו זו. אם שיטה כזו מופיע, אתה צריך לשחזר אותו או לחלוטין או לפחות את החלקים המבלבלים ביותר. בדרך כלל, בעת תזמון פרויקט, זמן מוקצה עבור reactoring, למשל, 30% מזמן הספרינט הוא עבור refactoring ובדיקות. כמובן, זה בהנחה שאין למהר (אבל מתי זה קורה אי פעם) אתה יכול למצוא דוגמה טובה לקוד ספגטי ועיבודו מחדש כאן
.
11. מספרי קסם
מספרי קסם הוא אנטי דפוס שבו כל מיני קבועים משמשים בתוכנית ללא כל הסבר של מטרתם או משמעותם. כלומר, בדרך כלל יש להם שמות גרועים או במקרים קיצוניים, אין הערה שמסבירה מהן ההערות או למה. כמו קוד ספגטי, זהו אחד האנטי-דפוסים הנפוצים ביותר. למישהו שלא כתב את הקוד אולי יש או אין מושג לגבי מספרי הקסם או איך הם פועלים (ועם הזמן, המחבר עצמו לא יוכל להסביר אותם). כתוצאה מכך, שינוי או הסרה של מספר גורמים לקוד להפסיק לעבוד ביחד. לדוגמה, 36 ו -73 . כדי להילחם נגד דפוס זה, אני ממליץ על סקירת קוד. הקוד שלך צריך להיבדק על ידי מפתחים שאינם מעורבים בסעיפים הרלוונטיים של הקוד. העיניים שלהם יהיו טריות ויהיו להם שאלות: מה זה ולמה עשית את זה? וכמובן, אתה צריך להשתמש בשמות מסבירים או להשאיר הערות.12. תכנות העתק והדבק
תכנות העתק-הדבק הוא אנטי-דפוס שבו הקוד של מישהו אחר מועתק ומודבק ללא מחשבה, דבר שעלול לגרום לתופעות לוואי בלתי צפויות. למשל, שיטות העתקה והדבקה עם חישובים מתמטיים או אלגוריתמים מורכבים שאיננו מבינים עד הסוף. זה עשוי לעבוד במקרה הספציפי שלנו, אבל בנסיבות אחרות זה עלול להוביל לצרות. נניח שאני צריך שיטה כדי לקבוע את המספר המרבי במערך. חיטטתי באינטרנט, מצאתי את הפתרון הזה:public static int max(int[] array) {
int max = 0;
for(int i = 0; i < array.length; i++) {
if (Math.abs(array[i]) > max){
max = array[i];
}
}
return max;
}
נקבל מערך עם המספרים 3, 6, 1, 4 ו-2, והשיטה מחזירה 6. מעולה, נשמור על זה! אבל מאוחר יותר נקבל מערך המורכב מ-2.5, -7, 2 ו-3, ואז התוצאה שלנו היא -7. והתוצאה הזו לא טובה. הבעיה כאן היא ש- Math.abs() מחזירה את הערך המוחלט. התעלמות מכך מובילה לאסון, אך רק במצבים מסוימים. ללא הבנה מעמיקה של הפתרון, ישנם מקרים רבים שלא תוכל לאמת. קוד מועתק עשוי גם לחרוג מהמבנה הפנימי של האפליקציה, הן מבחינה סגנונית והן ברמה הבסיסית יותר, האדריכלית. קוד כזה יהיה קשה יותר לקריאה ולתחזוקה. וכמובן, אסור לנו לשכוח שהעתקת קוד ישר של מישהו אחר היא סוג מיוחד של פלגיאט. באותם מקרים שבהם מתכנת לא מבין עד הסוף מה הוא או היא עושים ומחליט לקחת את הפתרון העובד כביכול של מישהו אחר, זה לא רק מראה על חוסר התמדה, אלא גם הפעולות הללו פוגעות בצוות, בפרויקט, ולפעמים כל החברה (אז העתק והדבק בזהירות).
13. המצאת הגלגל מחדש
המצאת הגלגל מחדש היא אנטי-דפוס, המכונה לפעמים המצאה מחדש של הגלגל המרובע . בעיקרו של דבר, תבנית זו היא ההיפך מהאנטי-דפוס של העתק-הדבק שנחשב לעיל. באנטי-דפוס זה, המפתח מיישם פתרון משלו לבעיה שכבר קיימים לה פתרונות. לפעמים הפתרונות הקיימים האלה טובים יותר ממה שהמתכנת ממציא. לרוב, זה מוביל רק לאיבוד זמן ולפרודוקטיביות נמוכה יותר: ייתכן שהמתכנת כלל לא ימצא פתרון או ימצא פתרון שהוא רחוק מלהיות הטוב ביותר. עם זאת, איננו יכולים לשלול את האפשרות ליצור פתרון עצמאי, מכיוון שעשייה זו היא דרך ישירה לתכנות העתק-הדבק. על המתכנת להיות מודרך על ידי משימות התכנות הספציפיות שעולות על מנת לפתור אותן בצורה מוכשרת, בין אם באמצעות פתרונות מוכנים ובין אם באמצעות יצירת פתרונות מותאמים אישית. לעתים קרובות מאוד, הסיבה לשימוש נגד דפוס זה היא פשוט חיפזון. התוצאה היא ניתוח רדוד של (חיפוש) פתרונות מוכנים. המצאה מחדש של הגלגל המרובע הוא מקרה שבו לאנטי-דפוס הנבחן יש תוצאה שלילית. כלומר, הפרויקט דורש פתרון מותאם אישית, והמפתח יוצר אותו, אבל גרוע. יחד עם זאת, אפשרות טובה כבר קיימת ואחרים משתמשים בה בהצלחה. שורה תחתונה: כמות עצומה של זמן אובדת. ראשית, אנו יוצרים משהו שלא עובד. אחר כך אנחנו מנסים לשחזר אותו, ולבסוף אנחנו מחליפים אותו במשהו שכבר היה קיים. דוגמה לכך היא הטמעת מטמון מותאם אישית משלך כאשר כבר קיימים הרבה יישומים. לא משנה כמה אתה מוכשר כמתכנת, אתה צריך לזכור שלהמציא מחדש גלגל מרובע הוא לכל הפחות בזבוז זמן. וכפי שאתה יודע, זמן הוא המשאב היקר ביותר.14. בעיית יו-יו
בעיית היו-יו היא אנטי-תבנית שבה מבנה האפליקציה מסובך מדי בגלל פיצול יתר (לדוגמה, שרשרת ירושה מחולקת יתר על המידה). "בעיית היו-יו" מתעוררת כאשר אתה צריך להבין תוכנית שההיררכיה של הירושה שלה ארוכה ומורכבת, ויוצרת קריאות מתודות מקוננות עמוקות. כתוצאה מכך, מתכנתים צריכים לנווט בין מחלקות ושיטות רבות ושונות על מנת לבחון את התנהגות התוכנית. השם של האנטי-דפוס הזה מגיע מהשם של הצעצוע. כדוגמה, בואו נסתכל על שרשרת הירושה הבאה: יש לנו ממשק טכנולוגי:public interface Technology {
void turnOn();
}
ממשק התחבורה יורש אותו:
public interface Transport extends Technology {
boolean fillUp();
}
ואז יש לנו ממשק נוסף, GroundTransport:
public interface GroundTransportation extends Transport {
void startMove();
void brake();
}
ומשם, אנו גוזרים כיתת מכוניות מופשטת:
public abstract class Car implements GroundTransportation {
@Override
public boolean fillUp() {
/* some implementation */
return true;
}
@Override
public void turnOn() {
/* some implementation */
}
public boolean openTheDoor() {
/* some implementation */
return true;
}
public abstract void fixCar();
}
הבא הוא מחלקת פולקסווגן המופשטת:
public abstract class Volkswagen extends Car {
@Override
public void startMove() {
/* some implementation */
}
@Override
public void brake() {
/* some implementation */
}
}
ולבסוף, דגם ספציפי:
public class VolkswagenAmarok extends Volkswagen {
@Override
public void fixCar(){
/* some implementation */
}
}
שרשרת זו מאלצת אותנו לחפש תשובות לשאלות כמו:
-
כמה שיטות
VolkswagenAmarok
יש? -
איזה סוג יש להכניס במקום סימן השאלה על מנת להשיג הפשטה מקסימלית:
? someObj = new VolkswagenAmarok(); someObj.brake();
15. מורכבות מקרית
מורכבות מיותרת היא תבנית אנטי שבה מציגים סיבוכים מיותרים לפתרון.
public void createDescriptionForElement(ServiceType type, Long languageId, Long serviceId, String description)throws Exception {
switch (type){
case CAR:
jdbcTemplate.update(CREATE_RELATION_WITH_CAR, languageId, serviceId, description);
case USER:
jdbcTemplate.update(CREATE_RELATION_WITH_USER, languageId, serviceId, description);
case FILE:
jdbcTemplate.update(CREATE_RELATION_WITH_FILE, languageId, serviceId, description);
case PLAN:
jdbcTemplate.update(CREATE_RELATION_WITH_PLAN, languageId, serviceId, description);
case CUSTOMER:
jdbcTemplate.update(CREATE_RELATION_WITH_CUSTOMER, languageId, serviceId, description);
default:
throw new Exception();
}
}
ובהתאם לכך, יש לנו את המספר הזה:
public enum ServiceType {
CAR(),
USER(),
FILE(),
PLAN(),
CUSTOMER()
}
הכל נראה פשוט וטוב... אבל מה עם שאר השיטות? ואכן, לכולם יהיו גם חבורה של switch
הצהרות וחבורה של שאילתות מסד נתונים כמעט זהות, מה שבתורן יסבך מאוד וינפח את הכיתה שלנו. איך אפשר להקל על כל זה? בואו נשדרג קצת את המספר שלנו:
@Getter
@AllArgsConstructor
public enum ServiceType {
CAR("cars_descriptions", "car_id"),
USER("users_descriptions", "user_id"),
FILE("files_descriptions", "file_id"),
PLAN("plans_descriptions", "plan_id"),
CUSTOMER("customers_descriptions", "customer_id");
private String tableName;
private String columnName;
}
כעת לכל סוג יש את שמות השדות המקוריים של הטבלה שלו. כתוצאה מכך, השיטה ליצירת תיאור הופכת:
private static final String CREATE_RELATION_WITH_SERVICE = "INSERT INTO %s(language_id, %s, description) VALUES (?, ?, ?)";
public void createDescriptionForElement(ServiceType type, Long languageId, Long serviceId, String description) {
jdbcTemplate.update(String.format(CREATE_RELATION_WITH_SERVICE, type.getTableName(), type.getColumnName()), languageId, serviceId, description);
}
נוח, פשוט וקומפקטי, אתה לא חושב? האינדיקציה של מפתח טוב היא אפילו לא באיזו תדירות הוא או היא משתמשים בדפוסים, אלא באיזו תדירות הוא או היא נמנעים מתבניות אנטי. בורות היא האויב הגרוע ביותר, כי אתה צריך להכיר את האויבים שלך ממראה עיניים. ובכן, זה כל מה שיש לי להיום. תודה לכולם! :)
GO TO FULL VERSION