如何让redesign/fix这个程序允许封装?
How to redesign/fix this program to allow encapsulation?
我最初是在 CodeReview 上问这个问题的。由于那里的负面接待(完全是我的错),我认为这是正确的地方。我在这里复制了我在那里问过的完全相同的问题(进行了一些小的修改)。
我的目标是尽可能使用 STL 功能用 C++ 编写 CPU 调度模拟器。目前为止,我只写了FCFS的代码,还没有提供任何输入法。
最让我印象深刻的是过程的定义class。我设计程序的方式,封装性很差。看我现在的情况,脑子里只有两个解决方案:
将所有数据成员设为私有。为 PID、到达时间和突发时间提供访问器,为其余部分提供修改器。但是,我担心这会使我的代码臃肿,而且使用修改器只会破坏封装。
制作 FCFS()
、displayResult()
以及我在 process
class 中添加的 friend
的任何其他算法。这让我可以在一定程度上控制封装,所以我喜欢这个。
我尝试做的另一件事是将 PID、到达时间和突发时间变量设置为 const
和 public。这允许算法读取它们的值但不能修改它们。但是,代码无法编译,因为 const
成员不是默认可分配的。
我想就如何解决上述问题以及如何更有效地使用 STL 使我的代码更具表现力提出建议。
#include <iostream>
#include <vector>
#include <algorithm>
#include <limits>
constexpr int notYetComputed = std::numeric_limits<unsigned int>::max();
struct process
{
unsigned int m_procId;
unsigned int m_arrivalTime;
unsigned int m_burstTime;
unsigned int m_responseTime;
unsigned int m_completionTime;
unsigned int m_turnaroundTime;
unsigned int m_waitingTime;
process(unsigned int pid, unsigned int at, unsigned int bt)
: m_procId {pid}, m_arrivalTime {at}, m_burstTime {bt}
{
m_waitingTime = m_turnaroundTime = m_completionTime = m_responseTime = notYetComputed;
}
};
void displayResult(const std::vector<process>& procs)
{
for(auto& x : procs)
std::cout << "PID: " << x.m_procId << ", "
<< "Waiting Time: " << x.m_waitingTime << ", "
<< "Turnaround Time: " << x.m_turnaroundTime << ", "
<< "Response Time: " << x.m_responseTime << ", "
<< "Completion Time: " << x.m_completionTime << "\n";
}
void FCFS(std::vector<process>& procList)
{
//Sort based on arrival order. Use PID in case of same arrival time.
auto arrivalOrder = [] (const process& p1, const process& p2) {
if(p1.m_arrivalTime < p2.m_arrivalTime) return true;
if(p1.m_arrivalTime == p2.m_arrivalTime) return (p1.m_procId < p2.m_procId);
return false;
};
std::sort(procList.begin(), procList.end(), arrivalOrder);
unsigned int clock {0};
auto computeResult = [&clock] (process& pr) {
pr.m_responseTime = clock - pr.m_arrivalTime;
pr.m_waitingTime = (pr.m_turnaroundTime =
(pr.m_completionTime = (clock += pr.m_burstTime)) - pr.m_arrivalTime)
- pr.m_burstTime;
};
std::for_each(procList.begin(), procList.end(), computeResult);
}
int main()
{
std::vector<process> procs {{0,0,5}, {1,1,3}, {2,2,8}, {3,3,6}};
FCFS(procs);
//Sort based on PID before showing result
std::sort(procs.begin(), procs.end(),
[](const process& p1, const process& p2) {
return p1.m_procId < p2.m_procId;
});
displayResult(procs);
}
更新:
我打算在未来添加更多的调度算法。所以我更喜欢按照 Jarod42 的思路回答。我真的无法将特定于算法的代码作为 process
class.
的一部分
如果你想有更多的封装,那么你需要应用OO(面向对象)方法。
您想将数据成员和操作这些成员的方法放在一个 class 中。所以这些方法可以访问 class 成员,因为它们是 class.
的一部分
函数void displayResult(const std::vector<process>& procs
和void FCFS(std::vector<process>& procList)
是独立的函数,它们操作和访问process
数据成员。他们不应该那样做。
所以我提议覆盖进程的插入操作符class。进程 class "knows" 如何打印其数据,因此应该这样做。
同样适用于排序。我们覆盖小于运算符“<”,然后排序函数知道要做什么。
而且函数运算符也会被重载。这也可以对过程内部数据进行操作。
不幸的是你想要一个额外的排序标准,所以我需要为 proc ID 添加一个 getter。
请参阅下面修改后的代码。所有过程数据都受到保护。我们只有一个通过 getter.
的读取权限
#include <iostream>
#include <vector>
#include <algorithm>
#include <limits>
#include <iterator>
constexpr unsigned int notYetComputed = std::numeric_limits<unsigned int>::max();
class process
{
public:
process(unsigned int pid, unsigned int at, unsigned int bt) : m_procId{ pid }, m_arrivalTime{ at }, m_burstTime{ bt }{}
friend std::ostream& operator << (std::ostream& os, const process& p) {
os << "PID: " << p.m_procId << ", "
<< "Waiting Time: " << p.m_waitingTime << ", "
<< "Turnaround Time: " << p.m_turnaroundTime << ", "
<< "Response Time: " << p.m_responseTime << ", "
<< "Completion Time: " << p.m_completionTime;
return os;
}
friend bool operator < (const process& p1, const process& p2) {
if (p1.m_arrivalTime < p2.m_arrivalTime) return true;
if (p1.m_arrivalTime == p2.m_arrivalTime) return (p1.m_procId < p2.m_procId);
return false;
}
void operator () (unsigned int& clock) {
m_responseTime = clock - m_arrivalTime;
m_waitingTime = (m_turnaroundTime =
(m_completionTime = (clock += m_burstTime)) - m_arrivalTime)
- m_burstTime;
}
unsigned int getProcID() const { return m_procId; }
protected:
unsigned int m_procId;
unsigned int m_arrivalTime;
unsigned int m_burstTime;
unsigned int m_responseTime{ notYetComputed };
unsigned int m_completionTime{ notYetComputed };
unsigned int m_turnaroundTime{ notYetComputed };
unsigned int m_waitingTime{ notYetComputed };
};
void displayResult(const std::vector<process>& procs)
{
std::copy(procs.begin(), procs.end(), std::ostream_iterator<process>(std::cout, "\n"));
}
void FCFS(std::vector<process>& procList)
{
std::sort(procList.begin(), procList.end());
unsigned int clock{ 0 };
std::for_each(procList.begin(), procList.end(), [&clock](process& p) { p(clock); });
}
int main()
{
std::vector<process> procs{ {0,0,5}, {1,1,3}, {2,2,8}, {3,3,6} };
FCFS(procs);
//Sort based on PID before showing result
std::sort(procs.begin(), procs.end(),
[](const process & p1, const process & p2) {
return p1.getProcID() < p2.getProcID();
});
displayResult(procs);
}
您的进程 class 包含输入和输出成员。
我会拆分它们(我使用的名称可能应该改进)以明确说明流程(我们可以摆脱 notYetComputed
这对我来说似乎是一个 hack):
struct process_input
{
unsigned int m_procId;
unsigned int m_arrivalTime;
unsigned int m_burstTime;
};
struct process_ouput
{
unsigned int m_responseTime;
unsigned int m_completionTime;
unsigned int m_turnaroundTime;
unsigned int m_waitingTime;
};
struct process
{
process_input m_input;
process_output m_output;
};
FCFS
会显示流量,所以 std::vector<process> FCFS(std::vector<process_input> inputs)
随着一些样式的改变,它变成了:
std::vector<process> FCFS(std::vector<process_input> inputs)
{
// Sort based on arrival order. Use PID in case of same arrival time.
auto proj = [](const process_input& p){ return std::tie(p.m_arrivalTime, p.m_procId); };
auto compare = [&] (const process& lhs, const process& rhs) {
return proj(lhs) < proj(rhs);
};
std::sort(inputs.begin(), inputs.end(), compare);
std::vector<process> res;
res.reserve(inputs.size());
unsigned int clock {0};
auto computeOutput = [] (const process_input& pr, unsigned int& clock) {
process_ouput res;
res.m_responseTime = clock - pr.m_arrivalTime;
clock += pr.m_burstTime;
res.m_completionTime = clock;
res.m_turnaroundTime = res.m_completionTime - pr.m_arrivalTime;
res.m_waitingTime = res.m_turnaroundTime - pr.m_burstTime;
return res;
};
for (auto& input : inputs) {
res.push_back({input, computeOutput(input, clock)});
}
return res;
}
将 main
更改为:
int main()
{
std::vector<process> inputs {{0,0,5}, {1,1,3}, {2,2,8}, {3,3,6}};
auto procs = FCFS(inputs);
//Sort based on PID before showing result
std::sort(procs.begin(), procs.end(),
[](const process& p1, const process& p2) {
return p1.m_input.m_procId < p2.m_input.m_procId;
});
displayResult(procs);
}
现在我们要保留哪些保证?
process_input
是没有内部约束的简单数据。
process_output
是简单的数据,似乎也没有内部约束。
process
应该在输入和输出之间具有一致性:因此不能对其成员进行非常量访问。
// const member might be enough in that case,
// but I prefer to avoid const members
// which make class harder to use (not movable, not assignable, ....)
// so only const getters
struct process
{
public:
const process_input& input() const { return m_input;
const process_output& output() const { return m_outnput; }
private:
process_input m_input;
process_output m_output;
};
作为替代方法或除 getter 之外,您可以在此处添加 Display
方法。
我最初是在 CodeReview 上问这个问题的。由于那里的负面接待(完全是我的错),我认为这是正确的地方。我在这里复制了我在那里问过的完全相同的问题(进行了一些小的修改)。
我的目标是尽可能使用 STL 功能用 C++ 编写 CPU 调度模拟器。目前为止,我只写了FCFS的代码,还没有提供任何输入法。
最让我印象深刻的是过程的定义class。我设计程序的方式,封装性很差。看我现在的情况,脑子里只有两个解决方案:
将所有数据成员设为私有。为 PID、到达时间和突发时间提供访问器,为其余部分提供修改器。但是,我担心这会使我的代码臃肿,而且使用修改器只会破坏封装。
制作
FCFS()
、displayResult()
以及我在process
class 中添加的friend
的任何其他算法。这让我可以在一定程度上控制封装,所以我喜欢这个。
我尝试做的另一件事是将 PID、到达时间和突发时间变量设置为 const
和 public。这允许算法读取它们的值但不能修改它们。但是,代码无法编译,因为 const
成员不是默认可分配的。
我想就如何解决上述问题以及如何更有效地使用 STL 使我的代码更具表现力提出建议。
#include <iostream>
#include <vector>
#include <algorithm>
#include <limits>
constexpr int notYetComputed = std::numeric_limits<unsigned int>::max();
struct process
{
unsigned int m_procId;
unsigned int m_arrivalTime;
unsigned int m_burstTime;
unsigned int m_responseTime;
unsigned int m_completionTime;
unsigned int m_turnaroundTime;
unsigned int m_waitingTime;
process(unsigned int pid, unsigned int at, unsigned int bt)
: m_procId {pid}, m_arrivalTime {at}, m_burstTime {bt}
{
m_waitingTime = m_turnaroundTime = m_completionTime = m_responseTime = notYetComputed;
}
};
void displayResult(const std::vector<process>& procs)
{
for(auto& x : procs)
std::cout << "PID: " << x.m_procId << ", "
<< "Waiting Time: " << x.m_waitingTime << ", "
<< "Turnaround Time: " << x.m_turnaroundTime << ", "
<< "Response Time: " << x.m_responseTime << ", "
<< "Completion Time: " << x.m_completionTime << "\n";
}
void FCFS(std::vector<process>& procList)
{
//Sort based on arrival order. Use PID in case of same arrival time.
auto arrivalOrder = [] (const process& p1, const process& p2) {
if(p1.m_arrivalTime < p2.m_arrivalTime) return true;
if(p1.m_arrivalTime == p2.m_arrivalTime) return (p1.m_procId < p2.m_procId);
return false;
};
std::sort(procList.begin(), procList.end(), arrivalOrder);
unsigned int clock {0};
auto computeResult = [&clock] (process& pr) {
pr.m_responseTime = clock - pr.m_arrivalTime;
pr.m_waitingTime = (pr.m_turnaroundTime =
(pr.m_completionTime = (clock += pr.m_burstTime)) - pr.m_arrivalTime)
- pr.m_burstTime;
};
std::for_each(procList.begin(), procList.end(), computeResult);
}
int main()
{
std::vector<process> procs {{0,0,5}, {1,1,3}, {2,2,8}, {3,3,6}};
FCFS(procs);
//Sort based on PID before showing result
std::sort(procs.begin(), procs.end(),
[](const process& p1, const process& p2) {
return p1.m_procId < p2.m_procId;
});
displayResult(procs);
}
更新:
我打算在未来添加更多的调度算法。所以我更喜欢按照 Jarod42 的思路回答。我真的无法将特定于算法的代码作为 process
class.
如果你想有更多的封装,那么你需要应用OO(面向对象)方法。
您想将数据成员和操作这些成员的方法放在一个 class 中。所以这些方法可以访问 class 成员,因为它们是 class.
的一部分函数void displayResult(const std::vector<process>& procs
和void FCFS(std::vector<process>& procList)
是独立的函数,它们操作和访问process
数据成员。他们不应该那样做。
所以我提议覆盖进程的插入操作符class。进程 class "knows" 如何打印其数据,因此应该这样做。
同样适用于排序。我们覆盖小于运算符“<”,然后排序函数知道要做什么。
而且函数运算符也会被重载。这也可以对过程内部数据进行操作。
不幸的是你想要一个额外的排序标准,所以我需要为 proc ID 添加一个 getter。
请参阅下面修改后的代码。所有过程数据都受到保护。我们只有一个通过 getter.
的读取权限#include <iostream>
#include <vector>
#include <algorithm>
#include <limits>
#include <iterator>
constexpr unsigned int notYetComputed = std::numeric_limits<unsigned int>::max();
class process
{
public:
process(unsigned int pid, unsigned int at, unsigned int bt) : m_procId{ pid }, m_arrivalTime{ at }, m_burstTime{ bt }{}
friend std::ostream& operator << (std::ostream& os, const process& p) {
os << "PID: " << p.m_procId << ", "
<< "Waiting Time: " << p.m_waitingTime << ", "
<< "Turnaround Time: " << p.m_turnaroundTime << ", "
<< "Response Time: " << p.m_responseTime << ", "
<< "Completion Time: " << p.m_completionTime;
return os;
}
friend bool operator < (const process& p1, const process& p2) {
if (p1.m_arrivalTime < p2.m_arrivalTime) return true;
if (p1.m_arrivalTime == p2.m_arrivalTime) return (p1.m_procId < p2.m_procId);
return false;
}
void operator () (unsigned int& clock) {
m_responseTime = clock - m_arrivalTime;
m_waitingTime = (m_turnaroundTime =
(m_completionTime = (clock += m_burstTime)) - m_arrivalTime)
- m_burstTime;
}
unsigned int getProcID() const { return m_procId; }
protected:
unsigned int m_procId;
unsigned int m_arrivalTime;
unsigned int m_burstTime;
unsigned int m_responseTime{ notYetComputed };
unsigned int m_completionTime{ notYetComputed };
unsigned int m_turnaroundTime{ notYetComputed };
unsigned int m_waitingTime{ notYetComputed };
};
void displayResult(const std::vector<process>& procs)
{
std::copy(procs.begin(), procs.end(), std::ostream_iterator<process>(std::cout, "\n"));
}
void FCFS(std::vector<process>& procList)
{
std::sort(procList.begin(), procList.end());
unsigned int clock{ 0 };
std::for_each(procList.begin(), procList.end(), [&clock](process& p) { p(clock); });
}
int main()
{
std::vector<process> procs{ {0,0,5}, {1,1,3}, {2,2,8}, {3,3,6} };
FCFS(procs);
//Sort based on PID before showing result
std::sort(procs.begin(), procs.end(),
[](const process & p1, const process & p2) {
return p1.getProcID() < p2.getProcID();
});
displayResult(procs);
}
您的进程 class 包含输入和输出成员。
我会拆分它们(我使用的名称可能应该改进)以明确说明流程(我们可以摆脱 notYetComputed
这对我来说似乎是一个 hack):
struct process_input
{
unsigned int m_procId;
unsigned int m_arrivalTime;
unsigned int m_burstTime;
};
struct process_ouput
{
unsigned int m_responseTime;
unsigned int m_completionTime;
unsigned int m_turnaroundTime;
unsigned int m_waitingTime;
};
struct process
{
process_input m_input;
process_output m_output;
};
FCFS
会显示流量,所以 std::vector<process> FCFS(std::vector<process_input> inputs)
随着一些样式的改变,它变成了:
std::vector<process> FCFS(std::vector<process_input> inputs)
{
// Sort based on arrival order. Use PID in case of same arrival time.
auto proj = [](const process_input& p){ return std::tie(p.m_arrivalTime, p.m_procId); };
auto compare = [&] (const process& lhs, const process& rhs) {
return proj(lhs) < proj(rhs);
};
std::sort(inputs.begin(), inputs.end(), compare);
std::vector<process> res;
res.reserve(inputs.size());
unsigned int clock {0};
auto computeOutput = [] (const process_input& pr, unsigned int& clock) {
process_ouput res;
res.m_responseTime = clock - pr.m_arrivalTime;
clock += pr.m_burstTime;
res.m_completionTime = clock;
res.m_turnaroundTime = res.m_completionTime - pr.m_arrivalTime;
res.m_waitingTime = res.m_turnaroundTime - pr.m_burstTime;
return res;
};
for (auto& input : inputs) {
res.push_back({input, computeOutput(input, clock)});
}
return res;
}
将 main
更改为:
int main()
{
std::vector<process> inputs {{0,0,5}, {1,1,3}, {2,2,8}, {3,3,6}};
auto procs = FCFS(inputs);
//Sort based on PID before showing result
std::sort(procs.begin(), procs.end(),
[](const process& p1, const process& p2) {
return p1.m_input.m_procId < p2.m_input.m_procId;
});
displayResult(procs);
}
现在我们要保留哪些保证?
process_input
是没有内部约束的简单数据。process_output
是简单的数据,似乎也没有内部约束。process
应该在输入和输出之间具有一致性:因此不能对其成员进行非常量访问。
// const member might be enough in that case,
// but I prefer to avoid const members
// which make class harder to use (not movable, not assignable, ....)
// so only const getters
struct process
{
public:
const process_input& input() const { return m_input;
const process_output& output() const { return m_outnput; }
private:
process_input m_input;
process_output m_output;
};
作为替代方法或除 getter 之外,您可以在此处添加 Display
方法。