Programming Paradigm ทั้งสามของ C++

พูดเรื่องพื้นฐานเช่นเดิมครับ ผมคิดว่าโปรแกรมเมอร์ส่วนใหญ่ที่ไม่เคยใช้งานเจ้าภาษานี้มักจะคิดว่าภาษา C++ นั้นเป็นภาษาที่ยาก ซับซ้อน อ่านไม่รู้เรื่อง และอะไรต่อมิอะไรแล้วแต่จะจินตนาการณ์ไปตามเรื่อง ว่ากันง่าย ๆ คือยังไม่ทันลองหัดเลยก็คิดไปกันก่อนแล้วนั่นล่ะ ปัญหานี้ทำให้ประเทศเราขาดประชากรโปรแกรมเมอร์ C++ อย่างหนัก ส่งผลให้ค่าตัวของคนเขียนภาษานี้สูงกว่าภาษาอื่น

จากการสำรวจของ Adecco ในปี 2014 พบว่า ค่า ตัวของโปรแกรมเมอร์ภาษา C++ ในประเทศไทยที่มีประสบการณ์ตั้งแต่ 5 ปีขึ้นไปนั้นเริ่มต้นที่ 50,000 บาทต่อเดือนครับ (ที่จริง ตัวเลขนี้สูงเกินจริงไปนิด แล้วก็ภาษาอื่นในกลุ่มเดียวกันก็ราคานี้แหละ 555 เอาเป็นว่าผมหลอกคุณละกัน) ฟังดูน่าสนใจใช่ไหมครับ

