设计文件夹/文件系统?

时间:2013-06-09 19:28:34

标签: c++ class-design

我正在尝试创建类似于大多数操作系统中使用的文件夹/文件系统。 基本上我已经发现我应该使用三个班级; FileFolder和公共基类。我们称之为Common是为了创造力。 以下是我认为这三者的标题应如下所示:

COMMON.H

class Common {
    string m_name; // all files and folders have a name
    Folder* m_parent; // all files and folders can have a parent

public:
    virtual void open() = 0; // executed when folder or file is opened by user
    virtual void draw() const; // all files and folders can be printed
    virtual void setParent(Folder* parent);
    virtual Folder* getParent() const;
};

folder.h

class Folder : public Common {
    vector<Common*> m_children; // folders can contain other files and folders
                                // whereas files cannot

public:
    virtual void open(); // folder opens; basically shows the content
    virtual void draw() const; // folder draws differently
};

file.h

class File : public Common {
    // not really "files", they just call a function when opened
    funcptr m_openAction;

public:
    virtual void open(); // calls m_openAction() when opened
};

正如您所看到的,问题是我的基类Common应该能够知道它的子类Folder,这是不好的行为,不应该这样做(至少根据我的老师的说法) )。 这使我无法像我原先计划的那样对系统进行编码。

如何设计这样的系统?

2 个答案:

答案 0 :(得分:7)

您现有的设计要求Common“知道其子类Folder”。 它只需要Common标头声明有一些这样的类 作为Folder

class Folder; // Forward declaration

class Common {
    string m_name; // all files and folders have a name
    Folder* m_parent; // all files and folders can have a parent

public:
    virtual ~Common(); // Don't forget virtual destructor!
    virtual void open() = 0; // executed when folder or file is opened by user
    virtual void draw() const; // all files and folders can be printed
    virtual void setParent(Folder* parent);
    virtual Folder* getParent() const;
};

这里没有依赖循环。

如果出于某种学术原因,你必须有一个甚至没有的基类 提及任何子类然后你可以像这样制作多态基类:

class Node {
    string m_name; // all files and folders have a name
    Node* m_parent; // all files and folders can have a parent

public:
    virtual ~Node(); // Don't forget virtual destructor!
    virtual void open() = 0; // executed when folder or file is opened by user
    virtual void draw() const; // all files and folders can be printed
    virtual void setParent(Node* parent);
    virtual Node* getParent() const;
};

使用此设计,setParent(Node* parent)方法将具有此功能 包含Node *参数parent实际上是a的运行时检查 Folder *,使用例如

Folder *pf = dynamic_cast<Folder *>(parent);

在这种情况下,它还需要一个非void返回类型 表示成功或失败。这是曲折的,而不仅仅是制造一个 声明class Folder

以解决OP的后续问题。

  

Inside Common setParent()我必须调用Folder的m_children;这会导致错误。即使我在common.cpp中包含folder.h,我也无法访问文件夹的私有成员。有任何想法吗? :

我之前的回答仅限于向您展示“使您无法按照计划对系统进行编码” 实际上并没有。

您现在看到的问题是将某个文件夹f设置为某些文件夹的父级 节点n不是节点(文件或文件夹)上的独立操作。 f只能 如果n同时成为其中之一,则有效地成为n的父级 f的孩子。因此在n.setParent(parent)中,与设置同时进行 n.mParent == parent您希望将n添加到parent->m_children; 但节点m_children无法访问n

此问题是您设计的重要提示。如果设置父母和添加孩子 必须始终一起出现然后它们实际上是相同的操作--set-parent-add-child - 只是以不同方式调用:来自父级或来自子级。如果有的话 Common提供setParent(Folder *)的原因那么同样好 Folder提供addChild(Common *)的原因,他们必须做同样的事情。

这是否表明,例如,static void Common::link(Folder * parent, Common * child) 可能会更好地公开取代他们两个?可能是吧;但是你开始了 Common::setParent(Folder *), 这是合理的;所以将它与Folder::addChild(Common *)匹配也是合理的, 然后我们可以通过调用彼此来让他们做同样的事情。

那么,考虑一下,因为pCommon->setParent(pFolder)
相当于pFolder->addChild(pCommon),你也需要手段 从其父级删除节点;因为在你可以有效添加之前 如果是父节点,则必须将其从现有父节点中删除(如果有)。和 这个操作很可能是客户端代码的福音;所以 Folder::removeChild(Common *)也是一个自然的补充 Folder界面。

Folder::addChild(Common * pnode)Folder::removeChild(Common * pnode)是 您缺乏管理私有成员Folder::m_children的接口。

