Jump to content
troikasix

Design Pattern Dalam C++ : Factory

Recommended Posts

Nak belajar pasal Pattern/Corak, tak susah cuma pening sikit tang mana nak mula.

Jadi pendapat saya, paling senang sekali, mulakan dengan Factory Pattern. Hati2, ini bukan Abstract Factory kalau nak cari kat internet. Tujuan memang sama, tapi kalau nak faham Abstract Factory. Kena fahamkan Factory Pattern dulu.

Motivasi untuk Factory pattern biasanya mula dengan terma "Refactor". Macam tajuk matematik tu... Pemfaktoran. Tapi dalam programming, pemfaktoran adalah proses selepas coding.

Kalau ikut cara biasa nak buat software, design dulu, baru coding. Tapi dalam pemfaktoran, coding dulu baru design. Bunyinya pelik, tapi biasa.

Katakan kita ada satu sistem utk buat operasi matematik camni (kod dipermudah utk pemahaman, baca betul betul)

double buatOperasi(char op, int x, int y)
{
    if(op == '+')
    {
        return x + y;
    }
    else if(op == 'x')
    {
        return x * y;
    }
}

int main ( void )
{
    char op;
    int x, y;

    cout << "Masukkan nombor yang yang hendak dioperate kan" << endl;
    cout << "dalam format [+,x] [nombor1] [nombor2]" << endl;
    cin >> op >> x >> y;
    cout << "Jawapan :" << buatOperasi(op,x,y);

    return 0;
}
Fungsi main bertindak sebagai antaramuka kepada pengguna program. Maklumat dikumpul dan diberi kepada fungsi yang akan mencerna jawapan. main kemudian memberitahu pengguna hasil jawapan. Fungsi buatOperasi pulak buat 2 kerja dalam 1. Pertama, buat keputusan. Kedua, logik atau algorithm utk dilaksanakan bagi mencerna jawapan. Saya tak mahu pembaca tinggal terus dan tak baca kod tadi. 2 fungsi tu dah pun dipermudah supaya lebih senang difahami. Tetapi dalam senario sebenar, kita akan lihat lebih banyak algorithm seperti ''error trapping'' atau nested if...else. Saya sebenarnya nak tunjuk betapa susah nak faham dan maintain kod yang panjang. Tapi kalau pembaca susah nak faham kod, camana nak explain pemfaktoran pulak. Melangkah kepada pemfaktoran. Factory Pattern adalah satu teknik untuk membahagikan tugas2 kepada class berlainan agar lebih bermakna dan terurus. Teknik ini adalah yang termudah untuk dipakai dan difahami. Daripada pattern ni, akan datang, bila perlu, kita boleh perkembang menjadi pattern lain. Tanpa menjejaskan program secara keseluruhan. Ada 3 jenis class yang digariskan dalam pattern ni 1- Factory - Pengilang 2- Interface - Antaramuka 3- Concrete Product - Produk yang dihasilkan oleh pengilang. Dengan wujudnya 3 jenis class yg kat atas ni, kita dah ada satu subsystem. (Saya tak reti nak upload gambarajah. kat forum ni. ada sesapa yg tahu tolong ajar) Langkah pemfaktoran menjadi Factory Pattern Mula - mula kita buat Interface. Dalam Bahasa Melayu Antaramuka.
class IOperasiApaSaje
{
public:
    virtual double beriJawapan(int x, int y) = 0;
    virtual ~IOperasiApaSaje();    // Destructor mesti virtual utk elak memory leak
};
class IOperasiApaSaje adalah orang tengah diantara program utama dengan logik/algorithm dalaman subsystem. Semua fungsi dalam class ni bersifat virtual. Pemanggil tak perlu tahu objek apa sebenarnya yang akan mereka gunakan. Dan bagaimana operasi dijalankan. Dengan cara ni, kita boleh sorok algorithm atau tukar algorithm sesuka hati tanpa menjejaskan logik pemanggil. Maknanya, bila kita tukar algorithm, pemanggil tak perlu tukar kod diorang. Tetapi, oleh kerana IOperasiApaSaje adalah pure virtual, pemanggil tak boleh buat objek ni sesuka hati macam ni
IOperasiApaSaje * pObj = new IOperasiApaSaje(); // Compiler error, tak boleh buat object. Class is virtual.
Error akan wujud masa proses kompil. Ini sebenarnya satu kelebihan sebab kita bawak error tu dari masa running kepada masa kompil disebabkan sifat virtual ini. Ini adalah betul. Kita tak mahu pelanggan menyalahguna interface kita tadi. Tapi camana pun kena bagi jugak cara supaya objek dapat di buat. Kat sinilah, pengilang main peranan. Langkah kedua, kita buat class pengilang untuk buat objek IOperasiApaSaje
class FactoryOperasi
{
public:
    static IOperasiApaSaje* dapatkanOperasi(char op)
    {
        switch (op)
        {
        case '+':
            return (IOperasiApaSaje*) new OperasiTambah();
            break;
        case 'x':
            return (IOperasiApaSaje*) new OperasiDarab();
            break;
        default:
            return 0;
            break;
        }
    }
};
class ni cuma mempunya satu fungsi dapatkanOperasi, iaitu fungsi untuk membuat keputusan dan membuat objek yang sepatutnya berdasarkan keputusan yang telah dibuat. Saya buat fungsi ni bersifat static sebab malas nak tulis banyak kod. Perasan tak dalam kod, fungsi dapatkanOperasi memulangkan pointer yang ditukar menjadi IOperasiApaSaje. Maknanya, client code cuma dapat interface. Diorang tak tahu objek apa yang sebenar. Kita berjaya menipu pelanggan dengan memperalatkan polymorphism bila buat macam ni. Objek sebenar yang di-create adalah objek dari jenis class OperasiTambah / OperasiDarab. Ingat, client cuma nampak objek IOperasiApaSaje. Tapi dalam subsystem kita objek lain, maka wujudnya manipulasi polymorphism. Objek sebenar berubah jadi objek interface tanpa pengetahuan pelanggan. Untuk memperboleh polymorphism, 2 kelas ni mesti berbentuk berasal dari IOperasiApaSaje.
class OperasiTambah : public IOperasiApaSaje
{
    double beriJawapan(int x, int y)
    {
        return x + y;
    }
};
class OperasiDarab : public IOperasiApaSaje
{
    double beriJawapan(int x, int y)
    {
        return x * y;
    }
};
tengok betul betul. 2 2 class inherit dari Interface IOperasiApaSaje. Maka wujudlah pertalian adik-beradik bila makbapak diorang sama. Dan kita katakan semua adik-beradik tu, ''sekumpulan'' class di bawah naungan IOperasiApaSaje. Akhirnya bolehla fungsi buatOperasi dimatikan sebab kod ini takkan berubah walaupun kita tambah lagi anak pada IOperasiApaSaje, ataupun ubah user-interface (fungsi ''main''). Perubahan berlaku masa run-time kerana objek berlainan dalam subsystem kita akan terhasil berdasarkan keputusan user.
double buatOperasi(char op, int x, int y)
{
    double rv = 0;

    IOperasiApaSaje * pOP = FactoryOperasi::dapatkanOperasi(op);
    rv = pOP->beriJawapan(x, y);
    delete pOP;

    return rv;
}
Maka selesailah proses pemfaktoran paling simple kita. Jelas berlaku penggunaan virtual, polymorphism dan encapsulation. Mungkin ada yang tertanya, kan senang terus pakai if...else atau switch...case sahaja. Jawapannya, tengok pada complexity. Berdasarkan contoh saya kat atas tadi, mmg betul, lebih senang pakai if...else/switch...case. Tapi ini adalah contoh mudah. Ikut kata pujangga (sayalah pujangga tu) "Perubahan pasti berlaku". Dalam kisah benar penggunaan corak Factory, ada satu subsystem biasa kita lihat. ''printing report''. Terdapat banyak jenis report. Setiap report mempunyai format dan layout yang berlainan. Sumber data yang berlainan, dan sebagainya yang berlainan, header, footer, margin. Kalau guna if...else/switch...case shj, alamat tunggang terbalik kod tu nanti. Susah nak faham bila baca semula. Saya malas nak guna susbsystem report printing sebagai contoh, panjang sgt. Kod lebih banyak ''dibaca'' dari ''ditulis'' kerana ''Perubahan pasti berlaku'' <----Tulis kat kertas tampal kat dinding. Ini kata kata azimat. Bila kita nak ubah sesuatu, biasanya kita akan baca balik semua kod yang kita tulis hari semalam utk difahami semula. Walaupun perubahan tu sikit. Dan kena pastikan perubahan yang dibuat tak menggangu tempat logik dalam subsystem lain. Jadi amat penting untuk kita cepat memahami dan meleraikan penggantungan kod antara satu subsystem dengan yang lain. Dalam ertikata lain, ubah kat sini, sana tak effect. Ubah sana, sini tak effect. Dalam contoh di atas, katakan saya nak tambah support untuk operasi '-' tolak. Saya cuma perlu buat satu lagi class
class OperasiTolak : public IOperasiApaSaje
{
    double beriJawapan(x,y)
    {
        return x - y;
    }
};
dan tambah satu lagi ''case'' dalam class pengilang
class FactoryOperasi
{
public:
    static IOperasiApaSaje* dapatkanOperasi(char op)
    {
        switch (op)
        {
            ...
        case '-'
            return new OperasiTolak();
            break;
            ...
        }
    }
};
Nampak tak betapa senangnya nak tambah kebolehan program ni. Saya cuma ubah 2 benda, penambahan class OperasiTolak dan FactoryOperasi utk support class yang baru ni. Perubahan masih dalaman pada subsystem. Tak menjejaskan fungsi buatOperasi dan fungsi main. Ok.. kekadang ada gak sikit. Supaya pengguna tahu wujudnya operasi tolak, kita ubah sikit fungsi ''main'' camni.
int main ( void )
{
    ...
    cout << "dalam format [+,x,-] nombor1 nombor2" << endl; 
    ...
}

