/ OOD

[365 วันแห่งโปรแกรม #day68] Anti-Pattern

วันที่หกสิบแปดของ ‪#‎365วันแห่งโปรแกรม เรื่องที่เราจะคุยในวันนี้เป็นส่วนกลับของ Design Pattern ครับ นั่นก็คือ Anti-Pattern


Anti-Pattern

Anti-Pattern เป็นส่วนกลับของ Design Pattern กล่าวคือเป็นวิธีการแก้ปัญหาแบบไม่ยั่งยืน แก้ปัญหาเฉพาะหน้าแล้วปัญหาอื่นตามมาเพียบ อะไรทำนองนั้น แต่ผมไม่ได้บอกว่า Anti-Pattern ไม่ดีนะครับ เพราะว่ามีปัญหาอีกมากมายที่แก้ด้วย Design Pattern ปกติไม่ได้ ไม่ว่าจะเป็นเพราะมันทำให้ซับซ้อนเกินไป หรือระบบมันใหญ่มากจนไม่รู้จะแก้ยังไง ดังนั้นการศึกษาเอาไว้ก็ไม่ใช่เรื่องเสียหายครับ

Anti-Pattern ที่เด่นๆ เลยก็น่าจะเป็น God object, Circular dependency อะไรประมาณนี้ เดี๋ยวเราจะมาดูตัวอย่างเหล่านี้กันครับ

God object

God object คือ object ที่มีความสามารถครอบจักวาร อยากทำอะไรก็เรียกได้จาก object นี้ ซึ่งเราก็รู้ๆ อยู่ว่ามันผิดหลัก Single Responsibility แต่ก็ทำกัน เพราะมันสะดวก ฮ่าๆ

class GodObject {
    function PerformInitialization() {}
    function ReadFromFile() {}
    function WriteToFile() {}
    function DisplayToScreen() {}
    function PerformCalculation() {}
    function ValidateInput() {}
    // and so on... //
}

ด้านบนเป็นตัวอย่างของ God object ครับ คลาสนี้สามารถทำได้หลายอย่าง ไม่ว่าจะเป็น อ่านไฟล์ เขียนไฟล์ แสดงบนหน้าจอ คำนวณ และอื่นๆ ตอนใช้แรกๆ ก็ใช้ได้ปกติครับ แล้วพอโค้ดเยอะขึ้นเรื่อยๆ เรื่อยๆ ก็เริ่มแก้ยากขึ้นทุกทีๆ เติบโตเป็น Technical Debt ครับ วิธีแก้ก็คือการเขียนใหม่ครับ เขียนใหม่เลย แก้ไปเดี๋ยวเจ๊ง เอาเป็นว่าจริงๆ มันต้องเขียนให้ดีตั้งแต่ต้น ไม่ใช่มาแก้ทีหลังแบบนี้

class FileInputOutput {
    function ReadFromFile() {}
    function WriteToFile() {}
}

class UserInputOutput {
    function DisplayToScreen() {}
    function ValidateInput() {}
}

class Logic {
    function PerformInitialization() {}
    function PerformCalculation() {}
}

แก้เป็นแบบนี้แล้วก็ดูง่ายขึ้น แต่ก็ต้องแลกมากับการใช้งานที่ยุ่งยาก ก็แล้วแต่มุมมองครับ ว่าชอบแบบ Maintain ง่าย หรือชอบแบบใช้งานง่าย ><

Circular dependency

Circular dependency คือการที่ object ของเรามีการ reference กันเองครับ แบบ B เรียกใช้ A แล้ว A ก็เรียกใช้ B ด้วย อะไรแบบนี้ การ reference ระหว่าง object ของคลาสเดียวกันก็ถือเป็น Circular dependency เช่นกัน

===a.h===

class A {
public:
    static A *first, *last;
    A *previous, *next;

    A();
    ~A();
};

===a.cpp===

#include "a.h"

A *A::first=0, *A::last=0; // don't put the word static here, that will cause an error

A::A() {
    if(first==0) first=this; //first A created
    previous=last;
    if(previous != 0) previous->next=this;
    last=this;
    next=0;
}

A::~A() {
    if(previous != 0) previous->next=next;
    if(next != 0) next->previous=previous;
}

ข้างต้นเป็นตัวอย่างในภาษา C++ ครับ เป็นการสร้าง Linked List ซึ่งจะมีการ Reference หากันระหว่าง object (ของคลาสเดียวกัน) ซึ่งเป็นหัวใจหลักของการสร้าง Linked List เลย ดังนั้นการใช้ Circular dependency ในงานนี้ถือว่าดีครับ อย่างไรก็ตามมันก็ไม่ได้ดีแบบนี้เสมอไปเพราะมันจะทำให้เกิดการผูกมัดแน่นระหว่าง object ซึ่งทำให้การแก้ไขและการ reuse เป็นไปได้ยาก

References

Anti-pattern - Wikipedia

God object - Wikipedia

What is an anti-pattern? - stackoverflow

Circular dependency - Wikipedia

#‎day68 #365วันแห่งโปรแกรม ‪#‎โครงการ365วันแห่ง‬...