我在C#(或Java,无关紧要)中实现了一个简单的树结构,其中一切都围绕着抽象类Node及其一些子类。Node提供了与数学图论相关的方法和属性。例如,Node.Parent 引用父节点,Node.Children 引用子节点
class Node {
Node Parent;
Node[] Children;
void appendNode(Node node) { ... }
}
我正在使用树来执行计算。计算涉及大量递归,我还需要存储每个节点的中间值。对于每个计算,我都向 Node 类引入了其他属性和方法,例如
class Node {
Node Parent;
Node[] Children;
// Calculate weight
int weight; // current weight
void recalculateWeight() {
// perform some heavily recursive stuff
// involving Parent.recalculateWeight()
// and update the value of the variable weight
}
int price; // current price
void recalculatePrice() {
// perform some heavily recursive stuff
// involving Parent.recalculatePrice()
// and update the value of the variable price
}
void appendNode(Node node) {
// ...
recalculateWeight();
recalculatePrice();
}
}
但现在我不得不放弃这种方法,因为计算值应该动态添加而不更改 Node 类。 动态意味着其他人应该能够在给定的树上实现自己的计算,仅依靠 Node 类的"图论方法"。
你知道什么是好的设计模式吗?
这尖叫着访客模式。
interface Visitor{
visit(Node node);
}
class Node{
//...
void accept(Visitor v){
//feel free to change visit order to viist children first
v.visit(this);
for(Node child : children){
v.visit(child);
}
}
}
然后,您可以将所有不同的计算设置为不同的访客。 创建新类型的计算或遍历根本不会更改Node
类。 您只需进行一个新的访客实现。
class WeightVisitor implements Visitor{
int weight = 0;
void visit(Node n){
weight += ...
}
}
然后每次你想计算体重
WeightVisitor visitor = new WeightVisitor();
rootNode.accept(visitor);
int weight = visitor.getWeight();
这取决于您要实现的目标。有两种不同的方法可以使用节点图:
-
节点只是数据的容器,节点库的用户可以使用它们来存储数据和计算各种内容。这类似于允许您在节点中存储数据、遍历列表、删除节点等的
LinkedList
,甚至是递归的。但这不需要对节点本身进行任何更改。这些方法完全由客户负责在外部实现。您的节点应该只提供以各种方式迭代它们的方法。并且可能提供一种类型安全(即像List<Type>
一样通用)的方式来存储自定义数据。 -
您正在为用户定义的节点提供图形。用户将以自定义方式扩展
Node
类以执行自定义操作。用户仍将使用您提供的图形函数,例如您可以提供的Tree.rebalance(Node root)
方法。您的节点类和其他方法应该允许的是使扩展变得容易。
例如,您应该考虑使类和其他方法成为通用方法,以便用户可以完全使用自定义子类型
// That's what you provide. Has all the base functionality of a node
class Node<T extends Node<T>> {
private T parent;
private List<T> children;
public void setParent(T parent) {
this.parent = parent;
}
public T getParent() {
return this.parent;
}
// ...
}
// that's what the user can do with it
class WeightedNode extends Node<WeightedNode> {
public int weight = 5;
public void update() {
WeightedNode parent = getParent();
// ^^ Typesafe access to WeightedNode!
this.weight = parent.weight + 1;
parent.update();
}
}
class User {
void use() {
WeightedNode node = new WeightedNode();
node.update();
}
}