Tapi macammana pun logik program ni tak berubah. Yang kita ubah cuma ayat yg akan dipaparkan shj. Senang sangat bila dah ada struktur pattern dalam kod. Nak buat perubahan... anytime. Takkan menjejaskan program pulak tu. Saya rasa best giler.. guna pattern.

Kesimpulan

Elemen penting Factory Pattern

1- Factory - Pembuat keputusan

2- Interface - Penghubung subsystem kepada dunia luar

3- Product - Objek yang sebenar yang membuat kerja. Mesti anak kepada Interface

Ironi bila buat pemfaktoran, class kita jadi ramai. Kod jadi banyak. Ini mmg biasa berlaku terutama bila kita asingkan tanggungjawab pada setiap class berbanding superclass dari planet krypton yang boleh buat semua kerja.

Tapi hasilnya, kita boleh lukis diagram interaksi objek dalam subsystem kita. Kod lebih mudah difahami berbanding all-in-one class. Kita boleh sorok implementasi setiap objek dgn berkesan. "Black box".

Beberapa tips tambahan

* Jangan bagi banyak sgt kerja dlm sesuatu class.

* Bila kita nampak wujudnya if...else atau switch...case dalam kod. Itu calon paling sesuai untuk memperalatkan polymorphism.

* Masa buat perubahan pada kod, pertimbangkan teknik pemfaktoran supaya lebih mudah dimaintain dimasa akan datang.

