Message-formatting code with pluralization, revised to be more functionalTrying to find a good design for reading in values of different types from a fileMutable String ClassFormatting phone number without knowing localeVariant class (named any like in boost::any)Giveth me thy easier user input in C++ - follow upPattern for writing a generic string transformation functionBinary test file generator with checksumC++ Parsing with chain of responsibilityGeneric Skip list implementation in C++ Version 3Object-oriented calculator
Masking using two sliders on Google Earth Engine
How to choose using Collection<Id> rather than Collection<String>, or the opposite?
Solve equation using Mathematica
Can you continue the movement of a Bonus Action Dash granted by Expeditious Retreat if your Concentration is broken mid-move?
How can a class have multiple methods without breaking the single responsibility principle
How would a lunar colony attack Earth?
Is there a word or phrase that means 'works but not for the reason we expect it to'?
May a hotel provide accommodation for fewer people than booked?
Should students have access to past exams or an exam bank?
How to innovate in OR
Unknown indication below upper stave
How can Paypal know my card is being used in another account?
What is my clock telling me to do?
Why would an invisible personal shield be necessary?
What force enables us to walk? Friction or normal reaction?
How can I solve this sudoku?
Typesetting numbers above, below, left, and right of a symbol
How can flights operated by the same company have such different prices when marketed by another?
Why didn't Stark and Nebula use jump points with their ship to go back to Earth?
Easy way to get process from window
How did astronauts using rovers tell direction without compasses on the Moon?
why there is no “error” term in survival analysis?
Why are prop blades not shaped like household fan blades?
Can machine learning learn a function like finding maximum from a list?
Message-formatting code with pluralization, revised to be more functional
Trying to find a good design for reading in values of different types from a fileMutable String ClassFormatting phone number without knowing localeVariant class (named any like in boost::any)Giveth me thy easier user input in C++ - follow upPattern for writing a generic string transformation functionBinary test file generator with checksumC++ Parsing with chain of responsibilityGeneric Skip list implementation in C++ Version 3Object-oriented calculator
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty margin-bottom:0;
$begingroup$
I'm a C++ dev, and I've recently started working my way through Clean Code*. Whenever I encounter an example I think I could improve, I try to re-implement it in C++.
On pp. 28-29 there is an example with a message building class, which looks like this when implemented in C++ (includes and namespaces omitted):
.h
class GuessStatisticsMessage
public:
GuessStatisticsMessage() = default;
~GuessStatisticsMessage() = default;
std::string make(char candidate, int count);
private:
void createPluralDependentMessageParts(int count);
void createThereAreManyLetters(int count);
void createThereIsOneLetter();
void createThereAreNoLetters();
std::string number;
std::string verb;
std::string pluralModifier;
;
.cpp
std::string GuessStatisticsMessage::make(char candidate, int count)
createPluralDependentMessageParts(count);
return fmt::format("There ", verb, number, candidate, pluralModifier);
void GuessStatisticsMessage::createPluralDependentMessageParts(int count)
if (count == 0)
createThereAreNoLetters();
else if (count == 1)
createThereIsOneLetter();
else
createThereAreManyLetters(count);
void GuessStatisticsMessage::createThereAreManyLetters(int count)
verb = "are";
number = std::to_string(count);
pluralModifier = "s";
void GuessStatisticsMessage::createThereIsOneLetter()
verb = "is";
number = "one";
pluralModifier = "";
void GuessStatisticsMessage::createThereAreNoLetters()
verb = "are";
number = "no";
pluralModifier = "s";
In an attempt to make this code more functional I ended up with the following:
.h
class GuessStatisticsMessageAlt
public:
GuessStatisticsMessageAlt() = default;
~GuessStatisticsMessageAlt() = default;
std::string make(char candidate, int count);
private:
struct MessageComponents
std::string verb;
std::string number;
std::string pluralModifier;
;
MessageComponents createPluralDependentMessageParts(int count);
MessageComponents createThereAreManyLetters(int count);
MessageComponents createThereIsOneLetter();
MessageComponents createThereAreNoLetters();
;
.cpp
std::string GuessStatisticsMessageAlt::make(char candidate, int count)
auto messageComponents = createPluralDependentMessageParts(count);
return fmt::format("There ", messageComponents.verb, messageComponents.number,
candidate, messageComponents.pluralModifier);
GuessStatisticsMessageAlt::MessageComponents GuessStatisticsMessageAlt::createPluralDependentMessageParts(int count)
if (count == 0)
return createThereAreNoLetters();
else if (count == 1)
return createThereIsOneLetter();
else
return createThereAreManyLetters(count);
GuessStatisticsMessageAlt::MessageComponents GuessStatisticsMessageAlt::createThereAreManyLetters(int count)
return "are", std::to_string(count), "s" ;
GuessStatisticsMessageAlt::MessageComponents GuessStatisticsMessageAlt::createThereIsOneLetter()
return "is", "one", "" ;
GuessStatisticsMessageAlt::MessageComponents GuessStatisticsMessageAlt::createThereAreNoLetters()
return "are", "no", "s" ;
Since at this point there was no real need for a class, I continued, reaching the final result:
.h
std::string makeGuessStatisticsMessage(char candidate, int count);
.cpp
namespace
struct MessageComponents
std::string verb;
std::string number;
std::string pluralModifier;
;
MessageComponents createThereAreManyLetters(int count)
return "are", std::to_string(count), "s" ;
MessageComponents createThereIsOneLetter()
return "is", "one", "" ;
MessageComponents createThereAreNoLetters()
return "are", "no", "s" ;
MessageComponents createPluralDependentMessageParts(int count)
if (count == 0)
return createThereAreNoLetters();
else if (count == 1)
return createThereIsOneLetter();
else
return createThereAreManyLetters(count);
std::string makeGuessStatisticsMessage(char candidate, int count)
auto messageComponents = createPluralDependentMessageParts(count);
return fmt::format("There ", messageComponents.verb, messageComponents.number,
candidate, messageComponents.pluralModifier);
The point is, I grapple a bit with when to apply different programming paradigms and find it difficult to decide if my re-implementation is actually an improvement in this case. In particular, hiding so much stuff in the anonymous namespace is something that bothers me somewhat.
So the primary question is - is this really an improvement? And the secondary - how can I learn to judge this better?
I hope it isn't too opinion-based, as this is a real struggle for me.
* Clean Code: A Handbook of Agile Software Craftsmanship, Robert C. Martin
c++ object-oriented functional-programming comparative-review formatting
$endgroup$
add a comment |
$begingroup$
I'm a C++ dev, and I've recently started working my way through Clean Code*. Whenever I encounter an example I think I could improve, I try to re-implement it in C++.
On pp. 28-29 there is an example with a message building class, which looks like this when implemented in C++ (includes and namespaces omitted):
.h
class GuessStatisticsMessage
public:
GuessStatisticsMessage() = default;
~GuessStatisticsMessage() = default;
std::string make(char candidate, int count);
private:
void createPluralDependentMessageParts(int count);
void createThereAreManyLetters(int count);
void createThereIsOneLetter();
void createThereAreNoLetters();
std::string number;
std::string verb;
std::string pluralModifier;
;
.cpp
std::string GuessStatisticsMessage::make(char candidate, int count)
createPluralDependentMessageParts(count);
return fmt::format("There ", verb, number, candidate, pluralModifier);
void GuessStatisticsMessage::createPluralDependentMessageParts(int count)
if (count == 0)
createThereAreNoLetters();
else if (count == 1)
createThereIsOneLetter();
else
createThereAreManyLetters(count);
void GuessStatisticsMessage::createThereAreManyLetters(int count)
verb = "are";
number = std::to_string(count);
pluralModifier = "s";
void GuessStatisticsMessage::createThereIsOneLetter()
verb = "is";
number = "one";
pluralModifier = "";
void GuessStatisticsMessage::createThereAreNoLetters()
verb = "are";
number = "no";
pluralModifier = "s";
In an attempt to make this code more functional I ended up with the following:
.h
class GuessStatisticsMessageAlt
public:
GuessStatisticsMessageAlt() = default;
~GuessStatisticsMessageAlt() = default;
std::string make(char candidate, int count);
private:
struct MessageComponents
std::string verb;
std::string number;
std::string pluralModifier;
;
MessageComponents createPluralDependentMessageParts(int count);
MessageComponents createThereAreManyLetters(int count);
MessageComponents createThereIsOneLetter();
MessageComponents createThereAreNoLetters();
;
.cpp
std::string GuessStatisticsMessageAlt::make(char candidate, int count)
auto messageComponents = createPluralDependentMessageParts(count);
return fmt::format("There ", messageComponents.verb, messageComponents.number,
candidate, messageComponents.pluralModifier);
GuessStatisticsMessageAlt::MessageComponents GuessStatisticsMessageAlt::createPluralDependentMessageParts(int count)
if (count == 0)
return createThereAreNoLetters();
else if (count == 1)
return createThereIsOneLetter();
else
return createThereAreManyLetters(count);
GuessStatisticsMessageAlt::MessageComponents GuessStatisticsMessageAlt::createThereAreManyLetters(int count)
return "are", std::to_string(count), "s" ;
GuessStatisticsMessageAlt::MessageComponents GuessStatisticsMessageAlt::createThereIsOneLetter()
return "is", "one", "" ;
GuessStatisticsMessageAlt::MessageComponents GuessStatisticsMessageAlt::createThereAreNoLetters()
return "are", "no", "s" ;
Since at this point there was no real need for a class, I continued, reaching the final result:
.h
std::string makeGuessStatisticsMessage(char candidate, int count);
.cpp
namespace
struct MessageComponents
std::string verb;
std::string number;
std::string pluralModifier;
;
MessageComponents createThereAreManyLetters(int count)
return "are", std::to_string(count), "s" ;
MessageComponents createThereIsOneLetter()
return "is", "one", "" ;
MessageComponents createThereAreNoLetters()
return "are", "no", "s" ;
MessageComponents createPluralDependentMessageParts(int count)
if (count == 0)
return createThereAreNoLetters();
else if (count == 1)
return createThereIsOneLetter();
else
return createThereAreManyLetters(count);
std::string makeGuessStatisticsMessage(char candidate, int count)
auto messageComponents = createPluralDependentMessageParts(count);
return fmt::format("There ", messageComponents.verb, messageComponents.number,
candidate, messageComponents.pluralModifier);
The point is, I grapple a bit with when to apply different programming paradigms and find it difficult to decide if my re-implementation is actually an improvement in this case. In particular, hiding so much stuff in the anonymous namespace is something that bothers me somewhat.
So the primary question is - is this really an improvement? And the secondary - how can I learn to judge this better?
I hope it isn't too opinion-based, as this is a real struggle for me.
* Clean Code: A Handbook of Agile Software Craftsmanship, Robert C. Martin
c++ object-oriented functional-programming comparative-review formatting
$endgroup$
add a comment |
$begingroup$
I'm a C++ dev, and I've recently started working my way through Clean Code*. Whenever I encounter an example I think I could improve, I try to re-implement it in C++.
On pp. 28-29 there is an example with a message building class, which looks like this when implemented in C++ (includes and namespaces omitted):
.h
class GuessStatisticsMessage
public:
GuessStatisticsMessage() = default;
~GuessStatisticsMessage() = default;
std::string make(char candidate, int count);
private:
void createPluralDependentMessageParts(int count);
void createThereAreManyLetters(int count);
void createThereIsOneLetter();
void createThereAreNoLetters();
std::string number;
std::string verb;
std::string pluralModifier;
;
.cpp
std::string GuessStatisticsMessage::make(char candidate, int count)
createPluralDependentMessageParts(count);
return fmt::format("There ", verb, number, candidate, pluralModifier);
void GuessStatisticsMessage::createPluralDependentMessageParts(int count)
if (count == 0)
createThereAreNoLetters();
else if (count == 1)
createThereIsOneLetter();
else
createThereAreManyLetters(count);
void GuessStatisticsMessage::createThereAreManyLetters(int count)
verb = "are";
number = std::to_string(count);
pluralModifier = "s";
void GuessStatisticsMessage::createThereIsOneLetter()
verb = "is";
number = "one";
pluralModifier = "";
void GuessStatisticsMessage::createThereAreNoLetters()
verb = "are";
number = "no";
pluralModifier = "s";
In an attempt to make this code more functional I ended up with the following:
.h
class GuessStatisticsMessageAlt
public:
GuessStatisticsMessageAlt() = default;
~GuessStatisticsMessageAlt() = default;
std::string make(char candidate, int count);
private:
struct MessageComponents
std::string verb;
std::string number;
std::string pluralModifier;
;
MessageComponents createPluralDependentMessageParts(int count);
MessageComponents createThereAreManyLetters(int count);
MessageComponents createThereIsOneLetter();
MessageComponents createThereAreNoLetters();
;
.cpp
std::string GuessStatisticsMessageAlt::make(char candidate, int count)
auto messageComponents = createPluralDependentMessageParts(count);
return fmt::format("There ", messageComponents.verb, messageComponents.number,
candidate, messageComponents.pluralModifier);
GuessStatisticsMessageAlt::MessageComponents GuessStatisticsMessageAlt::createPluralDependentMessageParts(int count)
if (count == 0)
return createThereAreNoLetters();
else if (count == 1)
return createThereIsOneLetter();
else
return createThereAreManyLetters(count);
GuessStatisticsMessageAlt::MessageComponents GuessStatisticsMessageAlt::createThereAreManyLetters(int count)
return "are", std::to_string(count), "s" ;
GuessStatisticsMessageAlt::MessageComponents GuessStatisticsMessageAlt::createThereIsOneLetter()
return "is", "one", "" ;
GuessStatisticsMessageAlt::MessageComponents GuessStatisticsMessageAlt::createThereAreNoLetters()
return "are", "no", "s" ;
Since at this point there was no real need for a class, I continued, reaching the final result:
.h
std::string makeGuessStatisticsMessage(char candidate, int count);
.cpp
namespace
struct MessageComponents
std::string verb;
std::string number;
std::string pluralModifier;
;
MessageComponents createThereAreManyLetters(int count)
return "are", std::to_string(count), "s" ;
MessageComponents createThereIsOneLetter()
return "is", "one", "" ;
MessageComponents createThereAreNoLetters()
return "are", "no", "s" ;
MessageComponents createPluralDependentMessageParts(int count)
if (count == 0)
return createThereAreNoLetters();
else if (count == 1)
return createThereIsOneLetter();
else
return createThereAreManyLetters(count);
std::string makeGuessStatisticsMessage(char candidate, int count)
auto messageComponents = createPluralDependentMessageParts(count);
return fmt::format("There ", messageComponents.verb, messageComponents.number,
candidate, messageComponents.pluralModifier);
The point is, I grapple a bit with when to apply different programming paradigms and find it difficult to decide if my re-implementation is actually an improvement in this case. In particular, hiding so much stuff in the anonymous namespace is something that bothers me somewhat.
So the primary question is - is this really an improvement? And the secondary - how can I learn to judge this better?
I hope it isn't too opinion-based, as this is a real struggle for me.
* Clean Code: A Handbook of Agile Software Craftsmanship, Robert C. Martin
c++ object-oriented functional-programming comparative-review formatting
$endgroup$
I'm a C++ dev, and I've recently started working my way through Clean Code*. Whenever I encounter an example I think I could improve, I try to re-implement it in C++.
On pp. 28-29 there is an example with a message building class, which looks like this when implemented in C++ (includes and namespaces omitted):
.h
class GuessStatisticsMessage
public:
GuessStatisticsMessage() = default;
~GuessStatisticsMessage() = default;
std::string make(char candidate, int count);
private:
void createPluralDependentMessageParts(int count);
void createThereAreManyLetters(int count);
void createThereIsOneLetter();
void createThereAreNoLetters();
std::string number;
std::string verb;
std::string pluralModifier;
;
.cpp
std::string GuessStatisticsMessage::make(char candidate, int count)
createPluralDependentMessageParts(count);
return fmt::format("There ", verb, number, candidate, pluralModifier);
void GuessStatisticsMessage::createPluralDependentMessageParts(int count)
if (count == 0)
createThereAreNoLetters();
else if (count == 1)
createThereIsOneLetter();
else
createThereAreManyLetters(count);
void GuessStatisticsMessage::createThereAreManyLetters(int count)
verb = "are";
number = std::to_string(count);
pluralModifier = "s";
void GuessStatisticsMessage::createThereIsOneLetter()
verb = "is";
number = "one";
pluralModifier = "";
void GuessStatisticsMessage::createThereAreNoLetters()
verb = "are";
number = "no";
pluralModifier = "s";
In an attempt to make this code more functional I ended up with the following:
.h
class GuessStatisticsMessageAlt
public:
GuessStatisticsMessageAlt() = default;
~GuessStatisticsMessageAlt() = default;
std::string make(char candidate, int count);
private:
struct MessageComponents
std::string verb;
std::string number;
std::string pluralModifier;
;
MessageComponents createPluralDependentMessageParts(int count);
MessageComponents createThereAreManyLetters(int count);
MessageComponents createThereIsOneLetter();
MessageComponents createThereAreNoLetters();
;
.cpp
std::string GuessStatisticsMessageAlt::make(char candidate, int count)
auto messageComponents = createPluralDependentMessageParts(count);
return fmt::format("There ", messageComponents.verb, messageComponents.number,
candidate, messageComponents.pluralModifier);
GuessStatisticsMessageAlt::MessageComponents GuessStatisticsMessageAlt::createPluralDependentMessageParts(int count)
if (count == 0)
return createThereAreNoLetters();
else if (count == 1)
return createThereIsOneLetter();
else
return createThereAreManyLetters(count);
GuessStatisticsMessageAlt::MessageComponents GuessStatisticsMessageAlt::createThereAreManyLetters(int count)
return "are", std::to_string(count), "s" ;
GuessStatisticsMessageAlt::MessageComponents GuessStatisticsMessageAlt::createThereIsOneLetter()
return "is", "one", "" ;
GuessStatisticsMessageAlt::MessageComponents GuessStatisticsMessageAlt::createThereAreNoLetters()
return "are", "no", "s" ;
Since at this point there was no real need for a class, I continued, reaching the final result:
.h
std::string makeGuessStatisticsMessage(char candidate, int count);
.cpp
namespace
struct MessageComponents
std::string verb;
std::string number;
std::string pluralModifier;
;
MessageComponents createThereAreManyLetters(int count)
return "are", std::to_string(count), "s" ;
MessageComponents createThereIsOneLetter()
return "is", "one", "" ;
MessageComponents createThereAreNoLetters()
return "are", "no", "s" ;
MessageComponents createPluralDependentMessageParts(int count)
if (count == 0)
return createThereAreNoLetters();
else if (count == 1)
return createThereIsOneLetter();
else
return createThereAreManyLetters(count);
std::string makeGuessStatisticsMessage(char candidate, int count)
auto messageComponents = createPluralDependentMessageParts(count);
return fmt::format("There ", messageComponents.verb, messageComponents.number,
candidate, messageComponents.pluralModifier);
The point is, I grapple a bit with when to apply different programming paradigms and find it difficult to decide if my re-implementation is actually an improvement in this case. In particular, hiding so much stuff in the anonymous namespace is something that bothers me somewhat.
So the primary question is - is this really an improvement? And the secondary - how can I learn to judge this better?
I hope it isn't too opinion-based, as this is a real struggle for me.
* Clean Code: A Handbook of Agile Software Craftsmanship, Robert C. Martin
c++ object-oriented functional-programming comparative-review formatting
c++ object-oriented functional-programming comparative-review formatting
edited Jul 22 at 22:08
200_success
135k21 gold badges173 silver badges443 bronze badges
135k21 gold badges173 silver badges443 bronze badges
asked Jul 21 at 9:48
SG_90SG_90
785 bronze badges
785 bronze badges
add a comment |
add a comment |
2 Answers
2
active
oldest
votes
$begingroup$
Getting rid of the class is definitely a step in the right direction.
Assuming
count
can never be negative, we should use anunsigned
integer type for it.I don't think the
MessageComponents
class is a good idea. It makes the design very inflexible. If we ever want to change the message, we'll have to change every single function there.We're programming a very specific output message, and the code doesn't seem to be intended for re-use, so I don't think we need the named functions either.
I'd suggest something more like:
std::string makeStatisticsMessage(char letter, unsigned int count)
auto const verb = (count == 1) ? "is" : "are";
auto const number = (count == 0) ? "no" : (count == 1) ? "one" : std::to_string(count);
auto const plural = (count == 1) ? "" : "s";
return fmt::format("There ''.", verb, number, letter, plural);
Although we now check count
multiple times, I'd argue that it's much easier to follow the logic for each component separately.
It also means that we can abstract or change the behavior for each component later (e.g. if we decide we want to print all the numbers as words instead of digits).
$endgroup$
1
$begingroup$
One reason to use a class rather than a simple function could be a redesign to allow multiple languages for the output. In this case I would define a interface with a virtual functionmakeStatisticsMessage()
and several inherited classes for the specific languages which should be supported.
$endgroup$
– πάντα ῥεῖ
Jul 21 at 12:15
4
$begingroup$
Also to enhance readability of your conditional logic, I'd recommend to avoid nested ternary operations in favor ofif
/else
statements. The result would be the same and it's much better to follow for anyone (beginner or expert).
$endgroup$
– πάντα ῥεῖ
Jul 21 at 12:22
$begingroup$
I've accepted this answer since the other one builds on it. Both of them were helpful, though.
$endgroup$
– SG_90
Jul 23 at 18:19
add a comment |
$begingroup$
While I am supporting @user673679's approach a lot in terms that putting everything into a single function, I still have concerns:
Using an interface might be better if you want to refactor the functionality to support multiple languages:
struct IGuessStatisticsMessage
virtual std::string makeStatisticsMessage(char letter, unsigned int count) = 0;
virtual ~IGuessStatisticsMessage()
;This would allow to provide implementations for different languages easier:
struct GuessStatisticsMessage_EN() : public IGuessStatisticsMessage
std::string makeStatisticsMessage(char letter, unsigned int count)
auto const verb = (count == 1) ? "is" : "are";
auto const number = (count == 0) ? "no" : (count == 1) ? "one" :
std::to_string(count);
auto const plural = (count == 1) ? "" : "s";
return fmt::format("There ''.", verb, number, letter, plural);
;
struct GuessStatisticsMessage_DE() : public IGuessStatisticsMessage
std::string makeStatisticsMessage(char letter, unsigned int count)
auto const number = (count == 0) ? "kein" : (count == 1) ? "ein" :
std::to_string(count);
auto const plural = (count == 1) ? "" : "s";
return fmt::format("Es gibt ''.", number, letter, plural);
;Also I am not a friend of nested ternary expressions, they generally tend to be hard to read. I'd rather use some code like this:
std::string makeStatisticsMessage(char letter, unsigned int count) {
auto const verb = (count == 1) ? "is" : "are";
std::string number;
if(count == 0)
number = "no";
else if (count == 1)
number = "one";
else
number = std::to_string(count);
auto const plural = (count == 1) ? "" : "s";
return fmt::format("There ''.", verb, number, letter, plural);
$endgroup$
$begingroup$
Thanks for the contribution. Would it make sense to go a step further by getting rid of theverb
/number
/plural
vars altogether and returning a complete string depending on thecount
(having multiplereturn
statements), or would you insist on keeping the vars?
$endgroup$
– SG_90
Jul 23 at 18:16
$begingroup$
@SG_90 The (local) variables are a minor point. Keeping a replacable interface is a major.
$endgroup$
– πάντα ῥεῖ
Jul 23 at 18:21
add a comment |
Your Answer
StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");
StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "196"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);
else
createEditor();
);
function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);
);
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f224603%2fmessage-formatting-code-with-pluralization-revised-to-be-more-functional%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
$begingroup$
Getting rid of the class is definitely a step in the right direction.
Assuming
count
can never be negative, we should use anunsigned
integer type for it.I don't think the
MessageComponents
class is a good idea. It makes the design very inflexible. If we ever want to change the message, we'll have to change every single function there.We're programming a very specific output message, and the code doesn't seem to be intended for re-use, so I don't think we need the named functions either.
I'd suggest something more like:
std::string makeStatisticsMessage(char letter, unsigned int count)
auto const verb = (count == 1) ? "is" : "are";
auto const number = (count == 0) ? "no" : (count == 1) ? "one" : std::to_string(count);
auto const plural = (count == 1) ? "" : "s";
return fmt::format("There ''.", verb, number, letter, plural);
Although we now check count
multiple times, I'd argue that it's much easier to follow the logic for each component separately.
It also means that we can abstract or change the behavior for each component later (e.g. if we decide we want to print all the numbers as words instead of digits).
$endgroup$
1
$begingroup$
One reason to use a class rather than a simple function could be a redesign to allow multiple languages for the output. In this case I would define a interface with a virtual functionmakeStatisticsMessage()
and several inherited classes for the specific languages which should be supported.
$endgroup$
– πάντα ῥεῖ
Jul 21 at 12:15
4
$begingroup$
Also to enhance readability of your conditional logic, I'd recommend to avoid nested ternary operations in favor ofif
/else
statements. The result would be the same and it's much better to follow for anyone (beginner or expert).
$endgroup$
– πάντα ῥεῖ
Jul 21 at 12:22
$begingroup$
I've accepted this answer since the other one builds on it. Both of them were helpful, though.
$endgroup$
– SG_90
Jul 23 at 18:19
add a comment |
$begingroup$
Getting rid of the class is definitely a step in the right direction.
Assuming
count
can never be negative, we should use anunsigned
integer type for it.I don't think the
MessageComponents
class is a good idea. It makes the design very inflexible. If we ever want to change the message, we'll have to change every single function there.We're programming a very specific output message, and the code doesn't seem to be intended for re-use, so I don't think we need the named functions either.
I'd suggest something more like:
std::string makeStatisticsMessage(char letter, unsigned int count)
auto const verb = (count == 1) ? "is" : "are";
auto const number = (count == 0) ? "no" : (count == 1) ? "one" : std::to_string(count);
auto const plural = (count == 1) ? "" : "s";
return fmt::format("There ''.", verb, number, letter, plural);
Although we now check count
multiple times, I'd argue that it's much easier to follow the logic for each component separately.
It also means that we can abstract or change the behavior for each component later (e.g. if we decide we want to print all the numbers as words instead of digits).
$endgroup$
1
$begingroup$
One reason to use a class rather than a simple function could be a redesign to allow multiple languages for the output. In this case I would define a interface with a virtual functionmakeStatisticsMessage()
and several inherited classes for the specific languages which should be supported.
$endgroup$
– πάντα ῥεῖ
Jul 21 at 12:15
4
$begingroup$
Also to enhance readability of your conditional logic, I'd recommend to avoid nested ternary operations in favor ofif
/else
statements. The result would be the same and it's much better to follow for anyone (beginner or expert).
$endgroup$
– πάντα ῥεῖ
Jul 21 at 12:22
$begingroup$
I've accepted this answer since the other one builds on it. Both of them were helpful, though.
$endgroup$
– SG_90
Jul 23 at 18:19
add a comment |
$begingroup$
Getting rid of the class is definitely a step in the right direction.
Assuming
count
can never be negative, we should use anunsigned
integer type for it.I don't think the
MessageComponents
class is a good idea. It makes the design very inflexible. If we ever want to change the message, we'll have to change every single function there.We're programming a very specific output message, and the code doesn't seem to be intended for re-use, so I don't think we need the named functions either.
I'd suggest something more like:
std::string makeStatisticsMessage(char letter, unsigned int count)
auto const verb = (count == 1) ? "is" : "are";
auto const number = (count == 0) ? "no" : (count == 1) ? "one" : std::to_string(count);
auto const plural = (count == 1) ? "" : "s";
return fmt::format("There ''.", verb, number, letter, plural);
Although we now check count
multiple times, I'd argue that it's much easier to follow the logic for each component separately.
It also means that we can abstract or change the behavior for each component later (e.g. if we decide we want to print all the numbers as words instead of digits).
$endgroup$
Getting rid of the class is definitely a step in the right direction.
Assuming
count
can never be negative, we should use anunsigned
integer type for it.I don't think the
MessageComponents
class is a good idea. It makes the design very inflexible. If we ever want to change the message, we'll have to change every single function there.We're programming a very specific output message, and the code doesn't seem to be intended for re-use, so I don't think we need the named functions either.
I'd suggest something more like:
std::string makeStatisticsMessage(char letter, unsigned int count)
auto const verb = (count == 1) ? "is" : "are";
auto const number = (count == 0) ? "no" : (count == 1) ? "one" : std::to_string(count);
auto const plural = (count == 1) ? "" : "s";
return fmt::format("There ''.", verb, number, letter, plural);
Although we now check count
multiple times, I'd argue that it's much easier to follow the logic for each component separately.
It also means that we can abstract or change the behavior for each component later (e.g. if we decide we want to print all the numbers as words instead of digits).
answered Jul 21 at 11:25
user673679user673679
4,6951 gold badge14 silver badges34 bronze badges
4,6951 gold badge14 silver badges34 bronze badges
1
$begingroup$
One reason to use a class rather than a simple function could be a redesign to allow multiple languages for the output. In this case I would define a interface with a virtual functionmakeStatisticsMessage()
and several inherited classes for the specific languages which should be supported.
$endgroup$
– πάντα ῥεῖ
Jul 21 at 12:15
4
$begingroup$
Also to enhance readability of your conditional logic, I'd recommend to avoid nested ternary operations in favor ofif
/else
statements. The result would be the same and it's much better to follow for anyone (beginner or expert).
$endgroup$
– πάντα ῥεῖ
Jul 21 at 12:22
$begingroup$
I've accepted this answer since the other one builds on it. Both of them were helpful, though.
$endgroup$
– SG_90
Jul 23 at 18:19
add a comment |
1
$begingroup$
One reason to use a class rather than a simple function could be a redesign to allow multiple languages for the output. In this case I would define a interface with a virtual functionmakeStatisticsMessage()
and several inherited classes for the specific languages which should be supported.
$endgroup$
– πάντα ῥεῖ
Jul 21 at 12:15
4
$begingroup$
Also to enhance readability of your conditional logic, I'd recommend to avoid nested ternary operations in favor ofif
/else
statements. The result would be the same and it's much better to follow for anyone (beginner or expert).
$endgroup$
– πάντα ῥεῖ
Jul 21 at 12:22
$begingroup$
I've accepted this answer since the other one builds on it. Both of them were helpful, though.
$endgroup$
– SG_90
Jul 23 at 18:19
1
1
$begingroup$
One reason to use a class rather than a simple function could be a redesign to allow multiple languages for the output. In this case I would define a interface with a virtual function
makeStatisticsMessage()
and several inherited classes for the specific languages which should be supported.$endgroup$
– πάντα ῥεῖ
Jul 21 at 12:15
$begingroup$
One reason to use a class rather than a simple function could be a redesign to allow multiple languages for the output. In this case I would define a interface with a virtual function
makeStatisticsMessage()
and several inherited classes for the specific languages which should be supported.$endgroup$
– πάντα ῥεῖ
Jul 21 at 12:15
4
4
$begingroup$
Also to enhance readability of your conditional logic, I'd recommend to avoid nested ternary operations in favor of
if
/ else
statements. The result would be the same and it's much better to follow for anyone (beginner or expert).$endgroup$
– πάντα ῥεῖ
Jul 21 at 12:22
$begingroup$
Also to enhance readability of your conditional logic, I'd recommend to avoid nested ternary operations in favor of
if
/ else
statements. The result would be the same and it's much better to follow for anyone (beginner or expert).$endgroup$
– πάντα ῥεῖ
Jul 21 at 12:22
$begingroup$
I've accepted this answer since the other one builds on it. Both of them were helpful, though.
$endgroup$
– SG_90
Jul 23 at 18:19
$begingroup$
I've accepted this answer since the other one builds on it. Both of them were helpful, though.
$endgroup$
– SG_90
Jul 23 at 18:19
add a comment |
$begingroup$
While I am supporting @user673679's approach a lot in terms that putting everything into a single function, I still have concerns:
Using an interface might be better if you want to refactor the functionality to support multiple languages:
struct IGuessStatisticsMessage
virtual std::string makeStatisticsMessage(char letter, unsigned int count) = 0;
virtual ~IGuessStatisticsMessage()
;This would allow to provide implementations for different languages easier:
struct GuessStatisticsMessage_EN() : public IGuessStatisticsMessage
std::string makeStatisticsMessage(char letter, unsigned int count)
auto const verb = (count == 1) ? "is" : "are";
auto const number = (count == 0) ? "no" : (count == 1) ? "one" :
std::to_string(count);
auto const plural = (count == 1) ? "" : "s";
return fmt::format("There ''.", verb, number, letter, plural);
;
struct GuessStatisticsMessage_DE() : public IGuessStatisticsMessage
std::string makeStatisticsMessage(char letter, unsigned int count)
auto const number = (count == 0) ? "kein" : (count == 1) ? "ein" :
std::to_string(count);
auto const plural = (count == 1) ? "" : "s";
return fmt::format("Es gibt ''.", number, letter, plural);
;Also I am not a friend of nested ternary expressions, they generally tend to be hard to read. I'd rather use some code like this:
std::string makeStatisticsMessage(char letter, unsigned int count) {
auto const verb = (count == 1) ? "is" : "are";
std::string number;
if(count == 0)
number = "no";
else if (count == 1)
number = "one";
else
number = std::to_string(count);
auto const plural = (count == 1) ? "" : "s";
return fmt::format("There ''.", verb, number, letter, plural);
$endgroup$
$begingroup$
Thanks for the contribution. Would it make sense to go a step further by getting rid of theverb
/number
/plural
vars altogether and returning a complete string depending on thecount
(having multiplereturn
statements), or would you insist on keeping the vars?
$endgroup$
– SG_90
Jul 23 at 18:16
$begingroup$
@SG_90 The (local) variables are a minor point. Keeping a replacable interface is a major.
$endgroup$
– πάντα ῥεῖ
Jul 23 at 18:21
add a comment |
$begingroup$
While I am supporting @user673679's approach a lot in terms that putting everything into a single function, I still have concerns:
Using an interface might be better if you want to refactor the functionality to support multiple languages:
struct IGuessStatisticsMessage
virtual std::string makeStatisticsMessage(char letter, unsigned int count) = 0;
virtual ~IGuessStatisticsMessage()
;This would allow to provide implementations for different languages easier:
struct GuessStatisticsMessage_EN() : public IGuessStatisticsMessage
std::string makeStatisticsMessage(char letter, unsigned int count)
auto const verb = (count == 1) ? "is" : "are";
auto const number = (count == 0) ? "no" : (count == 1) ? "one" :
std::to_string(count);
auto const plural = (count == 1) ? "" : "s";
return fmt::format("There ''.", verb, number, letter, plural);
;
struct GuessStatisticsMessage_DE() : public IGuessStatisticsMessage
std::string makeStatisticsMessage(char letter, unsigned int count)
auto const number = (count == 0) ? "kein" : (count == 1) ? "ein" :
std::to_string(count);
auto const plural = (count == 1) ? "" : "s";
return fmt::format("Es gibt ''.", number, letter, plural);
;Also I am not a friend of nested ternary expressions, they generally tend to be hard to read. I'd rather use some code like this:
std::string makeStatisticsMessage(char letter, unsigned int count) {
auto const verb = (count == 1) ? "is" : "are";
std::string number;
if(count == 0)
number = "no";
else if (count == 1)
number = "one";
else
number = std::to_string(count);
auto const plural = (count == 1) ? "" : "s";
return fmt::format("There ''.", verb, number, letter, plural);
$endgroup$
$begingroup$
Thanks for the contribution. Would it make sense to go a step further by getting rid of theverb
/number
/plural
vars altogether and returning a complete string depending on thecount
(having multiplereturn
statements), or would you insist on keeping the vars?
$endgroup$
– SG_90
Jul 23 at 18:16
$begingroup$
@SG_90 The (local) variables are a minor point. Keeping a replacable interface is a major.
$endgroup$
– πάντα ῥεῖ
Jul 23 at 18:21
add a comment |
$begingroup$
While I am supporting @user673679's approach a lot in terms that putting everything into a single function, I still have concerns:
Using an interface might be better if you want to refactor the functionality to support multiple languages:
struct IGuessStatisticsMessage
virtual std::string makeStatisticsMessage(char letter, unsigned int count) = 0;
virtual ~IGuessStatisticsMessage()
;This would allow to provide implementations for different languages easier:
struct GuessStatisticsMessage_EN() : public IGuessStatisticsMessage
std::string makeStatisticsMessage(char letter, unsigned int count)
auto const verb = (count == 1) ? "is" : "are";
auto const number = (count == 0) ? "no" : (count == 1) ? "one" :
std::to_string(count);
auto const plural = (count == 1) ? "" : "s";
return fmt::format("There ''.", verb, number, letter, plural);
;
struct GuessStatisticsMessage_DE() : public IGuessStatisticsMessage
std::string makeStatisticsMessage(char letter, unsigned int count)
auto const number = (count == 0) ? "kein" : (count == 1) ? "ein" :
std::to_string(count);
auto const plural = (count == 1) ? "" : "s";
return fmt::format("Es gibt ''.", number, letter, plural);
;Also I am not a friend of nested ternary expressions, they generally tend to be hard to read. I'd rather use some code like this:
std::string makeStatisticsMessage(char letter, unsigned int count) {
auto const verb = (count == 1) ? "is" : "are";
std::string number;
if(count == 0)
number = "no";
else if (count == 1)
number = "one";
else
number = std::to_string(count);
auto const plural = (count == 1) ? "" : "s";
return fmt::format("There ''.", verb, number, letter, plural);
$endgroup$
While I am supporting @user673679's approach a lot in terms that putting everything into a single function, I still have concerns:
Using an interface might be better if you want to refactor the functionality to support multiple languages:
struct IGuessStatisticsMessage
virtual std::string makeStatisticsMessage(char letter, unsigned int count) = 0;
virtual ~IGuessStatisticsMessage()
;This would allow to provide implementations for different languages easier:
struct GuessStatisticsMessage_EN() : public IGuessStatisticsMessage
std::string makeStatisticsMessage(char letter, unsigned int count)
auto const verb = (count == 1) ? "is" : "are";
auto const number = (count == 0) ? "no" : (count == 1) ? "one" :
std::to_string(count);
auto const plural = (count == 1) ? "" : "s";
return fmt::format("There ''.", verb, number, letter, plural);
;
struct GuessStatisticsMessage_DE() : public IGuessStatisticsMessage
std::string makeStatisticsMessage(char letter, unsigned int count)
auto const number = (count == 0) ? "kein" : (count == 1) ? "ein" :
std::to_string(count);
auto const plural = (count == 1) ? "" : "s";
return fmt::format("Es gibt ''.", number, letter, plural);
;Also I am not a friend of nested ternary expressions, they generally tend to be hard to read. I'd rather use some code like this:
std::string makeStatisticsMessage(char letter, unsigned int count) {
auto const verb = (count == 1) ? "is" : "are";
std::string number;
if(count == 0)
number = "no";
else if (count == 1)
number = "one";
else
number = std::to_string(count);
auto const plural = (count == 1) ? "" : "s";
return fmt::format("There ''.", verb, number, letter, plural);
edited Jul 21 at 16:03
answered Jul 21 at 13:00
πάντα ῥεῖπάντα ῥεῖ
4,1283 gold badges14 silver badges28 bronze badges
4,1283 gold badges14 silver badges28 bronze badges
$begingroup$
Thanks for the contribution. Would it make sense to go a step further by getting rid of theverb
/number
/plural
vars altogether and returning a complete string depending on thecount
(having multiplereturn
statements), or would you insist on keeping the vars?
$endgroup$
– SG_90
Jul 23 at 18:16
$begingroup$
@SG_90 The (local) variables are a minor point. Keeping a replacable interface is a major.
$endgroup$
– πάντα ῥεῖ
Jul 23 at 18:21
add a comment |
$begingroup$
Thanks for the contribution. Would it make sense to go a step further by getting rid of theverb
/number
/plural
vars altogether and returning a complete string depending on thecount
(having multiplereturn
statements), or would you insist on keeping the vars?
$endgroup$
– SG_90
Jul 23 at 18:16
$begingroup$
@SG_90 The (local) variables are a minor point. Keeping a replacable interface is a major.
$endgroup$
– πάντα ῥεῖ
Jul 23 at 18:21
$begingroup$
Thanks for the contribution. Would it make sense to go a step further by getting rid of the
verb
/number
/plural
vars altogether and returning a complete string depending on the count
(having multiple return
statements), or would you insist on keeping the vars?$endgroup$
– SG_90
Jul 23 at 18:16
$begingroup$
Thanks for the contribution. Would it make sense to go a step further by getting rid of the
verb
/number
/plural
vars altogether and returning a complete string depending on the count
(having multiple return
statements), or would you insist on keeping the vars?$endgroup$
– SG_90
Jul 23 at 18:16
$begingroup$
@SG_90 The (local) variables are a minor point. Keeping a replacable interface is a major.
$endgroup$
– πάντα ῥεῖ
Jul 23 at 18:21
$begingroup$
@SG_90 The (local) variables are a minor point. Keeping a replacable interface is a major.
$endgroup$
– πάντα ῥεῖ
Jul 23 at 18:21
add a comment |
Thanks for contributing an answer to Code Review Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
Use MathJax to format equations. MathJax reference.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f224603%2fmessage-formatting-code-with-pluralization-revised-to-be-more-functional%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown