Jump to content
troikasix

Design Pattern Dalam C++ : Builder Pattern

Recommended Posts

Objek bercirikan Builder adalah blueprint untuk membina objek-objek kompleks. Idea utama adalah untuk memisahkan persembahan objek dengan cara pembinaannya.

Pembinaan objek yang boleh diubah semasa runtime berdasarkan permintaan/settings pengguna, memberi look-feel lebih mantap pada software.

Bayangkan objek builder ini sebagai pekerja buruh binaan yang diarah bekerja oleh kepala kontraktor mereka.

Watak utama dalam Pattern Builder ni:-

1- Builder = Pekerja yang membuat kerja membina objek

2- Director aka Contractor aka Architect = Mandur memberi arahan kepada pekerja

Katakan saya ada handler menu untuk mengeksport report dalam bentuk Xml atau Html.

void OnClickChangeFormat(LPCTSTR szReportFormat)
{
    if ( strcmp( szReportFormat, "xml") == 0 )
    {
        cout << "<xml version 1.0>" << endl;
        cout << "<header>Report dalam Xml</header>" << endl;
        cout << "<body>Abu, Bakar, Chin</body>" << endl;
        cout << "<footer>Muka 1 daripada 1</footer>" << endl;
        return;
    }
    else if ( strcmp( szReportFormat, "html") == 0 )
    {
        cout << "<html>" << endl;
        cout << "<header>Report dalam html</header>" << endl;
        cout << "<body>Abu, Bakar, Chin</body>" << endl;
        cout << "<footer>Muka 1 daripada 1</footer>" << endl;
        cout << "</html>" << endl;
        return;
    }

    cout << "Ralat!!, format diminta tidak wujud dalam sistem ini" << endl;
}
Lepas tu pelanggan minta tambah format excel. Kalau saya kekalkan fungsi ni sedemikian rupa, makin banyak format, makin susah nak maintain. Jadi...
// Interface kepada Objek pekerja
class IReportBuilder 
{
public:
    virtual string writeHeader() = 0;
    virtual string writeBody() = 0;
    virtual string writeFooter() = 0;

    // Destructor mesti virtual
    virtual ~IReportBuilder() {;}
};
// Pekerja pakar buat Xml
class XmlBuilder : public IReportBuilder
{
    string writeHeader() { return "<xml version 1.0>\n<header>Report dalam xml</header>"; }
    string writeBody() { return "<body>Abu, Bakar, Chin</body>"; }
    string writeFooter() { return "<footer>Muka 1 daripada 1</footer></html>"; }
};
// Pekerja pakar membuat Html
class HtmlBuilder : public IReportBuilder
{
    string writeHeader() { return "<html>\n<header>Report dalam html</header>"; }
    string writeBody() { return "<body>Abu, Bakar, Chin</body>"; }
    string writeFooter() { return "<footer>Muka 1 daripada 1</footer></html>"; }
};

//Mandur
class ReportDirector 
{
public:
    ReportDirector& setBuilder( ReportBuilder& bd )
    {
        pBuilder = &bd
        return *this;
    }
    void construct();
    void show( std::iostream output )
    {
        //binaan dilakukan disini dgn memberi arahan kepada pekerja
        output << pBuilder->writeHeader() << endl;
        output << pBuilder->writeBody() << endl;
        output << pBuilder->writeFooter() << endl;
    }
private:
    IReportBuilder * pBuilder;
};

void OnClickChangeFormat(LPCTSTR szReportFormat)
{
    ReportDirector rd;
    IReportBuilder * pBuilder;

    // kod berikut boleh diubah menggunakan Factory Pattern.
    if ( strcmp( szReportFormat, "xml") == 0 )
    {
        pBuilder = new XmlBuilder;
    }
    else if ( strcmp( szReportFormat, "html") == 0 )
    {
        pBuilder = new HtmlBuilder;
    }
    
    // Kerja pembinaan bermula disini. Tanpa mengambil kira siapa yang membuat kerja, xml atau html
    rd.setBuilder(pBuilder);
    rd.construct();            // Mandur memberi arahan membuat kerja
    rd.show( cout );

    delete pBuilder;
}
Apabila pengguna klik pada menu dan membuat pilihan, objek director akan membina report yang baru menurut permintaan. Perhatikan bahawa kod untuk pembinaan bermula dari
...rd.setBuilder(pBuilder)....
boleh diguna utk sebarang format yang diminta. Teknik ini membolehkan antaramuka diubah pada masa run-time dengan bertukar-tukar builder. Hanya satu objek Director yang diperlukan. Bukan itu shj, boleh guna semula kod yang sama untuk tujuan lain. Contohnya, untuk menambah fungsi simpan ke fail,
void OnSaveReport(LPCTSTR szReportFormat)
{
    ReportDirector rd;
    IReportBuilder * pBuilder;

    if ( strcmp( szReportFormat, "xml") == 0 )
    {
        pBuilder = new XmlBuilder;
    }
    else if ( strcmp( szReportFormat, "html") == 0 )
    {
        pBuilder = new HtmlBuilder;
    }
    
    rd.setBuilder(pBuilder);    // Mandur memanggil pekerja mana
    rd.construct();            // Mandur memberi arahan membuat kerja

    // buat objek fail untuk output
    ofstream fs("out.txt");
    rd.show( fs );    // rd.show( cout );
    fs.flush();
    fs.close();

    delete pBuilder;
}

Kesimpulan

1- Builder pattern menyelesaikan masalah pembinaan objek kompleks dengan membahagikan tugas kepada objek "Pekerja"/"Builder" dan "Mandur"/"Director".

2- Mana mana objek boleh ditukar tanpa menjejaskan pihak yang lain.

3- Peluang penggunaan semula kod (reuse) amat tinggi. Boleh cakap kat bos, "Saya boleh buat fungsi "Save As" pelbagai format dalam masa 2 hari. (Buat 5 minit dah siap + stabil, selebihnya tido. Barulah professional).

4- Penggunaan pattern tidak semestinya habis pada satu-satu pattern. Kita boleh mencampur-adukkan semua jenis pattern yg ada mengikut kreativiti dan keadaan.

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...