“继承"关系的关系另一个(1:N)关系 [英] relationship that "inherit" another (1:N) relationship

查看:89
本文介绍了“继承"关系的关系另一个(1:N)关系的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想要一个支持这些特定的1:N关系的数据结构:-
1#. Human 加薪 0-N Human
2#. Human 0-N Dog
3#. Human 耕种 0-N Tree
4#. Dog 0-N Parasites的房子.

I want a data-structure that supports these specific 1:N relations :-
1#. Human raise 0-N Human
2#. Human has 0-N Dog
3#. Human cultivate 0-N Tree
4#. Dog is a house of 0-N Parasites.

注意:
-这些关系中的国家都是暂时的,例如Human1可能提高 Human2,但是一年之后,Human1可能会放弃Human2.
-所有对象均继承自BaseObject,并且具有唯一的int ID.

Note:
- State in these relations are all temporary e.g. Human1 may raise Human2, but after a year, Human1 may abandon Human2.
- All objects are inherited from BaseObject and has unique int ID.

在以上所有关系中,我希望能够支持以下功能:-
F1.添加关系例如human_dog->addRelation(Human* a,Dog* b)
F2.删除关系human_dog->removeRelation(Human* a,Dog* b)
F3.查询所有孩子human_dog->getAllChildren(Human*)
F4.查询所有父项human_dog->getAllParents(Dog*)
F5.检查父母是否有> = 1个孩子
F6.检查孩子是否有> = 1个父母
F7.删除父母的所有孩子
F8.删除一个孩子的所有父母

In all of the above relation, I want to be able to support these features :-
F1. add relation e.g. human_dog->addRelation(Human* a,Dog* b)
F2. remove relation e.g. human_dog->removeRelation(Human* a,Dog* b)
F3. query all children e.g. human_dog->getAllChildren(Human*)
F4. query all parent e.g. human_dog->getAllParents(Dog*)
F5. check whether a parent has >=1 child
F6. check whether a child has >=1 parent
F7. remove all children for a parent
F8. remove all parent for a child

这可以通过std::unordered_map或更容易定制的方式来实现.

This can be implemented by std::unordered_map or something more customized quite easily.

我想将关系1#,2#,3#(即所有实线)标记为 Feed .
它必须以 聚集 样式支持功能F3-F8.

I want to mark relation 1#,2#,3# (i.e. all solid lines) as Feed.
It has to support feature F3-F8 in an aggregating style.

例如:-

  • feed->getAllChildren(BaseObject* b):
    如果b是人类,则必须返回b的所有抚养耕种的孩子.
  • feed->removeAllParent(BaseObject* b):
    如果b是狗,它的作用类似于cultivate->removeAllParent(b).
  • feed->getAllChildren(BaseObject* b) :
    If b is human, it must return all children of raise,has and cultivate of the b.
  • feed->removeAllParent(BaseObject* b) :
    If b is a dog, it will effect like cultivate->removeAllParent(b).

总而言之,我希望能够轻松注入这样的 聚合 .
前任.调用:-

In summary, I want to be able to easily inject such aggregation.
Ex. It is useful to call :-

void BaseObject::declareForFreedom(){
    feed->removeAllParent(this);
}

上面的示例仅显示4个关系和1个间接级别.
在我的真实情况下,这种继承/间接关系有8-10个关系和3-4个级别.

The above example shows only 4 relations and 1 level of indirection.
In my real case, there are 8-10 relations and 3-4 levels of such inherit/indirection.

什么是适合这种情况的数据结构/设计模式?

What is a data-structure/design-pattern that suitable for this case?

我目前为1#-4#创建一个自定义的1:N关系,并对每个 feed 函数进行硬编码.这很乏味.
我已经猛烈抨击了几个月,但没有发现任何看起来优雅的实现.

I currently create a custom 1:N relation for 1#-4#, and hard-code every feed's function. It is tedious.
I have banged by head for a few months, but not found any implementation that look elegant.

http://coliru.stacked-crooked.com/a/1f2decd7a8d96e3c

基本类型:-

#include <iostream>
#include <map>
#include <vector>
enum class Type{
    HUMAN,DOG,TREE,PARASITE,ERROR
}; //for simplicity
class BaseObject{public: Type type=Type::ERROR; };
class Human : public BaseObject{
    public: Human(){ type=Type::HUMAN; }    
};
class Dog : public BaseObject{
    public: Dog(){ type=Type::DOG; }    
};
class Tree : public BaseObject{
    public: Tree(){ type=Type::TREE; }    
};
class Parasite : public BaseObject{
    public: Parasite(){ type=Type::PARASITE; }    
};

基本的1:N地图

template<class A,class B> class MapSimple{
    std::multimap<A*, B*> aToB;
    std::multimap<B*, A*> bToA;
    public: void addRelation(A* b1,B* b2){
        aToB.insert ( std::pair<A*,B*>(b1,b2) );   
        bToA.insert ( std::pair<B*,A*>(b2,b1) );   
    }
    public: std::vector<B*> queryAllChildren(A* b1){
        auto ret = aToB.equal_range(b1);
        auto result=std::vector<B*>();
        for (auto it=ret.first; it!=ret.second; ++it){
            result.push_back(it->second);
        }
        return result;
    }
    public: void removeAllParent(B* b){
        if(bToA.count(b)==0)return;
        A* a=bToA.find(b)->second;
        bToA.erase(b);
        auto iterpair = aToB.equal_range(a);
        auto it = iterpair.first;
        for (; it != iterpair.second; ++it) {
            if (it->second == b) { 
                aToB.erase(it);
                break;
            }
        }
    }
    //.. other functions 
};

这是数据库实例和聚合:-

Here is the database instance and the aggregation :-

MapSimple<Human,Human> raise;
MapSimple<Human,Dog> has;
MapSimple<Human,Tree> cultivate;
MapSimple<Dog,Parasite> isHouseOf;
class Feed{
    public: void removeAllParent(BaseObject* b1){
        if(b1->type==Type::HUMAN){
            raise.removeAllParent(static_cast<Human*>(b1));
        } 
        if(b1->type==Type::DOG){
            has.removeAllParent(static_cast<Dog*>(b1));
        }
        //.... some other condition (I have to hard code them - tedious) ...
    }
    //other function 
};
Feed feed;

用法

int main(){
    Human h1;
    Dog d1,d2;

    has.addRelation(&h1,&d1);
    has.addRelation(&h1,&d2);
    auto result=has.queryAllChildren(&h1);
    std::cout<<result.size(); //print 2
    feed.removeAllParent(&d1);
    result=has.queryAllChildren(&h1);
    std::cout<<result.size(); //print 1
}

推荐答案

编辑

Jarod42 建议使用更好的代码

EDIT

Better code was suggested by Jarod42 in this topic. C++17 style:

#include <algorithm>
#include <tuple>
#include <vector>

class BaseObject {
public:
    virtual ~BaseObject() = default;
    virtual std::vector<BaseObject*> getAllParents() const = 0;
    virtual std::vector<BaseObject*> getAllChildren() const = 0;
    virtual void removeAllParents() = 0;
    virtual void removeAllChildren() = 0;
};

template<typename TParentTuple, typename TChilderenTuple>
class Obj;

template<typename... ParentTags,
         typename... ChildTags>
class Obj<std::tuple<ParentTags...>, std::tuple<ChildTags...>> : public BaseObject
{
    std::tuple<std::vector<typename ParentTags::obj_type*>...> parents;
    std::tuple<std::vector<typename ChildTags::obj_type*>...> children;

public:

    template <typename T>
    void addParent(T* parent) { std::get<std::vector<T*>>(parents).push_back(parent); }

    template <typename T>
    void removeParent(const T* parent) {
        auto& v = std::get<std::vector<T*>>(parents);
        auto it = std::find(std::cbegin(v), std::cend(v), parent);
        if (it != std::cend(v)) { v.erase(it); }
    }

    template <typename T>
    void addChild(T* child) { std::get<std::vector<T*>>(children).push_back(child); }

    template <typename T>
    void removeChild(const T* child) {
        auto& v = std::get<std::vector<T*>>(children);
        auto it = std::find(std::cbegin(v), std::cend(v), child);
        if (it != std::cend(v)) { v.erase(it); }
    }

    std::vector<BaseObject*> getAllParents() const override {
        std::vector<BaseObject*> res;

        std::apply([&](auto&... v){ (res.insert(res.end(), v.begin(), v.end()), ...); },
                   parents);
        return res;
    }
    std::vector<BaseObject*> getAllChildren() const override {
        std::vector<BaseObject*> res;

        std::apply([&](auto&... v){ (res.insert(res.end(), v.begin(), v.end()), ...); },
                   children);
        return res;
    }

    void removeAllParents() override {
        std::apply(
            [this](auto&... v)
            {
                [[maybe_unused]] auto clean = [this](auto& v) {
                    for (auto* parent : v) {
                        parent->removeChild(this);
                    }
                    v.clear();
                };
                (clean(v), ...);
            },
            parents);
    }

    void removeAllChildren() override {
        std::apply(
            [this](auto&... v)
            {
                [[maybe_unused]] auto clean = [this](auto& v) {
                    for (auto* child : v) {
                        child->removeParent(this);
                    }
                    v.clear();
                };
                ( clean(v), ...);
            },
            children);
    }
};

