0

I've tried to transform this code into Object Adapter, but as i'm still trying to fully understand this i didn't find a correct way to do so.

This is the only Adapter code that I found in C++, and other codes are often not complete, showing just the adapter part, so i also find it hard to write my own code.

Other answers on the site didn't help. If someone could rewrite it in Object Adapter and explain to me what exactly is the difference, i'd be very thankful

#include <iostream>

using namespace std;

typedef int Coordinate;
typedef int Dimension;

// Desired interface
class Rectangle
{
  public:
    virtual void draw() = 0;
    };

// Legacy component
class LegacyRectangle
{
  public:
    LegacyRectangle(Coordinate x1, Coordinate y1, Coordinate x2, Coordinate y2)
    {
        x1_ = x1;
        y1_ = y1;
        x2_ = x2;
        y2_ = y2;
        cout << "LegacyRectangle:  create.  (" << x1_ << "," << y1_ << ") => ("<< x2_ << "," << y2_ << ")" << endl;
    }
    void oldDraw()
    {
        cout << "LegacyRectangle:  oldDraw.  (" << x1_ << "," << y1_ << 
          ") => (" << x2_ << "," << y2_ << ")" << endl;
    }

  private:
    Coordinate x1_;
    Coordinate y1_;
    Coordinate x2_;
    Coordinate y2_;
};

// Adapter wrapper
class RectangleAdapter: public Rectangle, private LegacyRectangle
{
  public:
    RectangleAdapter(Coordinate x, Coordinate y, Dimension w, Dimension h):
      LegacyRectangle(x, y, x + w, y + h)
    {
        cout << "RectangleAdapter: create.  (" << x << "," << y << 
          "), width = " << w << ", height = " << h << endl;
    }
    virtual void draw()
    {
        cout << "RectangleAdapter: draw." << endl;
        oldDraw();
    }
};

int main()
{
  Rectangle *r = new RectangleAdapter(120, 200, 60, 40);
  r->draw();
}

2 Answers2

0

The relationship of RectangleAdapter with LegacyRectangle should be "has-a" instead of "is-a".

class RectangleAdapter: public Rectangle
{
  public:
    RectangleAdapter(Coordinate x, Coordinate y, Dimension w, Dimension h):
      legacyRectangle(x, y, x + w, y + h)
    {
        cout << "RectangleAdapter: create.  (" << x << "," << y << 
          "), width = " << w << ", height = " << h << endl;
    }
    virtual void draw()
    {
        cout << "RectangleAdapter: draw." << endl;
        legacyRectangle.oldDraw();
    }
private:
    LegacyRectangle legacyRectangle;
};

Note: I didn't test it

