7

Here we have TV class and DVD class as example:

class TV
{
public:
    TV();
    virtual ~TV();

    void on() const;
    void off() const;
};

class DVDPlayer
{
public:
    DVDPlayer();
    ~DVDPlayer();

    void SetCD() const;
    void StartPlay() const;
    void Eject() const;
    void PowerOff() const;
};

We create Adapter which helps DVD to "pretends" TV:

class TVStyleAdapter :
    public TV
{
public:
    TVStyleAdapter(DVDPlayer* AdapteeDVD);
    ~TVStyleAdapter();

    void on() const;
    void off() const;

private:
    DVDPlayer* DVD;
};

// override base class funcs:
void TVStyleAdapter::on() const
{
    DVD->SetCD();
    DVD->StartPlay();
}

void TVStyleAdapter::off() const
{
    DVD->Eject();
    DVD->PowerOff();
}

After that I can add "virtual" in the TV(Base) class & override on() off() functions in Adapter class. And it will be works right.

BUT the questions is:

  1. Can we somehow create an Adapter without ANY changes (e.g. adding "virtual") in a base ('TV') class?
  2. Is it possible in this situation not violate the Open Closed principle?
  3. If I assume using an adapter in my program in the future, should I do the virtual methods in my classes in advance?
Christophe
  • 81,699
Anton
  • 91

3 Answers3

11

1. Yes, but no:

You could compile any code with a TVStyleAdapter instead of a TV, and it would work without a change in TV. So in a certain sense, the answer could be yes.

But this compile-time reuse of the interface would also work if TVStyleAdapter would be unrelated to TV. Unfortunately, it's no dynamic: you have to know the real object type at compile time, and you couldn't hope for a dynamic, object dependent behavior at runtime.

If you want polymorphism, i.e. use a pointer or a reference to a TV base class and expect the correct behavior to happen depending on the real type of the object you must have the virtual in the base class.

2. Yes, but who broke the principle ?

The question is not so much whether have to change the base change the base class, but rather why the base class was not already virtual.

You think that you break the close principle because you have to add the virtual in the base class. But in reality the designer of the base class broke the open principle in the first line.

3. Design for polymorphism from the beginning

If you want a class to be polymorphic, that is behave differently at rutime depending on its real type, you have to design it accordingly from the start. SO yes, virtual in advance (and consequently a virtual destructor, even if it remains empty)

Robert Harvey
  • 200,592
Christophe
  • 81,699
9

The classic adapter pattern, utilizing inheritance, simply does not work if the base class is not prepared for it. And yes, you are correct, without TV having virtual methods, it does not conform to the OCP. The OCP requires classes to provide required "extension points" or parameters beforehand, see this former Q&A about the OCP and what it means to class design.

In C++, however, there is the possibility to implement the adapter pattern using templates instead, by making the kind of TV class it uses a template parameter:

    template <class T> void myFunction(T tv)
    {
         tv->on();
    }

This will allow you to write code which either takes the original TV class or the TVStyleAdapter as a parameter, without making the onand off methods virtual. Note this makes it a compile time decision which of the two classes will be used, whilst the inheritance solution will allow to switch at run time.

If one needs a run-time decision together with the template solution, the decision has to be made by the code which calls either myFunction<TV> or myFunction<TVStyleAdapter>. Or better use @JackAidley's solution, which is a good example of "composition over inheritance".

Doc Brown
  • 218,378
3

Yes, you can.

The solution is simple: wrap TV too, and inherit both your TV and DVD adaptors from a common abstract base class. You can then use this abstract base class in place of everywhere you were using TV and get run-time flexibility.

class TVBase {
    public:
    virtual ~TVBase() {}

    virtual void on() const = 0;
    virtual void off() const = 0;
};

class TVAdaptor : public TVBase {
    public:
    // Obviously you need a constructor!
    virtual void on() const { tv->on(); }
    virtual void off() const { tv->off(); }

    private:
    TV* tv;
};

class DVDAdaptor : public TVBase {
    // Insert contents of your TV style adaptor here...
};

I've omitted various implementation details in the above but, hopefully, you can see what I've done. You could even implement it as a template with automatic pass through for on and off and then specialise for classes like DVD with a different interface.