如何让redesign/fix这个程序允许封装?

How to redesign/fix this program to allow encapsulation?

我最初是在 CodeReview 上问这个问题的。由于那里的负面接待(完全是我的错),我认为这是正确的地方。我在这里复制了我在那里问过的完全相同的问题(进行了一些小的修改)。

我的目标是尽可能使用 STL 功能用 C++ 编写 CPU 调度模拟器。目前为止,我只写了FCFS的代码,还没有提供任何输入法。

最让我印象深刻的是过程的定义class。我设计程序的方式,封装性很差。看我现在的情况,脑子里只有两个解决方案:

  1. 将所有数据成员设为私有。为 PID、到达时间和突发时间提供访问器,为其余部分提供修改器。但是,我担心这会使我的代码臃肿,而且使用修改器只会破坏封装。

  2. 制作 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>& procsvoid 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 方法。