struct Human_tag;
struct Tree_tag;
struct Dog_tag;
struct Parasite_tag;

using Human = Obj<std::tuple<>, std::tuple<Tree_tag, Dog_tag>>;
using Tree = Obj<std::tuple<Human_tag>, std::tuple<>>;
using Dog = Obj<std::tuple<Human_tag>, std::tuple<Parasite_tag>>;
using Parasite = Obj<std::tuple<Dog_tag>, std::tuple<>>;

struct Human_tag { using obj_type = Human; };
struct Tree_tag { using obj_type = Tree; };
struct Dog_tag { using obj_type = Dog; };
struct Parasite_tag { using obj_type = Parasite; };

template<class A, class B>
void addRelation(A* a, B* b)
{
    a->addChild(b);
    b->addParent(a);
}

#include <iostream>
int main() {
    Human h1;
    Dog d1, d2;

    addRelation(&h1, &d1);
    addRelation(&h1, &d2);
    auto result = h1.getAllChildren();
    std::cout << result.size() << "\n"; //print 2
    d1.removeAllParents();
    result = h1.getAllChildren();
    std::cout << result.size() << "\n"; //print 1
}

旧代码:(我的尝试)

好的,因为您不想重复的代码,所以我一直将此项目作为元编程/可变模板的初次经验.这就是我得到的:

Old code: (my attempt)

OK, since you did not want duplicated code, I've been using this project as my first experience with metaprogramming/variadic templating. So this is what I got:

#include <tuple>
#include <vector>
#include <algorithm>

template<class T>
using prtVector = std::vector<T*>;

// Interface, as required by assignment
class BaseObject {
public:
    virtual ~BaseObject() {}
    virtual prtVector<BaseObject> getAllParents() const = 0;
    virtual prtVector<BaseObject> getAllChildren() const = 0;
    virtual void removeAllParents() = 0;
    virtual void removeAllChildren() = 0;
};

// base prototype
template<typename TOwnTag, typename TParentTagsTuple, typename TChildTagsTuple>
class Obj;

// Parent-type deduction
template<typename TOwnTag, typename TParentTag, typename... TParentTags, typename... TChildTags>
class Obj<TOwnTag, std::tuple<TParentTag, TParentTags...>, std::tuple<TChildTags...>>
    : public Obj<TOwnTag, std::tuple<TParentTags...>, std::tuple<TChildTags...>>
{
    // local types
    using TOwn = typename TOwnTag::obj_type;
    using TParent = typename TParentTag::obj_type;
    // container
    prtVector<TParent> parentsPtrs;
    //befriend types
    friend class Obj;
    template<class A, class B>
    friend void addRelation(A* const a, B* const b);
protected:
    // prevent base function hiding with 'using'-declaration
    using Obj<TOwnTag, std::tuple<TParentTags...>, std::tuple<TChildTags...>>::addParent;
    using Obj<TOwnTag, std::tuple<TParentTags...>, std::tuple<TChildTags...>>::removeParent;
    // add and remove element functions
    void addParent(TParent* const parentPtr) { parentsPtrs.push_back(parentPtr); }
    void removeParent(TParent const* const parentPtr) {
        auto it = std::find(std::cbegin(parentsPtrs), std::cend(parentsPtrs), parentPtr);
        if (it != std::cend(parentsPtrs)) parentsPtrs.erase(it);
    }
public:
    virtual ~Obj() {}
    virtual prtVector<BaseObject> getAllParents() const override {
        auto result = Obj<TOwnTag, std::tuple<TParentTags...>, std::tuple<TChildTags...>>::getAllParents();
        result.insert(std::begin(result), std::cbegin(parentsPtrs), std::cend(parentsPtrs));
        return result;
    }
    virtual prtVector<BaseObject> getAllChildren() const override {
        return Obj<TOwnTag, std::tuple<TParentTags...>, std::tuple<TChildTags...>>::getAllChildren();
    }
    virtual void removeAllParents() override {
        Obj<TOwnTag, std::tuple<TParentTags...>, std::tuple<TChildTags...>>::removeAllParents();
        for (auto&& parent : parentsPtrs) parent->removeChild(reinterpret_cast<TOwn* const>(this));
    }
    virtual void removeAllChildren() override {
        Obj<TOwnTag, std::tuple<TParentTags...>, std::tuple<TChildTags...>>::removeAllChildren();
    }
};

