I have recently encountered this problematic method (minimal reproducible sample in C++ but this question aims to be language agnostic past the syntax):
void MyObject::twice_bind_cycle() {
_bind1();
_bind2();
_activate();
_unbind2();
_unbind1();
}
This entire method interacts with an API as do the binding/unbinding methods themselves. It is OpenGL, but since this question is more about design decisions, I think it belongs on here at Software Engineering SE. I have generalized the problem for those unfamiliar with its usage.
I find there is tight coupling between the pairs of binding and unbinding methods. In order to perform activate()
correctly, _bind1()
must run before _bind2()
and _unbind2()
before _unbind1()
, which forms an implicit interdependence of state within twice_bind_cycle()
between the binds and unbinds. This would be acceptable in many cases---in fact it appears I could merge the functions if I were not reusing them elsewhere in say, bind_cycle_1()
and bind_cycle_2()
.
A problem arises when I want to move the inner _bind2()
and _unbind2()
functions from the MyObject class and into their own. Their implementations, which aren't shown here for generalization, strongly suggest to me they ought to belong to their own object. They do not rely on any object state, but they do control a critical part of the API and their semantics with MyObject are becoming increasingly incompatible as the project grows. Further I would like to write several new methods that call _bind2()
and _unbind2()
that should not belong to MyObject. Finally, I predict several issues that could occur if I do not decouple them soon and continue work as normal.
I am now concerned about how the twice_bind_cycle()
is supposed to be re-implemented and still function correctly in the provided order, while attempting to preserve its approach to encapsulation.
Some approaches I have considered:
Create the other object as proposed and use an intermediate class that contains both objects via composition. Move
twice_bind_cycle()
to this class. Okay, butactivate()
requires access to private variables insideMyObject
and the binding/unbinding methods are private. I am willing to break encapsulation here and provide the getters necessary foractivate()
because I find the attributes it requires to be insignificant to other objects. But making the binding functions public is a no-go because they are a major part of the objects' internal implementation.Use a command pattern. From the brief research I made, it appears like making the methods public just with extra steps. I have little experience with this pattern so I understand this is a limited view.
Make the binding/unbinding methods protected and inherit from both of the new classes (C++). Inheritance has its own problems and especially multiple inheritance. Although I am open to suggestions, I'd prefer to avoid that route in favor of composition. I am fine with interfaces, though.
Use the
friend
keyword (C++ only AFAIK). Make an intermediate class and make it a friend in the MyObject and MyOtherObject classes. However, there is still transitive coupling via the intermediary and if I wanted to use either object independently, that association with it will be there.
What should I do? Are there any other options? It seems inevitable that a compromise must be made somewhere. Also, am I being pedantic about the level of encapsulation and coupling within my program? Do I have enough reason to be?
Thanks
bond1(bond2(activateMe))
?