views:

65

answers:

3

Let's say I'm writing an interface for a class A that keeps a list of objects of type B, and I want to be able to manipulate that list through class A's interface. I could put add_B, remove_B, etc. methods to A's interface, but that's a lot of code duplication (this situation occurs in many classes in my programme), so I'd rather return a reference to the list itself. That, however, would break encapsulation.
Is there a standard way to handle this situation?

A: 

I had to face this very same situation in my current development. I just passed a reference to the list. I don't think there is any other option than the two you mentioned. Perhaps you should make methods to manipulate the whole list (add_b, remove_b) and a method to get a reference to a single item in the list for item manipulation.

BTW: If you are not using vectors to handle your list of object B, then I suggest you do.

Alexander Rafferty
std::list would be good, too. Seems most people jump to vectors :)
Merlyn Morgan-Graham
@Merlyn, linked lists are usually worse in most situations, because most of the time you handle a list of a few, at most a dozen elements, so insertion/removal speed of vectors are a non-issue, and they have far better cache hit rate too, unlike linked lists.
Alex B
You're right about using std::vector, thanks.
yaront
+1  A: 

Not enough information to give a detailed answer, but taking the question at face value, I think AddX/RemoveX interface is fine if you want to restrict list operations or need to maintain some other constraint when adding or removing elements.

However, if you frequently need to do complex manipulations on another class' list, maybe it doesn't belong there?

Or use a kind of a mixin class if you just want to remove repetition:

template<class T>
class ListOf {
public:
    // Public interface only allows adding or removal.
    typedef boost::shared_ptr<T> Ptr;
    void Add(const Ptr &elem) {
        elems_.insert(elem);
    }
    void Remove(const Ptr &elem) {
        elems_.erase(elem);
    }
protected:
    // Subclass can access container reference.
    typedef std::set<Ptr> ElemSet;
    ElemSet& Elements() {
        return elems_;
    }
    const ElemSet& Elements() const {
        return elems_;
    }
private:
    ElemSet elems_;
};

class Foo : public Bar, public ListOf<Baz> {
    ...
};
Alex B
+1  A: 

The code below is based on the proxy design pattern. It preserves encapsulation and avoids a bulky interface in 'A' by delegating the interface to the 'proxy' object.

Also note how the typedef allows freedom of changing 'list' to 'vector' or anything else.

struct B{};

struct A{
    struct containerproxy{
        void Add(B *pb);
        void Remove(B *pb);
    };
    friend struct containerproxy;
    containerproxy &GetProxy(){return mlprx;}
    typedef containerproxy intf;
    ~A(){
        // Code to delete list elements and cleanup the list
    }
private:
    containerproxy mlprx;
    list<B*> mlp;
};

void A::containerproxy::Add(B *pb){ /*code to add pb to mlp*/}
void A::containerproxy::Remove(B *pb){/*code to remove pb from mlp*/}

int main(){
    A a;
    A::intf &r = a.GetProxy();

    B *p = new B;
    r.Add(p);
}
Chubsdad
I like this solution. I'd implement the proxy as an independent class (since I'll be using it in many places), though. Thanks.
yaront