4

I have a dumb c++ design question. Is there a way for one class to have the same method names (hence, the same API) of the methods found in several classes?

My current situation is that I have a situation where I have classes

struct A
{
    void foo() { std::cout << "A::foo" << std::endl;}
    void boo() { std::cout << "A::boo" << std::endl;}
};

struct B
{
    void moo() { std::cout << "B::moo" << std::endl;}
    void goo() { std::cout << "A::goo" << std::endl;}
};
.... imagine possibly more

What I really want is another class that acts an interface for those of these functionalities. I might be misinterpreting as the facade design pattern for a simple interface that hides the complexity of instantiating classes above but still use their same interface.

struct C 
{
    void foo() { ... }
    void boo() { ... }
    void moo() { ... }
    void goo() { ... }
};

For small number of methods shown above this is feasible by either declaring structs A and B or passing them in as parameters to struct C and call the methods of A and B in C but this is impracticable if A has 40 methods and B has 30 has methods. Redeclaring 70 methods with the same name in C to call the underlying methods of A and B seemed like a lot of redundancy for no reason if I could do better.

I thought of a second solutions of using a base class

struct base
{
    void foo() { }
    void boo() { }

    void moo() { }
    void goo() { }
};

struct A : public base
{
    void foo() { std::cout << "A::foo" << std::endl;}
    void boo() { std::cout << "A::boo" << std::endl;}
};

struct B : public base
{
    void moo() { std::cout << "B::moo" << std::endl;}
    void goo() { std::cout << "A::goo" << std::endl;}
};

To try and use a shared_ptr that has all the function definitions. e.g

std::shared_ptr<base> l_var;
l_var->foo();
l_var->boo();
l_var->moo();
l_var->goo();

That still doesn't quite give me what I want because half of the methods are defined in struct A while the other half is in struct B.

I was wondering if multiple inheritance would do the trick but in school I heard it's bad practice to do multiple inheritance (debugging is hard?)

Any thoughts or recommendations? Basically it's easier to manage struct A and B (and so on as it's own class for abstraction purposes). But would like the flexibility of somehow calling their methods in some wrapper where this complexity is hidden from the user.

Andre Marin
  • 540
  • 1
  • 6
  • 16
  • 1
    Multiple inheritance? `class C: public A, public B` – 273K May 13 '18 at 03:22
  • Your asking or telling? lol I didn't put it down for class C because it was example of what I wanted to accomplish but wasn't sure how. Or your suggesting to use it? – Andre Marin May 13 '18 at 03:28
  • My question is *It seems multiply class inheritance should pass your requirement. Isn't it?* – 273K May 13 '18 at 03:30
  • 3
    "if A has 40 methods and 30 has methods" then you have a big problem – n. m. could be an AI May 13 '18 at 03:33
  • What is the relationship between a and b? Why must they share an interface? –  May 13 '18 at 03:37
  • @S.M. I mentioned that I think it could, but hesitant to use multiple inheritance in a manner that is considered bad practice (whatever that means) since I myself don't understand when to properly use it. – Andre Marin May 13 '18 at 03:51
  • @n.m. I would love to have a private conversation about a general statement w/o knowing the actual context of the problem if you would so generously give your time. – Andre Marin May 13 '18 at 03:51
  • 1
    In your question multiple inheritance is not a bad solution. – 273K May 13 '18 at 03:53
  • @CarlodelMundo A is a subset of modules, B is another subset of modules. They fall under one bigger parent module that would be a nice interface to call their methods while keeping the fact that I'm managing as two different pieces away from the user. – Andre Marin May 13 '18 at 03:53
  • 1
    you have a class A with something such 40 methods, and class B with something such 30 methods. This is already a code smell. Now you want a class C with 70 methods. This is a bigger code smell. Google for SRP (Single Responsability Principle), it will be helpful in writing more manageable code. – Gian Paolo May 13 '18 at 07:56
  • Sounds like you want to do the opposite of the ISP- interface segregation principle says, that is generally not a good idea. You end up with a big interface which over time creates extra maintenance to keep up to date. – AndersK May 13 '18 at 15:21
  • @GianPaolo, I have a large spec I'm decoding. Each individual field I'm decoding is a method. Struct A and B are different modules of the same spec. Largo number of methods is due to the number of fields to decode. Not sure if that is still breaking SRP. – Andre Marin May 14 '18 at 14:08
  • @user1945925, maybe you are within SRP circle, but it's hard to imagine that you need 70 functions to decode 70 fields. don't they share anything in common? – Gian Paolo May 14 '18 at 18:30

4 Answers4

1

A Bridge Design Pattern will shine here. By decoupling abstraction from its implementation , many derived classes can used these implementations separately.

    struct base {    
    protected:
        struct impl;
        unique_ptr<impl> _impl;
    };

    struct base::impl {
        void foo() {}
        void bar() {}
    };    

    struct A :public base {
        void foo() { _impl->foo(); }
    };

    struct B:public base {
        void foo() { _impl->foo(); }
        void bar() { _impl->bar(); }
    };

