试图了解 OOP 并想知道我的功能是否正确完成
Trying to get my head around OOP and wondering if my functions are done correctly
我正在制作一个简单的小计算器应用程序,我想知道我如何才能最好地将封装和抽象合并到我的代码中以使其整洁有序。
这是关于如何使用以下代码处理这些方法的理想方式吗?我不确定它是否有点过火,但在我看来,它使它非常易读和整洁。
另外有没有规定如果你重复代码,它属于一个函数?
提前谢谢你。
// Test ApplicationV2.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <iostream>
#include <string>
#include <vector>
int getNumber();
char getOperation();
int additionResult(int& val1, int& val2);
int multiplyResult(int& val1, int& val2);
int divisionResult(int& val1, int& val2);
int main()
{
int val1, val2;
char operation;
std::cout << "Enter the first value.";
val1 = getNumber();
std::cout << "Enter operation";
operation = getOperation();
std::cout << "Enter second number.";
val2 = getNumber();
switch (operation)
{
case '+':
std::cout << additionResult(val1, val2);
break;
case '*':
std::cout << multiplyResult(val1, val2);
break;
case '/':
std::cout << divisionResult(val1, val2);
break;
default:
std::cout << "Please enter sufficient data.";
}
//Keep window open
std::string barn;
std::cin >> barn;
return 0;
}
int getNumber()
{
int x;
std::cin >> x;
return x;
}
char getOperation()
{
char op;
std::cin >> op;
return op;
}
int additionResult(int& val1, int& val2)
{
return (val1 + val2);
}
int multiplyResult(int& val1, int& val2)
{
return (val1 * val2);
}
int divisionResult(int& val1, int& val2)
{
return (val1 / val2);
}
如前所述,您的程序不使用 OOP(除了 std::cout
和 std::cin
是对象)。如果您的目标是探索使用 OOP 作为构建代码的方式,请考虑:
#include <iostream>
class Calculation
{
public:
Calculation(int first, int second)
: first_(first), second_(second)
{ }
int add() { return first_ + second_; }
int multiply() { return first_ * second_; }
int divide() { return first_ / second_; }
private:
int first_, second_;
};
int main()
{
int val1, val2;
char operation;
if (std::cout << "Enter the first value: " &&
std::cin >> val1 &&
std::cout << "Enter operation: " &&
std::cin >> operation &&
std::cout << "Enter the second value: " &&
std::cin >> val2)
{
Calculation calc(val1, val2);
switch (operation)
{
case '+':
std::cout << calc.add() << '\n';
break;
case '*':
std::cout << calc.multiply() << '\n';
break;
case '/':
std::cout << calc.divide() << '\n';
break;
default:
std::cerr << "Invalid operator: must be +, * or /.\n";
}
}
else
std::cerr << "Error getting inputs.\n";
//Keep window open
std::cin.get(); // real operating systems don't need this ;-P
}
注意 Calculation
class 是 OOP 方面,它有 private
数据:这就是封装。虽然它没有太多抽象 - 问题太简单 - 但你可以说它在构造时捕获了输入并且 add()
、multiply()
、divide()
调用不必注意涉及的数据类型或数量(即 Calculator
可能存储了 std::vector<int>
或 double
并且 switch
语句的代码无需更改,前提是 operator<<
为这三个函数的 return 类型重载)。
您可以通过在 Calculation
中使用成员函数来获得更多抽象,例如:
int do_operation(char c) const
{
switch (c)
{
case '+': return first_ + second_;
case '*': return first_ * second_;
case '/': return first_ / second_;
default: throw std::runtime_error("invalid operator character");
}
}
那么main
中的use就可以直接尝试一个操作看是否支持,不知道Calculation
提供的具体操作:
Calculation calc(val1, val2);
try
{
std::cout << calc.do_operation(operation) << '\n';
}
catch (const std::exception& e)
{
std::cerr << "oops - caught exception: " << e.what() << '\n';
}
这似乎没什么大不了的,但这意味着如果您有很多地方使用 Calculation
对象,那么当您想要支持另一个操作时不必更新它们。一个缺点是 do_operation
的 return 类型对于所有类型的操作必须相同,因此您无法决定 "hey, I'll have divide return a double
instead of rounding down to an int
"并简单地单独更改 divide()
的 return 类型(但您可以将它们全部 return double
- 在这种简单的情况下不会造成太大伤害,更普遍的是可以使用 "variant" 到 return 任何一组类型,但客户端代码更难使用。
Also is there a rule in saying that if you are repeating code, it belongs in a function?
差不多。请注意,您没有重复(调用两次)您放入函数中的任何代码,但它在更复杂的程序中变得很重要。
作为使用函数分解上述代码的示例,请考虑如何将提示和输入组合在可重用函数中:
template <typename T>
bool input(const std::string& prompt, T& value)
{
return std::cout << prompt && std::cin >> value;
}
template <typename T>
意味着上述函数可以与任何类型的 value
一起使用。使用这个input
函数,提示和输入操作可以改写为:
if (input("Enter the first value: ", val1) &&
input("Enter operation: ", operation) &&
input("Enter the second value: ", val2))
{
这是否更好取决于 reader 是否可以猜测 input
的功能是否足够好而无需去研究 input
函数,但它被重用的频率越高这样的努力越有可能是值得的。
一种可能是有一个抽象 "binary operation" class,然后为每个实际操作(加法、减法、乘法、除法)派生 classes。
如果这样做,您就可以(例如)创建一个二进制操作对象(指针或引用)的容器,并在该集合中查找正确的操作。例如:
struct bin_op {
virtual int operator()(int a, int b);
};
struct add : bin_op {
virtual int operator()(int a, int b) { return a + b; }
};
struct sub : bin_op {
virtual int operator()(int a, int b) { return a - b; }
};
// mul and div similarly.
std::map<char, bin_op *> ops;
add a;
sub s;
mul m;
div d;
ops['+'] = &a;
ops['-'] = &s;
ops['*'] = &m;
ops['/'] = &d;
然后要执行操作,您可以这样做:
auto b = ops.find(operation);
if (b != ops.end())
std::cout << b->second(val1, val2);
else
std::cerr << "unknown operation: " << operation << "\n";
这样做的一个明显优势是它使添加新操作变得相当简单:定义一个从 bin_op
派生的新 class 来执行您的新操作,将其实例添加到ops
(用你想要触发该操作的角色),然后开始。
我正在制作一个简单的小计算器应用程序,我想知道我如何才能最好地将封装和抽象合并到我的代码中以使其整洁有序。
这是关于如何使用以下代码处理这些方法的理想方式吗?我不确定它是否有点过火,但在我看来,它使它非常易读和整洁。
另外有没有规定如果你重复代码,它属于一个函数?
提前谢谢你。
// Test ApplicationV2.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <iostream>
#include <string>
#include <vector>
int getNumber();
char getOperation();
int additionResult(int& val1, int& val2);
int multiplyResult(int& val1, int& val2);
int divisionResult(int& val1, int& val2);
int main()
{
int val1, val2;
char operation;
std::cout << "Enter the first value.";
val1 = getNumber();
std::cout << "Enter operation";
operation = getOperation();
std::cout << "Enter second number.";
val2 = getNumber();
switch (operation)
{
case '+':
std::cout << additionResult(val1, val2);
break;
case '*':
std::cout << multiplyResult(val1, val2);
break;
case '/':
std::cout << divisionResult(val1, val2);
break;
default:
std::cout << "Please enter sufficient data.";
}
//Keep window open
std::string barn;
std::cin >> barn;
return 0;
}
int getNumber()
{
int x;
std::cin >> x;
return x;
}
char getOperation()
{
char op;
std::cin >> op;
return op;
}
int additionResult(int& val1, int& val2)
{
return (val1 + val2);
}
int multiplyResult(int& val1, int& val2)
{
return (val1 * val2);
}
int divisionResult(int& val1, int& val2)
{
return (val1 / val2);
}
如前所述,您的程序不使用 OOP(除了 std::cout
和 std::cin
是对象)。如果您的目标是探索使用 OOP 作为构建代码的方式,请考虑:
#include <iostream>
class Calculation
{
public:
Calculation(int first, int second)
: first_(first), second_(second)
{ }
int add() { return first_ + second_; }
int multiply() { return first_ * second_; }
int divide() { return first_ / second_; }
private:
int first_, second_;
};
int main()
{
int val1, val2;
char operation;
if (std::cout << "Enter the first value: " &&
std::cin >> val1 &&
std::cout << "Enter operation: " &&
std::cin >> operation &&
std::cout << "Enter the second value: " &&
std::cin >> val2)
{
Calculation calc(val1, val2);
switch (operation)
{
case '+':
std::cout << calc.add() << '\n';
break;
case '*':
std::cout << calc.multiply() << '\n';
break;
case '/':
std::cout << calc.divide() << '\n';
break;
default:
std::cerr << "Invalid operator: must be +, * or /.\n";
}
}
else
std::cerr << "Error getting inputs.\n";
//Keep window open
std::cin.get(); // real operating systems don't need this ;-P
}
注意 Calculation
class 是 OOP 方面,它有 private
数据:这就是封装。虽然它没有太多抽象 - 问题太简单 - 但你可以说它在构造时捕获了输入并且 add()
、multiply()
、divide()
调用不必注意涉及的数据类型或数量(即 Calculator
可能存储了 std::vector<int>
或 double
并且 switch
语句的代码无需更改,前提是 operator<<
为这三个函数的 return 类型重载)。
您可以通过在 Calculation
中使用成员函数来获得更多抽象,例如:
int do_operation(char c) const
{
switch (c)
{
case '+': return first_ + second_;
case '*': return first_ * second_;
case '/': return first_ / second_;
default: throw std::runtime_error("invalid operator character");
}
}
那么main
中的use就可以直接尝试一个操作看是否支持,不知道Calculation
提供的具体操作:
Calculation calc(val1, val2);
try
{
std::cout << calc.do_operation(operation) << '\n';
}
catch (const std::exception& e)
{
std::cerr << "oops - caught exception: " << e.what() << '\n';
}
这似乎没什么大不了的,但这意味着如果您有很多地方使用 Calculation
对象,那么当您想要支持另一个操作时不必更新它们。一个缺点是 do_operation
的 return 类型对于所有类型的操作必须相同,因此您无法决定 "hey, I'll have divide return a double
instead of rounding down to an int
"并简单地单独更改 divide()
的 return 类型(但您可以将它们全部 return double
- 在这种简单的情况下不会造成太大伤害,更普遍的是可以使用 "variant" 到 return 任何一组类型,但客户端代码更难使用。
Also is there a rule in saying that if you are repeating code, it belongs in a function?
差不多。请注意,您没有重复(调用两次)您放入函数中的任何代码,但它在更复杂的程序中变得很重要。
作为使用函数分解上述代码的示例,请考虑如何将提示和输入组合在可重用函数中:
template <typename T>
bool input(const std::string& prompt, T& value)
{
return std::cout << prompt && std::cin >> value;
}
template <typename T>
意味着上述函数可以与任何类型的 value
一起使用。使用这个input
函数,提示和输入操作可以改写为:
if (input("Enter the first value: ", val1) &&
input("Enter operation: ", operation) &&
input("Enter the second value: ", val2))
{
这是否更好取决于 reader 是否可以猜测 input
的功能是否足够好而无需去研究 input
函数,但它被重用的频率越高这样的努力越有可能是值得的。
一种可能是有一个抽象 "binary operation" class,然后为每个实际操作(加法、减法、乘法、除法)派生 classes。
如果这样做,您就可以(例如)创建一个二进制操作对象(指针或引用)的容器,并在该集合中查找正确的操作。例如:
struct bin_op {
virtual int operator()(int a, int b);
};
struct add : bin_op {
virtual int operator()(int a, int b) { return a + b; }
};
struct sub : bin_op {
virtual int operator()(int a, int b) { return a - b; }
};
// mul and div similarly.
std::map<char, bin_op *> ops;
add a;
sub s;
mul m;
div d;
ops['+'] = &a;
ops['-'] = &s;
ops['*'] = &m;
ops['/'] = &d;
然后要执行操作,您可以这样做:
auto b = ops.find(operation);
if (b != ops.end())
std::cout << b->second(val1, val2);
else
std::cerr << "unknown operation: " << operation << "\n";
这样做的一个明显优势是它使添加新操作变得相当简单:定义一个从 bin_op
派生的新 class 来执行您的新操作,将其实例添加到ops
(用你想要触发该操作的角色),然后开始。