Skip to content

Improve Documentation for internal facades #1413

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

Merged
merged 1 commit into from
Jun 22, 2023

Conversation

ChristianTackeGSI
Copy link
Member

I learned about the Facade pattern from @dennisklein and he also suggested to improve the documentation about this.

For the current cases:

From the page:

  • improve the readability and usability of a software library by masking interaction with more complex components behind a single (and often simplified) API

simplified: Yes
Trying to improve the readability (of the callers): Yes

  • provide a context-specific interface to more generic functionality (complete with context-specific input validation)

Yes!
Well, not everything is validated. But see the \todo.

  • serve as a launching point for a broader refactor

Maybe.

Note: The Facade pattern talks about objects, while this here is more applied to member functions.


Checklist:

Comment on lines 233 to 241
void FairRun::FillEventHeader()
{
if (fSource) {
fSource->FillEventHeader(fEvtHeader);
} else {
LOG(warning) << "FairRun::FillEventHeader: No Source!";
}
}
Copy link
Member

@dennisklein dennisklein Jun 22, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First, a pure stylistic suggestion:

Suggested change
void FairRun::FillEventHeader()
{
if (fSource) {
fSource->FillEventHeader(fEvtHeader);
} else {
LOG(warning) << "FairRun::FillEventHeader: No Source!";
}
}
void FairRun::FillEventHeader()
{
if (!fSource) {
LOG(warning) << "FairRun::FillEventHeader: No Source!";
return;
}
fSource->FillEventHeader(fEvtHeader);
}

The idea is to not use an explicit block for the else case by default to reduce the amount of blocks (indented code). Then there is a choice what to put inside the remaining block - here I followed the idea to keep the "good"/"healthy" code path without blocks and the "exceptional"/"special cases" in the conditional block. At least to me, this style improves readability. What do you think?


The next comment may very well be out of scope of this PR, but I make it anyways, while at it; if you like it, we can pick it up later:

Using logs to communicate program logic errors feels off. I would like to propose to promote the warning into a "precondition check". There are two standard approaches to do this that come to mind:

Option A: throw

void FairRun::FillEventHeader()
{
    if (!fSource) {
        throw std::runtime_error("fSource not set"); // should probably be a different exception type
    }

    fSource->FillEventHeader(fEvtHeader);
}

This could fit, if FairRoot in general wants to make much more use of exceptions.

Option B: assert

void FairRun::FillEventHeader()
{
    assert(fSource);

    fSource->FillEventHeader(fEvtHeader);
}

This would be the less invasive version, but will mostly be similar to a comment as people usually do not work with debug builds at all. At the end, this may also be subject to a project-wide style discussion on how to express function preconditions.

Copy link
Member

@dennisklein dennisklein Jun 22, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For reference, the C++ Core Guideline recommends adopting Expect() (and Ensure()), see I.5, I.6, I.7, and I.8

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for reminding me about reducing indented blocks! (It's actually one reason, why I like 80 column rules: It forces people to avoid too much indentation.)

And I like the style of "check things and exit/return early".


About pre-conditions, etc:

  • I don't know, whether fSource not being set is actually a valid scenario. I doubt it, but I am really not sure. So before going to LOG(error) or throw I wanted to increase awareness a bit.
  • Yes, I would prefer exceptions for this type of error. People can catch them, handle them, etc. Yes, we probably should discuss this in a broader community.
  • I haven't looked deeper into Expects, but this sounds like a nice thing!

Copy link
Member

@dennisklein dennisklein Jun 22, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know, whether fSource not being set is actually a valid scenario. I doubt it,

This would support that it should be understood as a classic pre-condition. I was looking at it from this angle: What can the user do to get rid of this warning?! Probably mostly by a code change. Which is a design time activity, so I would want to consider design time checks/hints. Since we also interpret everything optionally, it becomes more complex what/when design time is and which type of check is the best. but anyways, out of scope of this PR. LGTM

Copy link

@YanzhaoW YanzhaoW Jun 26, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have two suggestions:

  1. Always prefer exceptions to abort
    Using exception could lead to stack unwinding, releasing memories allocated in heap before the operating system kills the program. On the other hand, abort (or other functions that calls it in the end, such as assert and std::terminate) doesn't do any stack unwinding and leave the memory cleaning to the operating system. Some operating systems do not do the cleaning job and there could be memory leak. Of course there are many situations when we have no choice but to call abort.

  2. Include MS/GSL library into FairSoft?
    I don't know when FairRoot will move to C++20 or even C++23. But some features in GSL are really useful. Contract (expect and ensure) is one of them. gsl::span, gsl::not_null, Range are also pretty useful.

dennisklein
dennisklein previously approved these changes Jun 22, 2023
Copy link
Member

@dennisklein dennisklein left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I leave the merging to you, in case you want to apply the suggested style changes.

@dennisklein dennisklein merged commit 6031156 into FairRootGroup:dev Jun 22, 2023
@ChristianTackeGSI ChristianTackeGSI deleted the pr/formatting branch June 23, 2023 00:52
@YanzhaoW
Copy link

YanzhaoW commented Jun 26, 2023

Hi, :D

This follows the discussion from PR #1254

a facade is an object that serves as a front-facing interface masking more complex underlying or structural code.

To be honest, I don't think FillEventHeader is a facade design. As in the quote, a facade must serve as a "front-facing" interface, which means the interface must be open to the public user. However, the member FillEventHeader is a protected and therefore not entirely "front-facing".

improve the readability and usability of a software library by masking interaction with more complex components behind a single (and often simplified) API

In case of FairRoot developers who need to design a derived class from FairRun, FillEventHeader could be viewed as a "front-facing" interface. But there is another problem: there already exists another "front-facing" interface, called GetSource(), which "front-end" developers can also use to fill the event header. Therefore, the complex components are not longer "behind a single API".

The signature of this function is also concerning:

void FillEventHeader();

What is the purpose of this "front-facing" interface that takes nothing and outputs nothing? If everything the function does is just to fill an internal event header into an internal file source, why should a "front-end" user be concerned? Generally speaking, if the function doesn't require anything, it should be closed (declared as private) and executed internally.

@ChristianTackeGSI
Copy link
Member Author

What is the purpose of this "front-facing" interface that takes nothing and outputs nothing? […] if the function doesn't require anything, it should be closed (declared as private) and executed internally.

I don't understand this, really. So you would say, that std::vector<>::clear() should be a private member function of std::vector and be executed internally in the "right" places?

Anyway: Some of our "facades" (maybe let's call them "convenience helpers"?) can be considered part of a bigger refactoring (which also is listed as a point on the facade wikipedia page). Having very concise calls in the main control flow makes it a lot easier to spot refactoring options. Maybe we learn that ReadEvent is always followed by FillEventHeader()? Then you're right and we should put the FillEventHeader into ReadEvent. And as this is all purely internal to FairRoot, it does not concern any users ("customers") of FairRoot.

@YanzhaoW
Copy link

I don't understand this, really. So you would say, that std::vector<>::clear() should be a private member function of std::vector and be executed internally in the "right" places?

Sorry, I was too general. Yes, you are right. User should be concerned about functions like reset() or clear(), which do change the internal data.

For me, I also kind of think GetSource()->FillEventHeader(eventHeader) contains more information and more readable than FillEventHeader(). Both are one line of code. But anyway, let's not waste more time on this subtle thing :D. About my PR #1254, should I change it back to FillEventHeader()?

@ChristianTackeGSI
Copy link
Member Author

About my PR #1254, should I change it back to FillEventHeader()?

Yes. Please!

I think, it will also reduce the size of your patchset (because you like don't need to change so many places).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants