1

I am looking for advice on the most elegant and secure way to decouple two C++ classes that maintain collections of pointers to each other's types.

I implemented it recently using a common base class for polymorphism and was told that it was an unsatisfactory solution. I am eager to learn other ways this can be achieved.

Thanks in advance...

I have added a simplified version of the class definitions below. I am aware that the SalesTeam class is not decoupled from SalesPerson here.

// Global Vectors
vector<Customer *> v_Customer;          
vector<SalesPerson *> v_SalesPerson;  
vector<SalesTeam *> v_SalesTeam;


class Person {    // Base Class
};

class Customer: public Person {
private:
    const Person *contact;         // This is the SalesPerson that serves the Customer
public:
    Customer(const int aBirthYear);
    virtual ~Customer() {}
};

class SalesPerson: public Person {
private:
    vector<Person *> v_Client;  // These are the customers that the SalesPerson serves
public:
    SalesPerson();
    virtual ~SalesPerson(){};
};

class SalesTeam {
private:
    vector<SalesPerson *> v_TeamMember;  // These are the sales people in the SalesTeam
public:
    SalesTeam();
};
jmstoker
  • 3,315
  • 22
  • 36
hally9k
  • 2,423
  • 2
  • 25
  • 47
  • 3
    We would need at lest a minimalistic example of the classes to offer any guidance. – rerun Aug 29 '13 at 02:06
  • 1
    Any idea what was unsatisfactory about it? – Vaughn Cato Aug 29 '13 at 02:22
  • Are you sure you don't want `Customer`s as the clients of a `SalesPerson` ? If not, that class seems somewhat useless (`Customer` I mean). Edit: ok, I see them in their own global container. nm. – WhozCraig Aug 29 '13 at 02:23
  • @WhozCraig: Customer class would, in the real code, contain vital info. The Person class is supposed to be used to allow the SalesPerson class to maintain a list of pointers to Customers without having to know about Customer.h. – hally9k Aug 29 '13 at 02:26
  • 1
    Use a separate data structure like a `std::map` to maintain the relationship. – andre Aug 29 '13 at 02:26
  • @Vaughn Cato: It was vague but the main point was 'raw pointers' and memory leaks, Im assuming the memory leaks were in the further complexity of my implementation and I left myself open to them by using raw pointers. Is there a better and more secure way to do this in C++? – hally9k Aug 29 '13 at 02:29
  • @andre: that sounds like a good solution, I will study that option, Thanks. – hally9k Aug 29 '13 at 02:31
  • I was told 'the person base class is not a successful idea' and this was what lead me to thinking there must be a better way to do this. – hally9k Aug 29 '13 at 02:32
  • 1
    @Hal9K It just seems odd that a sales person should know "nothing" about their customers except that they're "Person"s, but thats a design thing by you, so ok. Anyway, answering your question posted to Andre, `std::shared_ptr`, which tuck nicely into standard containers. – WhozCraig Aug 29 '13 at 02:33
  • the base class seems totally unnecessary. forward declarations are a thing. SalesPerson does not have to include Customer.h, etc. just forward-declare class Customer, then you can point to it without a definition. – underscore_d May 07 '21 at 18:47

3 Answers3

3

can you use mediator pattern to handle this decouple issue? code example:

class Mediator
{
    private:
        //store your relationship in this class
}

class SalesPerson: public Person {
private:
    Mediator mediator;
public:
    SalesPerson();
    virtual ~SalesPerson(){};
};

class SalesTeam {
private:
    Mediator mediator;
public:
    SalesTeam();
};
john zhao
  • 986
  • 1
  • 6
  • 8
  • This seems like a viable option but can't help but feel that this would be slightly over engineering such a simple problem. – hally9k Aug 29 '13 at 02:36
2

The unsatisfactory part about your first solution is that the Customer and SalesPerson classes contain references to each other, which gets really hairy to manage. I think the "best" solution largely depends on how you intend on customers and sales people interacting with each other. From my assumptions about sales people and customers, I'm assuming that customers don't actually require the sales person and vice versa, instead their relationship would probably be defined in a third class ... such as a transaction.

class Transaction
{
  private:
    Customer *customer;
    SalesPerson *employee;
}

There's hundreds, if not thousands, of design patterns but there is no universal pattern for every situation. I recommend drawing your objects on paper and connecting their references with arrows; if you draw a bi-directional arrow ( such as your Customer and SalesPerson relationships ) then stop and re-evaluate. Once your diagram is complete, you can use the arrows to visualize your interfaces for each object to understand how everything fits together.

Bryan Chacosky
  • 248
  • 2
  • 6
0

You can eliminate the person class by using forward declaration.

// Global Vectors
vector<Customer *> v_Customer;          
vector<SalesPerson *> v_SalesPerson;  
vector<SalesTeam *> v_SalesTeam;

class SalesPerson; //<-- forward declaration for contact

class Customer {
    const SalesPerson *contact;
public:
    Customer(const int aBirthYear);
    virtual ~Customer() {}
};

class SalesPerson {
    vector<Customer *> v_Client;
public:
    SalesPerson();
    virtual ~SalesPerson(){};
};

class SalesTeam {
    vector<SalesPerson *> v_TeamMember;
public:
    SalesTeam();
};
andre
  • 7,018
  • 4
  • 43
  • 75
  • I am aware of forward declaration. The whole point of the Person class was to allow the Customer class and the SalesPerson class to be decoupled and modular. They don't have to #include each other and are therefore reusable without needing each other. Am i misunderstanding the decoupling approach? – hally9k Aug 29 '13 at 06:42
  • @Hal9K: I don't see the advantage of that decoupling. Is it possible for the `SalesPerson::v_Clients` to be something other than `Customers`? If not, you are just reducing the type safety by saying it is a `Person` instead of a `Customer`. – Vaughn Cato Aug 29 '13 at 13:15
  • This is a hugely simplified version of a wider system. The advantage is code reusability and modularity, Salesperson can exist without Customer if reused in a different system. But I agree the type safety is the compromise. Thanks you for all your comments I appreciate the time you have taken. – hally9k Aug 30 '13 at 01:26