You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

309 lines
9.0 KiB
C++

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#include <iostream>
#include <vector>
#include <memory>
#include <mutex>
#include <algorithm>
#include <stdexcept>
#include <type_traits>
class INode;
class ILink;
class ILinkMixin;
using NodePtr = std::shared_ptr<INode>;
using LinkPtr = std::shared_ptr<ILink>;
using LinkMixinPtr = std::shared_ptr<ILinkMixin>;
class ILink {
public:
virtual ~ILink() = default;
virtual LinkPtr getParent() const = 0;
virtual void setParent(LinkPtr parent) = 0;
virtual const std::vector<LinkPtr>& getChildren() const = 0;
virtual void addChild(LinkPtr child) = 0;
virtual void removeChild(LinkPtr child) = 0;
virtual NodePtr getNode() const = 0;
virtual std::mutex& getLock() = 0;
virtual LinkMixinPtr getLinkMixin() const = 0;
};
class ILinkMixin {
public:
virtual ~ILinkMixin() = default;
virtual void linkChild(const NodePtr& child) = 0;
virtual void unlinkParent() = 0;
virtual void unlinkChild(const NodePtr& child) = 0;
virtual LinkPtr getLink() const = 0;
virtual NodePtr getNode() const = 0;
};
class INode {
public:
virtual ~INode() = default;
virtual LinkPtr getLink() const = 0;
virtual LinkMixinPtr getLinkMixin() const = 0;
};
class LeafLink : public ILink {
public:
explicit LeafLink(NodePtr node) : node_(std::move(node)) {}
LinkPtr getParent() const override { return parent_; }
void setParent(LinkPtr parent) override { parent_ = std::move(parent); }
const std::vector<LinkPtr>& getChildren() const override {
static std::vector<LinkPtr> empty;
return empty;
}
void addChild(LinkPtr) override {
throw std::logic_error("LeafLink cannot have children");
}
void removeChild(LinkPtr) override {}
NodePtr getNode() const override { return node_; }
std::mutex& getLock() override { return lock_; }
LinkMixinPtr getLinkMixin() const override { return node_->getLinkMixin(); }
private:
NodePtr node_;
LinkPtr parent_;
mutable std::mutex lock_;
};
class OneToManyLink : public ILink {
public:
explicit OneToManyLink(NodePtr node) : node_(std::move(node)) {}
LinkPtr getParent() const override { return parent_; }
void setParent(LinkPtr parent) override { parent_ = std::move(parent); }
const std::vector<LinkPtr>& getChildren() const override { return children_; }
void addChild(LinkPtr child) override { children_.push_back(std::move(child)); }
void removeChild(LinkPtr child) override {
children_.erase(std::remove(children_.begin(), children_.end(), child), children_.end());
}
NodePtr getNode() const override { return node_; }
std::mutex& getLock() override { return lock_; }
LinkMixinPtr getLinkMixin() const override { return node_->getLinkMixin(); }
private:
NodePtr node_;
LinkPtr parent_;
std::vector<LinkPtr> children_;
mutable std::mutex lock_;
};
class OneToOneLink : public ILink {
public:
explicit OneToOneLink(NodePtr node) : node_(std::move(node)) {}
LinkPtr getParent() const override { return parent_; }
void setParent(LinkPtr parent) override { parent_ = std::move(parent); }
const std::vector<LinkPtr>& getChildren() const override { return singleChildVec_; }
void addChild(LinkPtr child) override {
if (!singleChildVec_.empty()) {
throw std::logic_error("OneToOneLink already has a child");
}
singleChildVec_.push_back(std::move(child));
}
void removeChild(LinkPtr child) override {
if (!singleChildVec_.empty() && singleChildVec_.front() == child) {
singleChildVec_.clear();
}
}
NodePtr getNode() const override { return node_; }
std::mutex& getLock() override { return lock_; }
LinkMixinPtr getLinkMixin() const override { return node_->getLinkMixin(); }
private:
NodePtr node_;
LinkPtr parent_;
std::vector<LinkPtr> singleChildVec_;
mutable std::mutex lock_;
};
// Типизированная связь один ко многим
template <typename TChild>
class TypedOneToManyLink : public OneToManyLink {
public:
explicit TypedOneToManyLink(NodePtr node) : OneToManyLink(std::move(node)) {}
void addChild(LinkPtr child) override {
NodePtr n = child->getNode();
if (!std::dynamic_pointer_cast<TChild>(n)) {
throw std::invalid_argument("TypedOneToManyLink: child is not of expected type");
}
OneToManyLink::addChild(std::move(child));
}
};
// Базовая реализация LinkMixin
class BaseLinkMixin : public ILinkMixin {
public:
explicit BaseLinkMixin(NodePtr self) {
link_ = std::make_shared<OneToManyLink>(self);
}
void linkChild(const NodePtr& child) override {
LinkPtr childLink = child->getLink();
childLink->setParent(link_);
link_->addChild(childLink);
}
void unlinkParent() override {
if (!link_ || !link_->getParent()) return;
auto parent = link_->getParent();
parent->getLinkMixin()->unlinkChild(link_->getNode());
link_->setParent(nullptr);
}
void unlinkChild(const NodePtr& child) override {
LinkPtr childLink = child->getLink();
link_->removeChild(childLink);
childLink->setParent(nullptr);
}
LinkPtr getLink() const override { return link_; }
NodePtr getNode() const override { return link_->getNode(); }
protected:
LinkPtr link_;
};
// Ленивый LinkMixin с выбором стратегии по типу
template <typename TNode>
class LazyLinkMixin : public ILinkMixin {
public:
explicit LazyLinkMixin(NodePtr self) {
link_ = std::make_shared<LeafLink>(self);
}
void linkChild(const NodePtr& child) override {
ensureInitialized(child);
LinkPtr childLink = child->getLink();
childLink->setParent(link_);
link_->addChild(childLink);
}
void unlinkParent() override {
if (!link_ || !link_->getParent()) return;
auto parent = link_->getParent();
parent->getLinkMixin()->unlinkChild(link_->getNode());
link_->setParent(nullptr);
if (link_->getChildren().empty()) {
link_ = std::make_shared<LeafLink>(link_->getNode());
}
}
void unlinkChild(const NodePtr& child) override {
if (!link_) return;
LinkPtr childLink = child->getLink();
link_->removeChild(childLink);
childLink->setParent(nullptr);
if (link_->getChildren().empty()) {
link_ = std::make_shared<LeafLink>(link_->getNode());
}
}
LinkPtr getLink() const override { return link_; }
NodePtr getNode() const override { return link_->getNode(); }
private:
void ensureInitialized(const NodePtr& child) {
if (link_ && !std::dynamic_pointer_cast<LeafLink>(link_)) return;
LinkPtr oldLink = link_;
LinkPtr newLink;
if (std::dynamic_pointer_cast<TNode>(child)) {
newLink = std::make_shared<TypedOneToManyLink<TNode>>(oldLink->getNode());
} else {
newLink = std::make_shared<OneToOneLink>(oldLink->getNode());
}
newLink->setParent(oldLink->getParent());
link_ = newLink;
}
LinkPtr link_;
};
// Пример узла
class SimpleNode : public INode, public std::enable_shared_from_this<SimpleNode> {
public:
static std::shared_ptr<SimpleNode> create(std::string name) {
struct EnableMakeShared : public SimpleNode {
EnableMakeShared(std::string n) : SimpleNode(std::move(n)) {}
};
auto ptr = std::make_shared<EnableMakeShared>(std::move(name));
ptr->initMixin();
return ptr;
}
const std::string& name() const { return name_; }
LinkPtr getLink() const override { return mixin_->getLink(); }
LinkMixinPtr getLinkMixin() const override { return mixin_; }
void linkChild(const std::shared_ptr<SimpleNode>& child) { mixin_->linkChild(child); }
void unlinkParent() { mixin_->unlinkParent(); }
private:
explicit SimpleNode(std::string name) : name_(std::move(name)) {}
void initMixin() {
mixin_ = std::make_shared<LazyLinkMixin<SimpleNode>>(shared_from_this());
}
std::string name_;
LinkMixinPtr mixin_;
};
// Пример использования
void printTree(const LinkPtr& link, int indent = 0) {
for (int i = 0; i < indent; ++i) std::cout << " ";
auto node = link->getNode();
auto simpleNode = std::dynamic_pointer_cast<SimpleNode>(node);
if (simpleNode) {
std::cout << simpleNode->name() << "\n";
}
for (const auto& child : link->getChildren()) {
printTree(child, indent + 1);
}
}
int main() {
auto root = SimpleNode::create("Root");
auto child1 = SimpleNode::create("Child1");
auto child2 = SimpleNode::create("Child2");
root->linkChild(child1);
root->linkChild(child2);
auto subchild = SimpleNode::create("SubChild");
child1->linkChild(subchild);
printTree(root->getLink());
std::cout << "\nUnlinking Child2...\n";
child2->unlinkParent();
printTree(root->getLink());
return 0;
}