godfatherofpolka
godfatherofpolka

Reputation: 1673

Refactoring tightly coupled classes when only one side can be changed

Please accept my apologies in advance for the somewhat long-winded question. This is the minimal self-contained example I could come up with... I'm pretty sure there must be some obvious/nice/neat solution to this problem, but I'm currently not able to see it.

Ok, here's the problem: Imagine the following situation (nb. a compileable version of the code is available at http://goo.gl/dhRNex). Suppose

struct Thing1 {
public:
  void bar(class Implementation1 &i) {
    i.baz();
  }

  // ...various other methods like bar()
};

struct Thing2 {
public:
  void bar(class Implementation2 &i) {
    i.qux();
  }

  // ...various other methods like bar()
};

are given. Unfortunately, these classes are fixed, i.e., can not be changed/refactored.

However, Implementation1 and Implementation2 are changeable. These two classes share a lot of similar code, so it seems natural to put the shared code in a common base class. However, the code is dependent the type of Thing used, but there is no common base class for Thing1 and Thing2, so it seems also natural to use templates. Thus, I came up with the following solution for the base class

template<class T, class S>
struct ImplementationBase {
public:
  S *self;

  void foo() {
    T thing;
    thing.bar(*self);
  }

  // ...lots more shared code like foo()
};

and concrete implementations

struct Implementation1 : public ImplementationBase<class Thing1, class Implementation1> {
public:
  Implementation1() {
    self = this;
  }

  void baz() {
    std::cout << "Qux!" << std::endl;
  }
};

struct Implementation2 : public ImplementationBase<class Thing2, class Implementation2> {
public:
  Implementation2() {
    self = this;
  }

  void qux() {
    std::cout << "Qux!" << std::endl;
  }
};

Ideally, one would use this instead of self in foo, but the problem is that this is of type ImplementationBase<class Thing1, class Implementation1>, but Implementation1 is required. Obviously, the whole thing is quite a mess and the Implementation and Thing classes are too tightly coupled, but I cannot see an easy way out without being able to refactor the Thing classes. So, finally, my questions are:

  1. Is there a better alternative to using the self trick above?
  2. Is there a design that would solve this problem in a better manner? (I have a feeling, there is, but that I'm missing something obvious)

If you have made it this far, thanks a lot for taking the time and reading the whole story and my apologies again for this long-winded question.

Upvotes: 3

Views: 87

Answers (1)

Jay Miller
Jay Miller

Reputation: 2234

You're already using CRTP so you don't need the self at all:

template<class T, class S>
struct ImplementationBase {        
public:
  S* getThis() { return static_cast<S*>(this); }

  void foo() {
    T thing;
    thing.bar(*getThis());
  }

  // ...lots more shared code like foo()
};

Upvotes: 2

Related Questions