Message-formatting code with pluralization, revised to be more functionalTrying to find a good design for reading in values of different types from a fileFormatting 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 checksumGeneric Skip list implementation in C++ Version 3Object-oriented calculatorComputing the first n primes: an example from Clean Code revised for C++
Is 10/2 cable appropriate for a 240V, 30A garage heater outlet?
What's an "add" chord?
How to get the address of a C++ lambda function within the lambda itself?
Google just EOLed the original Pixel. How long until it's a brick?
What is the "opposite" of a random variable?
Why are Democrats mostly focused on increasing healthcare spending, rarely mentioning any proposals for decreasing the costs of healthcare services?
Decision problems for which it is unknown whether they are decidable
Is it sportsmanlike to waste opponents' time by giving check at the end of the game?
Dissecting the exotic bulbfish
Speaking German abroad and feeling condescended to when people speak English back to me
How does sudo handle $HOME differently since 19.10?
Find constant that allows an integral to be finite
Can using the BULK API to perform SOQL queries save on total number of queries?
Can a Scourge Aasimar fly?
What happened to Sophie in her last encounter with Arthur?
What are these criss-cross patterns close to Cambridge Airport (UK)?
At what point in time would humans notice a 21st century satellite observing them?
Physical interpretation of complex numbers
how to make a twisted wrapper
50% portfolio in single stock, JPM - appropriate for 80 year old?
Can we rotate symbols in LaTeX? How should we make this diagram?
Multivariate Limits
Find the Longest Even Length Word
Would nuclear bombs be effective against clouds of nanites?
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 fileFormatting 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 checksumGeneric Skip list implementation in C++ Version 3Object-oriented calculatorComputing the first n primes: an example from Clean Code revised for C++
.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
136k21 gold badges175 silver badges448 bronze badges
136k21 gold badges175 silver badges448 bronze badges
asked Jul 21 at 9:48
SG_90SG_90
936 bronze badges
936 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/4.0/"u003ecc by-sa 4.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
5,6191 gold badge15 silver badges40 bronze badges
5,6191 gold badge15 silver badges40 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,3563 gold badges15 silver badges28 bronze badges
4,3563 gold badges15 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