Edited ( eg implementation)

#include <memory>
#include <iostream>


using namespace std;

struct base {
    base();
protected:
    struct impl;
    unique_ptr<impl> _impl;
};

struct base::impl {
    void foo() { cout << " foo\n"; }
    void bar() { cout << " bar\n"; }
    void moo() { cout << " moo\n"; }
    void goo() { cout << " goo\n"; }
};

base::base():_impl(new impl()) {}

struct A :public base {
    A():base() { }
    void foo() { _impl->foo(); }
};

struct B:public base {
    B() :base() { }
    void foo() { _impl->foo(); }
    void bar() { _impl->bar(); }
};


struct C :public base {
    C() :base() { }
    void foo() { _impl->foo(); }
    void bar() { _impl->bar(); }
    void moo() { _impl->moo(); }
    void goo() { _impl->goo(); }
};


int main()
{
    B b;
    b.foo();
    C c1;
    c1.foo();
    c1.bar();   
    c1.moo();
    c1.goo();
    return 0;
}
seccpur
  • 4,996
  • 2
  • 13
  • 21
  • I think you might be right that the Bridge design pattern could be used here (although I don't think it resolves the issue of having to redeclare all the methods) but your code is not particularly clear to me. Where is `C` in your code? Can you give an example of how to use this? – Chris Drew May 13 '18 at 05:43
  • @ChrisDrew: Edited to include an example implementation. – seccpur May 13 '18 at 06:10
  • I thought OP wanted the methods on `C` to call the corresponding methods on `A` or `B`. This code doesn't achieve that. The methods on `C` end up calling methods on `base::impl`. – Chris Drew May 13 '18 at 07:06
  • OP tried similar implementation as second solution, please see. If `struct A` is declared public to `struct C`, its methods can similarly used by C. – seccpur May 13 '18 at 07:09
  • OP tried something like this but then found that this didn't work "because half of the methods are defined in struct A while the other half is in struct B". – Chris Drew May 13 '18 at 07:20
1

I think that

Redeclaring 70 methods with the same name in C to call the underlying methods of A and B

is the right path.

It is tempting to use multiple inheritance in cases like this to avoid writing pass-through code but I think that is generally a mistake. Prefer composition over inheritance.

I would question whether your user really wants to deal with one interface with 70 methods but if that's really what you want then I don't see why it is "impractical" to write the code in C:

class C {
    A a;
    B b;
public:
    void foo() { return a.foo(); }
    void boo() { return a.boo(); }
    void moo() { return b.moo(); }
    void goo() { return b.goo(); }
    // ...
};

Live demo.

This has the advantage that you can easily change your mind in the future and replace A and B with something else without changing the interface of C.

You can hide the implementation of C further by using the PIMPL idiom or by splitting C into an abstract base class C and an implementation CImpl.

Chris Drew
  • 14,926
  • 3
  • 34
  • 54
  • What do you mean by pass through code? What about using virtual inheritance as suggested below? It's C++ way of avoiding the diamond problem. – Andre Marin May 14 '18 at 20:43
  • When I say, "pass through code", I mean a member function that does nothing else other than call a member function on another object. virtual inheritance has its place but I wouldn't recommend it here for the reasons I give. Inheritance is for an "is-a" relationship. This sounds like a "is implemented using a" relationship so composition is a better fit. – Chris Drew May 14 '18 at 21:09
  • That's interesting because these methods are just decoding fields in a large spec. Struct A and B are modules for this spec and have their own fields. But both of them together is a complete spec I'm trying to create an API for. But I guess it isn't a "is a" relationship in that I'm not extending functionality of these base classes by the third. It is just a "pass through" as you mentioned. – Andre Marin May 14 '18 at 21:19
0

Use virtual multiple inheritance. The reason why

it's bad practice to do multiple inheritance

is because it directly will lead to ambiguous calls or redundant data, so you can use virtual inheritance to avoid it.

Learn how C++ implement iostream will help a lot, I thought.

con ko
  • 1,368
  • 5
  • 18
0

I second Chris Drew's answer: not only multiple iharitance is bad, any inharitance is bad, compared to composition.

The purpose of the Fascade pattern is to hide complexity. As in, given your classes A and B with 40 and 30 methods, a Fascade would expose about 10 of them - those, needed by the user. Thus is avoided the problem of "if A has 40 methods and 30 has methods" then you have a big problem – n.m.

By the way, I love how you are using struct{} instead of class{public:}. This is quite controversial and the general consensus is it constitutes bad form, but stl does it and I do it.

Back to the question. If really all the 70 methods need to be exposed (!!), I would take a more pythonistic approach:

struct Iface
{
    A _a;
    B _b;
};

If you manage to make the fields const, things get even less bad. And for the third time - you are probably violating SRP with those large classes.

Vorac
  • 8,726
  • 11
  • 58
  • 101
  • Using struct for the example here to avoid more typing. In my actual source I use `class` because I need to hide member variables or private methods. – Andre Marin May 13 '18 at 13:56