* Buat eksperimen menggunakan Factory pattern ni pada kod korang. Mula dgn yang simple. Pastu dah berani, baru buat pada yang kompleks.

Kisah Benar

* Implementasi teknologi COM (Active-X, *.OCX, ADO, OleDB, ...etc..) oleh Microsoft, menggunakan Factory Pattern dalam bahasa 'C'.

Share this post


Link to post
Share on other sites

Nak tanya sikit, saya dah lama benar tak usik C/C++, sbb sekarang ni saya lebih tertumpu kpd C# .NET.

Nampaknya peggunaan OO dlm C++ tak jauh beza dgn C# dari segi syntax, cara penggunaan class dan konsepnya. Tapi ada satu yg saya pelik, ada ke dlm C++ meggunakan konsep Interface sprt code diatas?, sbb yg saya tau hanya C# dan Java je menggunakan Interface sbg menggantikan multiple inheritence dlm C++. Cuba bagi explanation sikit. Sorry kalau saya tersilap.

Share this post


Link to post
Share on other sites

Nak tanya sikit, saya dah lama benar tak usik C/C++, sbb sekarang ni saya lebih tertumpu kpd C# .NET.

Nampaknya peggunaan OO dlm C++ tak jauh beza dgn C# dari segi syntax, cara penggunaan class dan konsepnya. Tapi ada satu yg saya pelik, ada ke dlm C++ meggunakan konsep Interface sprt code diatas?, sbb yg saya tau hanya C# dan Java je menggunakan Interface sbg menggantikan multiple inheritence dlm C++. Cuba bagi explanation sikit. Sorry kalau saya tersilap.

Design Pattern berbentuk universal. Bukannya specific kpd sesuatu bahasa. Interface tu pun salah satu konsep dalam pattern.

Maknanya, kalau C++, guna perkataan 'interface', bukan bermakna pinjam dari Java atau C#. Atau C# guna perkataan "iterator", bukan pinjam dari C++. Ini semua dari pattern.

Pattern is the common ground. Belajar pattern, automatik belajar semua bahasa. Kan seronok.

Java dgn C# cuma follow dgn pattern ni. Diorang masukkan perkataan interface jadi reserved keyword utk lebih support Design Pattern. Bukan interface jer, ada lagi cthnya macam "abstract", "iterator", "clone".

Tapi C++ lebih matang dengan pattern. Malah dah introduce topik baru "Template Metaprogramming" lanjutan kepada pattern. Ini lagi advance dan kod jadi lagi pendek. Ikut kajian, "template metaprogramming" kadang2 menghasilkan applikasi lebih laju dari bahasa C sambil menggunakan implementasi pattern.

Dengar kabarnya, C# .Net 3.5 "ORCA'S" ada implementation guna "Template Metaprogramming". C++ dah mula sejak 80-an lagi masa lahirnya "Standard Template Library".

Terkini, C++ keyword nak tambah keyword "lambda" (function dalam function) pulak. tak tau lepas atau tidak ISO C++ review board, tengoklah nanti.

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...

×
×
  • Create New...