在编程的世界中,良好的设计原则是写出可维护、可扩展代码的关键。本文将通过一个具体的编程问题,探讨单一职责原则(Single Responsibility Principle, SRP)在C++编程中的应用,并提供相应的代码示例。单一职责原则是SOLID设计原则中的第一个原则,它强调一个类或函数应该只有一个引起变化的原因。
在之前的编程问题中,Reddit用户TheFlamefire提出了一些宝贵的建议,这些建议与单一职责原则直接相关。在本文中,将首先回顾之前问题的解决方案,并在此基础上,改进代码以符合单一职责原则。
根据维基百科的描述,单一职责原则是计算机编程中的一个原则,它指出每个模块、类或函数应该只负责软件提供的功能的一小部分。通过应用这一原则,将得到更加模块化的代码,代码块更小,更易于理解和操作。虽然这一原则有许多其他的优点,但本文不会详细描述。此外,这一原则是SOLID原则的第一部分,值得用几篇文章来正确解释。
在Day 4的解决方案中,创建了一个名为getMd5的函数,该函数将私钥传递给MD5算法,并将结果从unsigned char数组转换为std::string。强调的"AND"是可疑的,因为它表明该函数做了两件事,违反了单一职责原则。为了纠正这一点,不得不将getMd5函数拆分为两个函数:getMd5只返回与私钥生成的MD5哈希对应的字节数组,bytesToString将unsigned char数组转换为std::string。强烈建议去看看这些函数在GitHub上的实现。
完整的问题可以在Advent of Code网站上找到,这里只描述问题的本质:圣诞老人需要帮助确定他的文本文件中的哪些字符串是淘气的还是乖巧的。一个乖巧的字符串必须具备以下所有属性:它包含至少三个元音字母;它包含至少一个连续出现两次的字母;它不包含字符串ab、cd、pq或xy。
正如在Day 4中提到的,对于这个问题,将使用单一职责原则。事实上,这个问题非常适合引入这个原则,因为乖巧字符串属性列表中的每个项目都可以放在一个具有单一责任的函数中。因此,最终得到了三个函数:hasThreeVowels、hasLettersAppearingTwiceInRow和hasForbiddenSubString。让深入研究这些函数。
对于hasThreeVowels函数,可以直接使用std::count_if算法来实现正确的行为:
bool hasThreeVowels(const std::string_view str) {
return 3 <= std::count_if(
std::begin(str),
std::end(str), [](const auto& letter) {
return letter == 'a' || letter == 'e' || letter == 'i' || letter == 'o' || letter == 'u';
});
}
为了遵守单一职责原则,甚至可以创建一个isVowel函数,而不是直接在lambda中包含条件。
对于第二个函数hasLettersAppearingTwiceInRow,没有找到一个std算法可以提供一个方便的解决方案,所以使用了基于范围的循环的简单解决方案:
bool hasLettersAppearingTwiceInRow(const std::string_view str) {
char previousCharacter = CHAR_MIN;
for (const auto& character : str) {
if (character == previousCharacter) {
return true;
}
previousCharacter = character;
}
return false;
}
对于最后一个函数hasForbiddenSubString,可以使用std::string_view::find方法:
bool hasForbiddenSubString(const std::string_view str) {
return str.find("ab") != std::string_view::npos ||
str.find("cd") != std::string_view::npos ||
str.find("pq") != std::string_view::npos ||
str.find("xy") != std::string_view::npos;
}
现在有了这三个函数,还有两件事要做。首先,必须创建一个定义什么是乖巧字符串的函数,通过组合前面的函数来实现条件:
bool isNice(const std::string_view str) {
return hasThreeVowels(str) &&
hasLettersAppearingTwiceInRow(str) &&
!hasForbiddenSubString(str);
}
然后对输入的每一行应用这个函数:
auto numberOfNiceString{0};
foreachLineIn(fileContent, [&numberOfNiceString](const std::string& str) {
if (isNice(str)) {
++numberOfNiceString;
}
});
std::cout << numberOfNiceString;
就这样,得到了一个漂亮的解决方案,它应用了单一职责原则,同时找到了圣诞老人的乖巧字符串。