0

I've been learning about proxy classes in c++. I really don't see the advantages of using proxy classes, as they're not any better at concealing information than private members are. What advantages and/or disadvantages do proxy classes offer compared to normal private members?

Note: I am using the "proxy" definition found here.

  • Proxy classes for doing what? I don't see how a private member can be equivalent to a vector<bool>::iterator and its reference type. – Nicol Bolas Dec 5 '18 at 0:51
  • 1
    I am not aware of any such concept among the C++ idioms. Can you provide a link or two discussing this concept? For example, this SO question discusses proxy classes, which have nothing to do with private data specifically. – Nicol Bolas Dec 5 '18 at 0:58
  • 1
    see What is the problem with "Pros and Cons"? – gnat Dec 5 '18 at 4:58
  • 1
    That definition you linked to, even though it has 4 upvotes, doesn't make any sense to me. – Rik D Dec 5 '18 at 7:41
  • 1
    Agreed. The answer linked don't even describe what proxy pattern is and what problem it solves. It's a very vague and imprecise definition. The proxy pattern as. was described by GoF. has a very different purpose than mere "hide private data". – Laiv Dec 5 '18 at 8:42
5

Important preliminary remarks

First of all, you shouldn't choose a design pattern in a catalogue, because it is trendy or by looking at advantages and inconveniences. You should choose a design pattern if it corresponds to a problem you have and fits in your design.

Also, patterns do not have only one sentence definition. Patterns are defined by a combination of an intent (what's the purpose) and a description of their structure (how the purpose is fulfilled).

The proxy

This being said, what you describe, is a pattern aiming at changing an object's interface. This would rather be something like an adapter (although it's not a close match here either).

Some other posts in the linked question suggest to use the proxy pattern to add operators. But adding new responsibilities is the aim of the decorator pattern.

The proxy pattern according to GoF has the intent of providing a placeholder object (with the same interface), in order to control access to the original object.

So if you have no special need, there is no advantage for this pattern.

However, there are a couple of scenarios where proxies are really useful. For example:

  • if your objects are managed on a remote server, you could have a local proxy that forwards the operations via a network interface to the remote object.
  • if your objects are resource consuming and you have them most of their time stored on disk, you can use the proxy to act as placeholder. The proxy would then deserialize the object when it's needed and forward it the operations (e.g. cache).
  • if you want to add access control, for example make sure that an object is accessed read-only (by replacing any changing operations by a stub) or making user-based access control before really doing the operations (e.g. if the user is authorised, do the operation, if not, throw an access control exception. with the advantage of separation of concerns between the object's behaviour and the access control)
3

A note on terminology:

What you are talking about isn't called a proxy. The most common name used for it is "pImpl", being a shortening of "Pointer to Implementation". It is a pattern particular to the C++ compilation model.

The reason that you would use a pImpl is to maintain binary compatibility with your classes consumers, even in the face of changing implementation.

As a sketch

Foo.h:

class FooImpl; // Forward declaration, FooImpl is incomplete

class Foo {
public:
    ~Foo(); // Need to specify destructor, as the default is ill formed due to the incompleteness of FooImpl
    void bar(int);
    // etc
private:
    std::unique_ptr<FooImpl> pImpl;
};

FooUser.cpp

#include <Foo.h>

void DoStuff()
{
    Foo foo;
    foo.bar(0);
}

Foo.cpp

#include <Foo.h>
#include <Bar.h>
#include <Baz.h>
#include <Quux.h>

class FooImpl
{
    Bar bar;
    Baz baz;
    Quux quux;
    void do_bar(int) {}
};

Foo::~Foo() {} // Now we have the complete definition of FooImpl, this is well formed

void Foo::bar(int i) { pImpl->do_bar(i); }

FooUser.cpp is completely isolated from the implementation of Foo. Particularly, no #includes that Foo needs to define it's members are visible to FooUser.cpp. Any changes to Foo.cpp don't force FooUser.cpp to be re-compiled.

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.