接下来,请考虑这些方法中的每一种都必须遵守 确定pnode实际上是否是文件夹的子项:您不能添加 一个孩子到一个已经是孩子的文件夹,你不能删除一个不是一个孩子的孩子。 因此Folder::find(Common * pnode)也很有用 - 至少对实施而言 (私人),也可能是客户代码(公共):你可以决定。

然后,考虑Folder::find(Common * pnode)要求 另一种方法:bool Common::operator==(Common const & other)。我们就这么说吧 如果节点具有相同的名称,则它们是相等的。

Common::clearParent()也会浮现在脑海中,但我会把它放在一边。

这些想法足以满足以下实施要求 不完整,次优和不切实际,但展示了如何加入我们拥有的点 刚刚确定,以便通过成员访问障碍 仍然阻止你这是不切实际的,因为它忽略了 假设的动态对象的所有权的全部问题 由其方法的Folder *Common *参数解决。 你可以自己处理(你可能希望调查 std::shared_ptrstd::unique_ptr,即使 这些更多 比你应该在这个项目中使用的先进设施。)

<强> COMMON.H

#ifndef COMMON_H
#define COMMON_H

#include <string>

#include <iostream>

class Folder;

class Common {
    std::string m_name;
    Folder* m_parent;

public:
    explicit Common(std::string const & name)
    : m_name(name),m_parent(nullptr){}
    virtual ~Common(){};
    virtual void open() { /*Whatever*/}
    virtual void draw() const {/*Whatever*/}
    virtual Folder* getParent() const { return m_parent; };
    virtual void setParent(Folder* parent);
    bool operator==(Common const & other) const {
        return m_name == other.m_name;
    }
    bool operator!=(Common const & other) const {
        return !(*this  == other);
    }
#if 1 // Testing
    std::string const & name() const {
        return m_name;
    }
    std::string parent() const;

    virtual void list() const {
        std::cout << name() << " (in " << parent() << ')' << std::endl ;
    }
#endif
};

#endif // EOF

<强> folder.h

#ifndef FOLDER_H
#define FOLDER_H

#include "common.h"
#include <vector>

class Folder : public Common {
    std::vector<Common *> m_children;

    std::vector<Common *>::iterator find(Common const * child) {
        auto i = m_children.begin();
        for (   ;i != m_children.end() && **i != *child; ++i) {}
        return i;
    }

public:
    explicit Folder(std::string const & name)
    : Common(name){}
    virtual void open(){/*Whatever*/}
    virtual void draw() const {/*Whatever*/}
    void addChild(Common * child) {
        auto par = child->getParent();
        if (par && par != this) {
            par->removeChild(child);
        }
        if (find(child) == m_children.end()) {
            m_children.push_back(child);
            m_children.back()->setParent(this);
        }
    }
    void removeChild(Common const * child) {
        auto where = find(child);
        if (where != m_children.end()) {
            m_children.erase(where);
        }
    }
#if 1 // Testing
    void list() const {
        std::cout << name() << " {" << std::endl;
        for (Common const * child : m_children) {
            child->list();
        }
        std::cout << '}' << std::endl;
    }
#endif
};

#endif //EOF

<强> file.h

#ifndef FILE_H
#define FILE_H

#include "common.h"

class File : public Common {
    // Whatever
public:
    explicit File(std::string const & name)
    : Common(name){}
    virtual void open(){/*Whatever*/};

};

#endif // EOF

<强> common.cpp

#include "common.h"
#include "folder.h"

void Common::setParent(Folder* parent) {
    auto par = getParent();
    if (par && par != parent) {
        par->removeChild(this);
    }
    m_parent = parent;
    m_parent->addChild(this);
}

#if 1 // Testing
std::string Common::parent() const {
    return m_parent ? m_parent->name() : "<null>";
}
#endif

测试程序:

#include "common.h"
#include "folder.h"
#include "file.h"

int main()
{
    Folder *fo0 = new Folder("folder0");
    File * fi0 = new File("file0");
    File * fi1 = new File("file1");
    fo0->addChild(fi0);
    fi1->setParent(fo0);
    fo0->addChild(fi0);  // Duplicate
    fi1->setParent(fo0); // Duplicate
    // There are now 2 files in folder fo0
    fo0->list();
    Folder *fo1 = new Folder("folder1");
    fo1->addChild(fi1);
    fi0->setParent(fo1);
    fo1->addChild(fi1); // Duplicate
    fi0->setParent(fo1); // Duplicate
    // There are now 0 files in folder fo0
    // There are now 2 files in folder fo1
    fo0->list();
    fo1->list();
    delete fo0;
    delete fo1;
    delete fi0;
    delete fi1;
    return 0;
}

答案 1 :(得分:0)

您不能将文件夹放在公共文件夹中并在文件夹中进行公共继承;这是一个循环冗余错误。

此系统只能设计为两个类:文件和文件夹,但如果要进行概括,请从常用文件中删除文件夹。