// Child-type deduction
template<typename TOwnTag, typename TChildTag, typename... TChildTags>
class Obj<TOwnTag, std::tuple<>, std::tuple<TChildTag, TChildTags...>>
    : public Obj<TOwnTag, std::tuple<>, std::tuple<TChildTags...>>
{
    // local types
    using TOwn = typename TOwnTag::obj_type;
    using TChild = typename TChildTag::obj_type;
    // container
    prtVector<TChild> childrenPtrs;
    //befriend types
    friend class Obj;
    template<class A, class B>
    friend void addRelation(A* const a, B* const b);
protected:
    // empty functions required for 'using'-declaration
    void addParent() {}
    void removeParent() {}
    // prevent base function hiding with 'using'-declaration
    using Obj<TOwnTag, std::tuple<>, std::tuple<TChildTags...>>::addChild;
    using Obj<TOwnTag, std::tuple<>, std::tuple<TChildTags...>>::removeChild;
    // add and remove element functions
    void addChild(TChild* const childPtr) { childrenPtrs.push_back(childPtr); }
    void removeChild(TChild const* const childPtr) {
        auto it = std::find(std::cbegin(childrenPtrs), std::cend(childrenPtrs), childPtr);
        if (it != std::cend(childrenPtrs)) childrenPtrs.erase(it);
    }
public:
    virtual ~Obj() {}
    virtual prtVector<BaseObject> getAllParents() const override {
        return Obj<TOwnTag, std::tuple<>, std::tuple<TChildTags...>>::getAllParents();
    }
    virtual prtVector<BaseObject> getAllChildren() const override {
        auto result = Obj<TOwnTag, std::tuple<>, std::tuple<TChildTags...>>::getAllChildren();
        result.insert(std::begin(result), std::cbegin(childrenPtrs), std::cend(childrenPtrs));
        return result;
    }
    virtual void removeAllParents() override {}
    virtual void removeAllChildren() override {
        Obj<TOwnTag, std::tuple<>, std::tuple<TChildTags...>>::removeAllChildren();
        for (auto&& child : childrenPtrs) child->removeParent(reinterpret_cast<TOwn* const>(this));
    }
};

// terminator
template<typename TOwnTag>
class Obj<TOwnTag, std::tuple<>, std::tuple<>> : public BaseObject {
protected:
    // empty functions required for 'using'-declaration
    void addChild() {}
    void removeChild() {}
    void addParent() {}
    void removeParent() {}
public:
    virtual ~Obj() {}
    virtual prtVector<BaseObject> getAllParents() const override {
        return prtVector<BaseObject>();
    }
    virtual prtVector<BaseObject> getAllChildren() const override {
        return prtVector<BaseObject>();
    }
    virtual void removeAllParents() override {}
    virtual void removeAllChildren() override {}
};

//prototype class tags
struct Human_tag;
struct Tree_tag;
struct Dog_tag;
struct Parasite_tag;
//define class types
using Human = Obj<Human_tag, std::tuple<>, std::tuple<Tree_tag, Dog_tag>>;
using Tree = Obj<Tree_tag, std::tuple<Human_tag>, std::tuple<>>;
using Dog = Obj<Dog_tag, std::tuple<Human_tag>, std::tuple<Parasite_tag>>;
using Parasite = Obj<Parasite_tag, std::tuple<Dog_tag>, std::tuple<>>;
//couple tags to classes
struct Human_tag { using obj_type = Human; };
struct Tree_tag { using obj_type = Tree; };
struct Dog_tag { using obj_type = Dog; };
struct Parasite_tag { using obj_type = Parasite; };

//(befriend)helper function
// maybe could do somehting with std::enable_if
// i.e. "enable if type B is in child tuple of A and
//  type A is in parent tuple of B"
// that way the parser will already detect a relation is not possible
template<class A, class B>
void addRelation(A* const a, B* const b)
{
    a->addChild(b);
    b->addParent(a);
}

// now for some testing
#include <iostream>
int main() {
    Human h1;
    Dog d1, d2;
    Parasite p1;

    addRelation(&h1, &d1);
    addRelation(&h1, &d2);
    addRelation(&d1, &p1);
    //addRelation(&h1, &p1); // compiler error
    auto result = h1.getAllChildren();
    std::cout << result.size() << "\n"; //print 2
    d1.removeAllParents();
    result = h1.getAllChildren();
    std::cout << result.size() << "\n"; //print 1

    std::cin.ignore();
}

请问任何不清楚的问题,因为在过去的24小时里我一直在学习很多新东西,所以我不知道从哪里开始我的解释.

Please ask questions about anything that is unclear, because I've been learning so much new stuff over the past 24 hours, that I don't know where to begin with my explanation.

这篇关于“继承"关系的关系另一个(1:N)关系的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