Abordare adapter pattern

Fac rework la o aplicaţie şi m-am hotărât să decuplez partea logică de partea de UI. Am frunzărit prin pattern-uri şi cel mai potrivit mi se părut adapter pattern.

Practic îi dai părţii logice să lucreze cu nişte widget-uri abstracte, care sunt implementate în modulul UI.

#include <iostream>
#include <string>

typedef std::string String;


// interfata abstracta
class TextWidgetInterface {
public:
    virtual void setText(const String &str) = 0; // metoda abstracta
};

// functie care "rontaie" interfete abstracte
void aduna_si_afiseaza(TextWidgetInterface *textWidget, int a, int b)
{
    String str = std::to_string(a + b);
    textWidget->setText(str);
}


// implementare interfata
class TextWidget : public TextWidgetInterface {
public:
    void setText(const String &str) {
        printf("%s\n", str.c_str());
    }
};


int main()
{
    TextWidget textWidget;
    aduna_si_afiseaza(&textWidget, 1, 2);
}

http://cpp.sh/7f3pk

Practic, as putea sa implementez TextWidget cu orice toolkit, sky is the limit :slight_smile:

Ce parere aveti despre aceasta abordare? Se poate îmbunătăţi?

2 Likes

Ce ai facut tu se numeste “Programming to an interface” sau pur si simplu decuplare.
Adapter pattern e folosit sa “adapteze” un obiect cu alt obiect total diferit. Face obiectul din urma sa se comporte ca primul obiect.
E foarte bine ce faci tu doar ca nu te incurca in denumiri si implementarea design pattern-urilor efective. Daca aplici principiile SOLID e foarte bine.

2 Likes

Corect, dar nu-l opreste nimeni sa adapteze in TextWidget (adica TextWidget sa fie wrapper) ceva ce are o interfata diferita de ce expune el.

E prima dată când aud de “programming to an interface” şi nu sunt foarte sigur că înţeleg care e diferenţa. Poţi să detaliezi un pic? “Programming to an interface” îmi pare mai degrabă a variantă de implementare pentru “adapter pattern”.

Păi intr-un fel sau altul, TextWidget trebuie să fie un wrapper peste widget-ul corespunzător toolkit-ului pe care il folosesc (GTK+ Text Widget, QTextLine, FXTextField, Cocoa Text Field etc). Sau chiar sa apeleze widget-ul corespunzator din Android NDK prin JNI.

1 Like

O recomandare: daca poti sa pui mana pe cartea GoF: https://en.wikipedia.org/wiki/Design_Patterns , fa-o, face cat foarte, foarte multe discutii pe forumuri :slight_smile:

1 Like

E bună teoria, dar face bine şi să discuţi despre ce ai citit. Uneori îmi dau seama ce trebuie făcut chiar în timp ce formulez întrebarea pentru forum :slight_smile:

2 Likes

Am găsit un pattern pentru abstractizare şi mai interesant (mersi @anon31094663 pentru recomandarea de a citi GoF): bridge pattern. In cazul acestui pattern nu sunt obligat sa-i dau clientului să “ronţăie” clase pur abstracte (asta ma obliga sa folosesc pointeri şi imi complica viaţa dacă voiam să folosesc operatorii << şi >>).

De exemplu, definesc o interfaţă generică care acceptă string-uri, la care “conectez” o implementare oarecare (care poate sa faca orice cu stringul ala, sa-l scrie pe disc sau sa-l afiseze pe ecran sau sa-l trimita pe un socket, pe generatorul stringului nu-l intereseaza).

http://cpp.sh/9rqpn

#include <string>
#include <iostream>

typedef std::string String;


class StringIfaceImp {
public:
    virtual void operator<<(const String&) = 0;
};

class StringIface {
public:
    StringIface() {}
    void operator=(StringIfaceImp *imp) { this->imp = imp; }
    virtual void operator<<(const String &str) { (*imp) << str; }

protected:
    StringIfaceImp *imp;
};


class TextWidget : public StringIfaceImp {
public:
    void operator<<(const String &str) { std::cout << "TextWidget: " << str << std::endl; }
};


int main(int argc, char *argv[])
{
    TextWidget text_widget;
    StringIface str_iface;

    str_iface = &text_widget;
    str_iface << "test";

    return 0;
}
1 Like