-
Notifications
You must be signed in to change notification settings - Fork 343
[Framework] Apply template method design pattern to BaseMapping #5466
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
|
[ci-build][with-all-tests] |
|
[ci-depends-on] detected during build #2. To unlock the merge button, you must
|
| /// | ||
| /// If the Mapping can be represented as a matrix J, this method computes | ||
| /// $ out = J in $ | ||
| void doApply (const MechanicalParams* mparams, MultiVecCoordId outPos, ConstMultiVecCoordId inPos ) override; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| void doApply (const MechanicalParams* mparams, MultiVecCoordId outPos, ConstMultiVecCoordId inPos ) override; | |
| void doApply (const MechanicalParams* mparams, MultiVecCoordId outPos, ConstMultiVecCoordId inPos ) final; |
?
|
What a massive work. I'm surprised by the move from the pure virtual BaseMapping's method to virtual final. To me the purpose of declaring a method virtual with final keyword prevent further re-implementation... and thus is equivalent to a non virtual method. About the overload of apply()... BaseMapping
{
public:
virtual void apply (const MechanicalParams* mparams = mechanicalparams::defaultInstance()) = 0;
};
Mapping : public BaseMapping
{
public:
void apply (const MechanicalParams* mparams = mechanicalparams::defaultInstance()) override;
virtual void apply (const MechanicalParams* mparams, OutDataVecCoord& out, const InDataVecCoord& in) = 0;
};
AMapping : public Mapping
{
public:
void apply (const MechanicalParams* mparams = mechanicalparams::defaultInstance()) override;
void apply (const MechanicalParams* mparams, OutDataVecCoord& out, const InDataVecCoord& in) override;
};After applicaition of the template method pattern I should looks close to: BaseMapping
{
public:
// this method is public and non virtual, because we don't want subclass to "tune" (or screw) its internal logical.
void apply (const MechanicalParams* mparams = mechanicalparams::defaultInstance()) {
// ... common code here....
// calls the delegate only when pre-requierement/context is fullfill
doApply(...);
// ... other common code here
}
// there is a choice between private and protected.
// the choice depend if the method can be used in isolation in the child classes or if it is not supposed to.
private:
// child class must implement something
virtual void doApply(const MechanicalParams* mparams) void = 0;
};
Mapping : public BaseMapping
{
private:
void doApply(const MechanicalParams* mparams) override {
// the actual implementation providing the service to the "parent class"
// on this case, apply is actually dispatching to a new level of delegates
// ... common code should be here ...
doApplyOnCoordinates()
// ... common code should be here ...
}
// to de-ambiguate the second apply() method introduced in Mapping.h, give it a second name.
void doApplyOnCoordinates (const MechanicalParams* mparams, OutDataVecCoord& out, const InDataVecCoord& in) = 0;
};
AMapping : public Mapping
{
private:
void doApplyOnCoordinates (const MechanicalParams* mparams, OutDataVecCoord& out, const InDataVecCoord& in) final {
/// implements things here.
}
};The chaining of delegates with different names makes very explicit what is expected from each method. Because each use of TMP (template method pattern) can be seen as a contract between the base class and its child on the specific, and sole, purpose of the delegated method as well as its context of use. |
|
It is not equivalent, otherwise the keyword 'final' wouldn't exist... Actually, doing this we make sure no one shadows the method in a child class. You are right that without the virtual keyword, polymorphism will not apply, but it allows some bad coding practice that could lead to very hard to track runtime errors. In my opinion we should keep the final to make sure no one ever overrides it, this will avoid some tricky situations and changes nothing really in the API. Your suggestion seems good to me, nevertheless, we wanted to keep this first salve of PRs only at the first level of inheritance. Methods introduced at the second level will be treated in a second stage (maybe a new sprint ?). We will keep your comment in mind then ! |
Changed:
[ci-depends-on https://github.com/sofa-framework/PluginExample/pull/17]
[ci-depends-on https://github.com/SofaDefrost/ModelOrderReduction/pull/159]
[ci-depends-on https://github.com/sofa-framework/BeamAdapter/pull/189]
ℹ️ Need to be discussed
Some methods (such as apply) have been overloaded in child class, now we've got this stange pattern :
Mother::apply -> Mother::doApply(pure virtual) --> Child::apply
Just because we used an oveload
By submitting this pull request, I acknowledge that
I have read, understand, and agree SOFA Developer Certificate of Origin (DCO).
Reviewers will merge this pull-request only if