วันนี้จะมาเล่าให้ฟังว่าพื้นฐานของ C++ นั้นเป็นอะไรครับ คืองี้ครับ C++ เป็นภาษาที่รวมเอา programming paradigm ที่ได้รับความนิยมสูง 3 ตัวมารวมกันในภาษาเดียว (ภาษาสมัยใหม่อย่าง Java, C# เองก็เป็นแบบนี้เหมือนกัน) ทั้งสามนั้นก็ได้แก่

  1. Procedural Programming
  2. Object-Oriented Programming
  3. Metaprogramming

Procedural Programming

Procedural Programming เป็นคอนเซพท์ดั้งเดิมของ C++ ซึ่งได้รับการสืบทอดมาจากภาษา C คอนเซ็พท์นี้จริง ๆ ค่อนข้างตรงไปตรงมา … มันก็คือการเขียนโค๊ดในรูปแบบของฟังก์ชั่นทางคณิตศาสตร์ อย่าง f(x, y) = x^2 + 2xy +y^2 อะไรทำนองนี้นั่นล่ะครับ

ใน C++ ใช้ syntax เดียวกับภาษา C เลย ฟังก์ชั่นข้างบนเลยสามารถเขียนได้แบบนี้

int function(int x, int y) 
{
    return pow(x, 2) + (2*x*y) + pow(y, 2);
}

ง่ายไหม ?

Object-Oriented Programming

คอนเซพท์ของ OOP นั้นค่อนข้างกว้าง โดยหลัก ๆ แล้วมันคือการมองว่าหน่วยย่อยของโปรแกรมเป็นวัตถุ (ในคอนเซพท์อื่นจะมองว่าหน่วยย่อยของโปรแกรมนั้นเป็นแค่ก้อนข้อมูล) และการเขียนโปรแกรมก็คือการปฎิสัมพันธ์กันระหว่างวัตถุ

OOP แบบของ C++ นั้นจะเป็นแบบ Static Type ก็คือวัตถุทุกชนิดจะถูกสร้างโดยจะมี Class เป็นแม่แบบ และประเภทของวัตถุนั้นก็จะถูกนิยามโดย Class เมื่อถูกนิยามไปแล้วก็จะไม่สามารถเปลี่ยนแปลงลักษณะการทำงานภายในได้

เดี๋ยวเรื่อง OOP จะพูดถึงอย่างละเอียดในโอกาสถัดไปครับ รายละเอียดเยอะน่าดู

Metaprogramming

Metaprogramming นั้นเป็นลักษณะที่ตัวโปรแกรมจะยังไม่สมบูรณ์ในตัวมันเองในขณะที่เราเขียน ชุดโปรแกรมนั้นจะถูกเติมเต็มให้สมบูรณ์ในตอนที่เรานำไปใช้งานครับ

Metaprogramming ของ C++ นั้นจะเป็นลักษณะ Template โดย Template นั้นคือโปรแกรม (อาจจะเป็นคลาสหรือฟังก์ชั่นก็ได้) ที่มี Type บางตัวที่ประกาศเอาไว้เป็นแค่ placeholder ขึ้นมา เช่น

template <class T>
T sum(T x, T y) 
{
    return x + y;
}

ตัว Type T เนี่ยตอนที่เขียนเราวางเอาไว้เฉย ๆ ว่าจะแทนที่ด้วย Type อื่นในตอนที่เอาไปใช้ ซึ่งตอนเอาไปใช้จริง ๆ ก็ค่อย ๆ มากำหนดอีกทีว่าเป็น Type ไหนแบบนี้ครับ

int a = sum<int>(5, 10);
float b = sum<float>(0.25f, 0.85f);

ทั้งนี้ถ้า Type ที่เราใส่เข้าไปนั้นไม่มีฟังก์ชั่นหรือโอเปอเรเตอร์ที่ฟังก์ชั่น sum() รองรับ โปรแกรมก็จะคอมไพล์ไม่ผ่าน เท่านั้นเอง 🙂

อ้อ เราสามารถกำหนด Type หลอก ๆ ไว้กี่ตัวก็ได้ครับ ใส่เอาไว้ใน template< > นั่นแหละ กี่ตัวก็ใส่ไป คั่นด้วย comma

ก็จบเรื่องนี้แต่เพียงเท่านี้ โอกาสหน้าจะพูดถึง OOP ในรายละเอียดนะครับ

Edit: อ.Tapanan Yeophantong(@tapananYph) ติงมาว่า Functional Programming ที่พูดถึงน่าจะหมายถึง Procedural Programming มากกว่า ก็เลยแก้ไขให้ถูกต้อง ขอขอบคุณอ.มาณ.ที่นี้ครับผม

Mock – ออปเจ็คท์หลอกๆ สำหรับใช้ทดสอบโปรแกรม

หลาย ๆ คนคงรู้จักคำว่า mock-up คำนี้มีความหมายว่า แบบจำลอง หรือวัตถุปลอม ๆ ที่เอาไว้สำหรับแสดงให้ลูกค้าดูว่าเมื่อผลิตภัณฑ์สร้างเสร็จแล้วหน้าตามันจะเป็นอย่างไร คำนี้มีเป็นคำผสมที่มีพื้นฐานจากคำว่า mock ที่แปลว่าการล้อเลียน

แล้วไอ้การล้อเลียนที่ว่านี้มันใช้ทำอะไรในการพัฒนาโปรแกรมได้ ?

คืองี้ครับ หลาย ๆ ครั้งที่เราต้องเขียนโปรแกรมที่ซับซ้อน และต้องอาศัย parameter จากคลาสที่มีความซับซ้อนมาก ๆ ซึ่งการทดสอบโปรแกรมนั้น ๆ ด้วยการสร้างตัวแปรขึ้นมาเป็น parameter ส่งเข้าไปนั้นอาจจะเป็นเรื่องที่ทำได้ยาก

อย่างเช่นสมมติว่าเราพัฒนาโปรแกรมที่เป็น Servlet โดยเรามีคลาสที่สืบทอดมาจาก HTTPServlet และเรากำลังจะเขียนโปรแกรมทดสอบเจ้าเมธอด doGet() ที่มีหน้าตาแบบนี้

class MyServlet extends HTTPServlet {
    @Override
    protected void doGet(HttpServletRequest req,
                 HttpServletResponse resp) {
    /* Implementations */
    }
}

คำถามคือ แล้วเราจะสร้างตัวแปรที่มีประเภทเป็น HttpServletRequest และ HttpServletResponse ได้อย่างไร ? เพราะเจ้า type ทั้งสองตัวนี้เป็น Interface ครับ

คำตอบน่ะเหรอ … เขียนคลาสใหม่ขึ้นมา implement เจ้าสองคลาสนี้ไปเลยครับ

class MockHttpServletRequest implments HttpServletRequest{
     /* implements every method here*/
}

class MockHttpServletResponse implements HttpServletResponse {
    /* implements every method here*/
}

เขียน implementation ของแต่ละ method ข้างในให้ง่ายที่สุดครับ อันไหน get ก็คืนค่าคงที่ไปเลย อันไหน set ก็สร้างตัวแปรมารับ อะไรทำนองนี้ เช่น

class MockHttpServletRequest implments HttpServletRequest{
    String getContextPath() {
        return "/";
    }
}

ทั้งนี้ถ้าเป็น method ที่ใช้ในการทดสอบด้วย ก็ต้องเขียนให้สอดคล้องกับ test case ด้วยนะครับ

ทีนี้เราก็เขียนโปรแกรม unit test ได้ง่าย ๆ แบบนี้ครับ

class MyHttpServletTest {
    @Test
    void testDoGetRootPath() {
        HttpServletRequest req = new MockHttpServletRequest();
        HttpServletResponse resp = new MockHttpServletResponse();
        MyHttpServlet servlet = new MyHttpServlet();

        servlet.service(req, resp);
    }
}

อันนี้พอดีว่า doGet() มันเป็น private method ครับ ไปเรียกตรง ๆ ไม่ได้ ผมเลี่ยงไปเรียก service() แทน เพราะผมรู้ว่ามันจะไปเรียก doGet() แต่ถ้าอยากทดสอบเจ้า doGet() ก็มีอีกวิธี ก็คือเขียนชุดโปรแกรมทดสอบเป็น class ที่สืบทอดเจ้า MyHttpServletTest อีกต่อไปเลยโลด

class MyHttpServletTest extends MyHttpServlet{
    @Test
    void testDoGetRootPath() {
        HttpServletRequest req = new MockHttpServletRequest();
        HttpServletResponse resp = new MockHttpServletResponse();
        MyHttpServletTest servlet = new MyHttpServlet();

        servlet.doGet(req, resp);
    }
}

แค่นี้เอง

(อีกวิธีคือเอาตัว test ใส่ลงไปในคลาสที่จะ test ไปเลย ซึ่งไม่แนะนำครับ เพราะว่าตัว test จะติดไปในโค๊ดที่จะเอาไปรันจริงด้วย ทำให้มันกินแรมมากขึ้นเปล่า ๆ)

ปัญหาของการใช้ Mock คือ … ในกรณีที่แย่ที่สุดเราอาจจะต้องสร้างคลาสขึ้นมาสำหรับแต่ละเทสต์เคสเลย ซึ่งมันเยอะมาก และมันก็อาจจะไม่ชัดเจนพอที่เอาไปใส่ใน unit test (ซึ่งควรจะเรียบง่ายที่สุดไม่งั้นมันจะอ่านยาก) ที่แย่กว่านั้นเราต้อง implement เมธอดที่เราไม่ได้ใช้ทดสอบด้วย ทำให้โค๊ดยาวโดยใช้เหตุ

สำหรับจุดนี้เราสามารถใช้ mock framework เข้ามาช่วยได้ครับ ในภาษา Java นั้นมี Mock Framework ที่ได้รับความนิยมอยู่หลายตัว (ลอง Google ดูนะครับ) ผมจะลองใช้ Mockito ดู เจ้าโปรแกรมทดสอบมันก็จะเหลือแค่นี้

import static org.mockito.Mockito.*;

class MyHttpServletTest extends MyHttpServlet{
    @Test
    void testDoGetRootPath() {
        HttpServletRequest req = mock(HttpServletRequest.class);
        when(req.getContextPath()).thenReturn("/");
        HttpServletResponse resp = mock(HttpServletResponse.class);
        MyHttpServletTest servlet = new MyHttpServlet();

        servlet.doGet(req, resp);
    }
}

แค่นี้เอง ผมไม่ต้องสร้างคลาสใหม่ขึ้นมาเพื่อใช้ทดสอบด้วย ยอดไปเลย 🙂

สุดท้ายนี้ถ้าถามว่าทำไมถึงเรียกว่า mock ผมคิดว่ามันเกิดจากการล้อเลียนเจ้า class จริง ๆ น่ะครับ เราสร้าง class ใหม่ขึ้นมาล้อเลียนตัวเดิมที่ใช้งานได้ กลายเป็นคลาสง่อย ๆ ตัวนึง อะไรทำนองนี้ ที่จริงการสร้าง mock-up ก็คือการสร้างวัตถุชิ้นหนึ่งที่มีหน้าตาเหมือนของจริงแต่ทำอะไรไม่ได้ ใช่ไหมล่ะครับ 😉 เจ้า mock object นี่ก็เหมือนกันแหละ

ปล.โค๊ดข้างบนยังไม่ได้เทสต์ครับ อาจจะมีจุดผิดได้ 555 ลองดูก็แล้วกัน

สร้าง SDL_Texture จาก FT_Bitmap

หลังจากห่างหายกับเรื่องการวาดตัวอักษรไปนาน วันนี้จะมาอัพเดตเรื่องนี้สักเล็กน้อย

ในตัวอย่างเก่า ๆ ผมใช้การสร้าง SDL_Surface ขึ้นมาแล้วเรียก SDL_CreateTextureFromSurface() ในการสร้าง Texture ก่อนที่จะวาดลงไป วิธีนี้มีข้อเสียเพราะว่าเราจะมี object ใช้แล้วทิ้งเพิ่มมาหนึ่งตัว ซึ่งถ้าเราสามารถลดตรงนี้ไปได้โค๊ดเราก็จะทำงานได้อย่างมีประสิทธิภาพมากขึ้นครับ

โค๊ดก็ไม่ได้มีอะไรซับซ้อนเลย

SDL_Texture* CreateTextureFromFT_Bitmap(SDL_Renderer* renderer,
                                        const FT_Bitmap& bitmap,
                                        const SDL_Color& color)
{
    SDL_Texture* output = SDL_CreateTexture(renderer,
            SDL_PIXELFORMAT_RGBA8888,
            SDL_TEXTUREACCESS_STREAMING,
            bitmap.width,
            bitmap.rows);

    void *buffer;
    int pitch;
    SDL_LockTexture(output, NULL, &buffer, &pitch);

    unsigned char *src_pixels = bitmap.buffer;
    unsigned int *target_pixels = reinterpret_cast<unsigned int*>(buffer);

    SDL_PixelFormat* pixel_format = SDL_AllocFormat(SDL_PIXELFORMAT_RGBA8888);

    for (int y = 0; y < bitmap.rows; y++)
    {
        for (int x = 0; x < bitmap.width; x++)
        {
            int index = (y * bitmap.width) + x;
            unsigned int alpha = src_pixels[index];
            unsigned int pixel_value =
                    SDL_MapRGBA(pixel_format, color.r, color.g, color.b, alpha);

            target_pixels[index] = pixel_value;
        }
    }

    SDL_FreeFormat(pixel_format);
    SDL_UnlockTexture(output);

    return output;
}

ผมเปลี่ยนไปใช้ return statement แทนการใช้ pass-by-reference นะครับ โค๊ดจะอ่านง่ายขึ้นพอสมควร ส่วนการเอาไปใช้ก็เหมือนเดิมครับ อาจจะต้องปรับเปลี่ยนโค๊ดเดิมนิดหน่อยแต่ก็ไม่เกินความสามารถกันอยู่แล้ว

โค๊ดชุดนี้มีการลดการใช้ hardcode ในส่วนของการคำนวนค่าในแต่ละ pixel ลง จากของเก่าจะเป็นการยึด format มาเลย ก็เปลี่ยนมาใช้ SDL_MapRGBA() แทน แต่โค๊ดนี้ก็ยังอยู่ภายใต้ข้อจำกัดที่ว่าแต่ละ pixel นั้นมีขนาด 4 byte อันนี้อาจจะมีการแก้ไขในอนาคตครับ

อ่านแล้วก็ไปลองเล่นกันดูนะครับ 🙂