sithereal
  • 1,656
  • 9
  • 16
  • [Sure](https://en.wikipedia.org/wiki/Composition_over_inheritance) - but I don't feel like it's the core of the question. Seems to me as if Luccas hasn't understood the principle per se... – Aconcagua Apr 25 '17 at 06:56
  • 2
    Private inheritance is not an is-a relation: https://isocpp.org/wiki/faq/private-inheritance#priv-inherit-like-compos – stefaanv Apr 25 '17 at 07:02
  • @stefaanv But still there, they state "Use composition when you can, private inheritance when you have to." – Aconcagua Apr 25 '17 at 07:05
  • @Aconcagua: agreed, but that doesn't negate my statement. Also [this link](http://stackoverflow.com/questions/5467005/adapter-pattern-class-adapter-vs-object-adapter) seem to agree with the answer. – stefaanv Apr 25 '17 at 07:07
  • 1
    @stefaanv Depends on where you look from. From the inside of the class and for technical reasons(vtables, etc), secretly being a LegacyRectangle is still being a LegacyRectangle – sithereal Apr 25 '17 at 07:13
  • "is-a" or "has-a" are semantics and have nothing to do with technicalities. Anyway, you are arguing with the c++-faq. – stefaanv Apr 25 '17 at 07:24
  • @stefaanv Well, there's(or has to be) a homomorphism between the mind->semantics->code syntax. It's totally safe to point out if there is anything feels incosistent in this mapping(even if it's c++-faq) – sithereal Apr 25 '17 at 07:45
  • Spoken as a true [sithereal (dutch/english joke)](http://www.interglot.com/dictionary/nl/en/translate/sidderaal), still, in this case I'd still put my money on the c++-faq being correct: private inheritance is used for a "has-a" relation. If we can't agree on that, then the semantics are meaningless. – stefaanv Apr 25 '17 at 07:59
0

This is the only Adapter code that I found in C++, and other codes are often not complete, showing just the adapter part, so i also find it hard to write my own code.

Well, it is working fine...

[...] i'm still trying to fully understand this [...]

So I assume it would help to explain the example in detail...

Problem for need of such adapter classes is typically that you have some legacy code you want to integrate or use into your own API - but you cannot or do not want to modify that legacy coce. A variation of the scenario is using two different and incompatible API together.

The idea now is providing an adapter/wrapper class that "transforms" an object of one API into an object of the other API (let's stay with the legacy scenario, then transforming a legacy API object into your own API object).

If you now use composition or private inheritance (composition is preferrable, see the links in the comments of sithereal's answer) is not really the point here. Important is that your adapter/wrapper class incorporates the legacy object in some way that is suitable so that it gets access to whatever it needs from.

The basic idea is that the adapter will kind of emulate being the base class using the functionality the legacy class provides.

Back to the example:

class Rectangle;

The abstract base class. It defines the interface any Rectangle class shall provide (in this case: the draw function, which is pure virtual, i. e. has no implementation). Your own class(es) – lets call it OrdinaryRectangle (no better idea - invent something up yourself...) – will simply inherit from the Rectangle base class and implement the draw function as needed.

Following this example, you will define the same way yyour own interface and provide an implementation for - or you have already. Possibly, you have the finished implementation of your own class, but you might yet have to extract the public interface into a new base class. OK, drifting away...

class LegacyRectangle;

The class coming from the other/legacy API. You might get instances of from your legacy API and feed them back into later. Converting these instances into your own API and back (e. g. by providing appropriate constructors in Rectangle and cast operators) – the alternative – might be too expensive for any reason (runtime overhead, coding effort, ...). So you wrap another class around, pretending to be an instance of your own rectangle implementation, but using the legacy rectangle internally.

class RectangleAdapter : public Rectangle, private LegacyRectangle

OK, example used private inheritance, so I'll follow, in spite of all the discussion around in the other answer...

Inheriting publicly from Rectangle (as does (must!) OrdinaryRectangle, too) is necessary to be able to handle RectanlgeAdapter just as if it was an OrdinaryRectangle (technically incorrect: better: as both inherit from Rectangle, both can be treated as a such - no matter which instance you actually have - in most situations, you won't even know!).

Inheriting privately from LegacyRectangle (or preferrably having an internal member of) provides you the internal instance of your legacy API object.

Constructor; nice, I'll ommit that one to explain... But: you might want to add copy and move constructors for your LegacyRectangle (a little bit dangerous wording, normally, these terms are applied for copying/moving instances of your own class - but you'll see) - this is expecially (only?) interesting if LegacyRectangle provides such constructors itself (in the classic meaning, though):

RectangleAdapter(LegacyRectangle const& lr) : LegacyRectangle(lr) { }
RectangleAdapter(LegacyRectangle&& lr) : LegacyRectangle(std::move(lr)) { }

(Above is incomplete, but should suffice to illustrate!)

virtual void draw()
{
    cout << "RectangleAdapter: draw." << endl;
    oldDraw();
}

Here we are at the core - we implement the interface of our own API by use of the legacy API. In the example, this is totally simple - we just use the oldDraw() function. In reality, this can achieve arbitrary complexity. Just to get a little more of it into: Imagine there was no oldDraw, but instead drawBorder and fillInterior functions. You'd then have to use these inside your own draw function.

This is the part where it can get really difficult. If you have problems there, you need to need to get more concrete, we then need to know the exact szenario(s) and detailed questions to be able to help you... Have a look at how to ask a good question and how to create a mcve before...

Community
  • 1
  • 1
Aconcagua
  • 24,880
  • 4
  • 34
  • 59