1

I know there can't exist a 'semi-virtual' method, however, I've run into a design related issue.

Let's say I have a base class called WebPage. This class has a method called UpdatePage(). But because this method exists within an abstract WebPage object, the UpdatePage() method is virtual, and should be implemented by a user who is deriving a concrete class from WebPage.

But let's say that when the UpdatePage() method is called, it would be ideal that it timestamps some class data member regarding the last update time.

I'm in a position where I want some default implementation from a method (i.e. do a timestamp), but I also want the implementation to be custom to the concrete class derived from the base class WebPage.

I know I could come up with some technique to solve this issue. For example, I could make UpdatePage() non-virtual, and have it contain two methods: a timeStamp() method which is non-virtual, and a updateImplementation() method which is pure-virtual. This way when a user calls UpdatePage(), there would exist default and custom behavior.

But again, if there exists some design pattern/rule for this, I'd like to not reinvent the wheel.

Thanks!

Izzo
  • 4,461
  • 13
  • 45
  • 82
  • 3
    Make `UpdatePage` public and non-virtual, and make it call another method, say `DoUpdatePage`, that's protected and virtual. Derived classes would implement the latter; the former can do any required common tasks before and/or after. – Igor Tandetnik May 29 '16 at 23:55
  • 2
    Are you looking [for the Template Method pattern](https://en.wikipedia.org/wiki/Template_method_pattern)? – PaulMcKenzie May 29 '16 at 23:57
  • 1
    It sounds like you're discussing the [NVI idiom](http://www.gotw.ca/publications/mill18.htm), when you mention a non-virtual public function which calls a virtual function and may perform other operations before or after the virtual function call. – James Adkison May 30 '16 at 00:23
  • One thing I've seen some in some APIs do is just have a documented requirement that the derived class must call the super class's implementation. i.e. `WebPage::UpdatePage()` is virtual but has a default implementation, and its documented that overriding functions *must* call the default implementation before doing any other work (and this is only enforced via documentation and contract expectations). It's an option, though not my favorite. – Cornstalks May 30 '16 at 01:09
  • Like this? http://stackoverflow.com/questions/14712617/crtp-dispatch-in-c11 – Jerry Jeremiah May 30 '16 at 01:19

1 Answers1

3

What you mentioned in your question is the Template Method pattern which is suitable to solve your problem.

In brief, your UpdatePage() is going to provide a "template" of flow, and let the derived class provide the missing part of the template:

class WebPage {
public:
    virtual UpdatePage() {  // optional for the virtual
        // Update the timestamp

        // Call the logic provided by derived class
        DoUpdatePage();

        // Some other logic afterwards if you need
    };

protected:
    virtual DoUpdatePage() = 0;  // you may also provide a default impl too
};

(Wish the syntax is correct, have been quite some time haven't touch C++. But the idea should be clear though)


Another possible pattern, depending on your design, is Decorator.

In short, instead of making use of the derived class directly, you decorate it by a decorator that provides the timestamp logic

e.g.

class WebPage {
public:
    virtual void UpdatePage() = 0;
};

class TimeStampWebPageDecorator : public WebPage {
public:
    TimeStampWebPageDecorator(WebPage* webpage) : impl(webpage) {
    }

    virtual void UpdatePage() {
        // logic for the timestamp stuff

        impl->UpdatePage();

        // some other logic after calling impl
    }

private:
    WebPage * impl;
}

So, if you have a WebPage impl to invoke, you may decorate it with TimeStampWebPageDecorator. E.g.

FooWebPage fooWebPage{};

// Instead of invoking fooWebPage directly like this.
// WebPage& webpage = fooWebPage;
// webpage.UpdatePage()

// Decorate it first
WebPage& webpage = TimeStampWebPageDecorator{&fooWebPage};
webpage.UpdatePage()
Adrian Shum
  • 38,812
  • 10
  • 83
  • 131