当两个功能非常相似时如何避免复制和粘贴?
How to avoid copy and paste when two functions are very similar?
我经常遇到具有相同结构和逻辑的方法,但有一些差异,我找不到不重复自己的正确方法。
例如:
void ChannelSelection::selectAlmostOkChannels(int currentInkId)
{
bool selected = true;
foreach (auto report, m_reports) {
if (report.scoreByInk.find(currentInkId) != report.scoreByInk.end()) {
auto tmpStatus = Assessment::getStatusFromScore(report.scoreByInk.value(currentInkId));
if (tmpStatus == Assessment::Ok)
selected = false;
else if (tmpStatus == Assessment::NotOk)
m_autoSelection[report.name].setSelected(currentInkId, false);
}
}
m_currentSelection.insert(currentInkId, selected);
}
void ChannelSelection::selectNotOkChannels(int currentInkId)
{
bool selected = true;
foreach (auto report, m_reports) {
if (report.scoreByInk.find(currentInkId) != report.scoreByInk.end()) {
auto tmpStatus = Assessment::getStatusFromScore(report.scoreByInk.value(currentInkId));
if (tmpStatus == Assessment::Ok || tmpStatus == Assessment::AlmostOk)
selected = false;
}
}
m_currentSelection.insert(currentInkId, selected);
}
如您所见,这两个函数非常相似(只有内部不同)。我怎样才能很好地删除这段代码中的重复项?
我想到的解决方案之一是使用函子,例如:
void ChannelSelection::selectChannels(int currentInkId, std::function<bool()> fn)
{
bool selected = true;
foreach (auto report, m_reports) {
if (report.scoreByInk.find(currentInkId) != report.scoreByInk.end()) {
auto tmpStatus = Assessment::getStatusFromScore(report.scoreByInk.value(currentInkId));
selected = fn();
}
}
m_currentSelection.insert(currentInkId, selected);
}
调用者负责实现仿函数。有没有没有这个问题的替代方案?
您可以将两个函数合并为一个带有附加条件参数的函数,例如:
void ChannelSelection::selectChannels(int currentInkId, bool condition)
{
bool selected = true;
foreach (auto report, m_reports) {
if (report.scoreByInk.find(currentInkId) != report.scoreByInk.end()) {
auto tmpStatus = Assessment::getStatusFromScore(report.scoreByInk.value(currentInkId));
if (condition) {
if (tmpStatus == Assessment::Ok) {
selected = false;
} else if (tmpStatus == Assessment::NotOk) {
m_autoSelection[report.name].setSelected(currentInkId, false);
}
} else if (tmpStatus == Assessment::Ok || tmpStatus == Assessment::AlmostOk) {
selected = false;
}
}
}
m_currentSelection.insert(currentInkId, selected);
}
用 condition == true
调用它会调用 selectAlmostOkChannels()
函数的等价物,否则 selectNotOkChannels()
会调用它。
您不必使参数化 selectChannels
public。它可以是 selectAlmostOkChannels
和 selectNotOkChannels
的私有实现细节,您的 public 函数。
selectChannels
甚至可以实现为函数模板,这样生成的代码就相当于hand-written"copy pasted"版本,没有代码重复的维护负担
template<typename SelectFunction>
void ChannelSelection::selectChannels(int currentInkId, SelectFunction selectFn)
{
bool selected = true;
foreach (auto report, m_reports) {
if (report.scoreByInk.find(currentInkId) != report.scoreByInk.end()) {
auto tmpStatus = Assessment::getStatusFromScore(report.scoreByInk.value(currentInkId));
selected = selectFn(tmpStatus);
/* fill in */
}
}
m_currentSelection.insert(currentInkId, selected);
}
void ChannelSelection::selectAlmostOkChannels(int currentInkId)
{
selectChannels(currentInkId, [] (auto tmpStatus) -> bool {
return /* fill in */;
});
}
void ChannelSelection::selectNotOkChannels(int currentInkId)
{
selectChannels(currentInkId, [] (auto tmpStatus) -> bool {
return /* fill in */;
});
}
您可能被告知模板需要在 headers 中,但这实际上不是全部!模板定义需要在实例化的地方可见。由于您的模板仅用于成员函数的私有实现,因此您的模板定义可以在实现两个成员函数的同一个文件中
我经常遇到具有相同结构和逻辑的方法,但有一些差异,我找不到不重复自己的正确方法。
例如:
void ChannelSelection::selectAlmostOkChannels(int currentInkId)
{
bool selected = true;
foreach (auto report, m_reports) {
if (report.scoreByInk.find(currentInkId) != report.scoreByInk.end()) {
auto tmpStatus = Assessment::getStatusFromScore(report.scoreByInk.value(currentInkId));
if (tmpStatus == Assessment::Ok)
selected = false;
else if (tmpStatus == Assessment::NotOk)
m_autoSelection[report.name].setSelected(currentInkId, false);
}
}
m_currentSelection.insert(currentInkId, selected);
}
void ChannelSelection::selectNotOkChannels(int currentInkId)
{
bool selected = true;
foreach (auto report, m_reports) {
if (report.scoreByInk.find(currentInkId) != report.scoreByInk.end()) {
auto tmpStatus = Assessment::getStatusFromScore(report.scoreByInk.value(currentInkId));
if (tmpStatus == Assessment::Ok || tmpStatus == Assessment::AlmostOk)
selected = false;
}
}
m_currentSelection.insert(currentInkId, selected);
}
如您所见,这两个函数非常相似(只有内部不同)。我怎样才能很好地删除这段代码中的重复项?
我想到的解决方案之一是使用函子,例如:
void ChannelSelection::selectChannels(int currentInkId, std::function<bool()> fn)
{
bool selected = true;
foreach (auto report, m_reports) {
if (report.scoreByInk.find(currentInkId) != report.scoreByInk.end()) {
auto tmpStatus = Assessment::getStatusFromScore(report.scoreByInk.value(currentInkId));
selected = fn();
}
}
m_currentSelection.insert(currentInkId, selected);
}
调用者负责实现仿函数。有没有没有这个问题的替代方案?
您可以将两个函数合并为一个带有附加条件参数的函数,例如:
void ChannelSelection::selectChannels(int currentInkId, bool condition)
{
bool selected = true;
foreach (auto report, m_reports) {
if (report.scoreByInk.find(currentInkId) != report.scoreByInk.end()) {
auto tmpStatus = Assessment::getStatusFromScore(report.scoreByInk.value(currentInkId));
if (condition) {
if (tmpStatus == Assessment::Ok) {
selected = false;
} else if (tmpStatus == Assessment::NotOk) {
m_autoSelection[report.name].setSelected(currentInkId, false);
}
} else if (tmpStatus == Assessment::Ok || tmpStatus == Assessment::AlmostOk) {
selected = false;
}
}
}
m_currentSelection.insert(currentInkId, selected);
}
用 condition == true
调用它会调用 selectAlmostOkChannels()
函数的等价物,否则 selectNotOkChannels()
会调用它。
您不必使参数化 selectChannels
public。它可以是 selectAlmostOkChannels
和 selectNotOkChannels
的私有实现细节,您的 public 函数。
selectChannels
甚至可以实现为函数模板,这样生成的代码就相当于hand-written"copy pasted"版本,没有代码重复的维护负担
template<typename SelectFunction>
void ChannelSelection::selectChannels(int currentInkId, SelectFunction selectFn)
{
bool selected = true;
foreach (auto report, m_reports) {
if (report.scoreByInk.find(currentInkId) != report.scoreByInk.end()) {
auto tmpStatus = Assessment::getStatusFromScore(report.scoreByInk.value(currentInkId));
selected = selectFn(tmpStatus);
/* fill in */
}
}
m_currentSelection.insert(currentInkId, selected);
}
void ChannelSelection::selectAlmostOkChannels(int currentInkId)
{
selectChannels(currentInkId, [] (auto tmpStatus) -> bool {
return /* fill in */;
});
}
void ChannelSelection::selectNotOkChannels(int currentInkId)
{
selectChannels(currentInkId, [] (auto tmpStatus) -> bool {
return /* fill in */;
});
}
您可能被告知模板需要在 headers 中,但这实际上不是全部!模板定义需要在实例化的地方可见。由于您的模板仅用于成员函数的私有实现,因此您的模板定义可以在实现两个成员函数的同一个文件中