如何避免 Google 闭包库/编译器中的循环依赖错误
How to avoid circular-dependency errors in Google Closure Library/Compiler
我正在开发一个系统,该系统在内存中生成图形网络,并使用Dijkstra的算法解决最短路径问题。有两个类相互引用。第一个是
// edge.js
goog.provide('Edge');
goog.require('Vertex');
/**
* @constructor
*/
Edge = function(){
this.vertices_ = [];
};
/**
* @param {Vertex} vertex
*/
Edge.prototype.addVertex(vertex){
this.vertices_.push(vertex);
};
/**
* @return {Array.<Vertex>}
*/
Edge.prototype.getVertices(){
return this.vertices_;
};
另一个是...
// vertex.js
goog.provide('Vertex');
goog.require('Edge');
/**
* @constructor
*/
Vertex = function(){
this.edges_ = [];
};
/**
* @param {Edge} edge
*/
Vertex.prototype.addEdge(edge){
this.edges_.push(edge);
};
/**
* @return {Array.<Edge>}
*/
Vertex.prototype.getAllEdges(){
return this.edges_;
};
是的,边是引用顶点,顶点是引用边。我认为这是很自然的。
问题是这些代码无法编译,因为包括循环依赖。这里说包含循环依赖的系统设计是错误的,但我最终无法找出它的问题所在。
使用闭包编译器来开发可以解决JavaScript最短路径问题的应用程序是错误的吗?有没有替代解决方案?
边是参照顶点,顶点是参照边。我认为这是很自然的。
这是一个有缺陷的假设。顶点指向其边,也有边指向其顶点是不自然的。一个应该引用另一个,而不是两个都相互引用。重复的引用责任可能会导致各种问题。例如,如果它们不同步,您将没有用于重建图形的事实来源。
若要避免循环依赖,需要有一个关系的"所有者"。在您的特定示例中,引用顶点的边使我感到更明显的"所有者",如下所示:
// vertex.js
goog.provide('Vertex');
goog.require('goog.math.Coordinate');
/**
* @constructor
* @param {number=} opt_x x-position, defaults to 0
* @param {number=} opt_y y-position, defaults to 0
* @extends {goog.math.Coordinate}
*/
Vertex = function(opt_x, opt_y) {
goog.base(this, opt_x, opt_y);
};
然后你的优势...
// edge.js
goog.provide('Edge');
goog.require('Vertex');
goog.require('goog.math.Coordinate');
/**
* @constructor
* @param {Vertex} start
* @param {Vertex} end
*/
Edge = function(start, end) {
/**
* @type {Vertex}
* @private
*/
this.start_ = start;
/**
* @type {Vertex}
* @private
*/
this.end_ = end;
};
/**
* @return {number}
*/
Edge.prototype.getDistance = function() {
goog.math.Coordinate.distance(this.start_, this.end_);
};
/**
* Checks if this Edge connects to the referenced vertex.
* @param {Vertex} vertex
* @return {bool}
*/
Edge.prototype.connects = function(vertex) {
return this.start_ === vertex || this.end_ === vertex;
};
/**
* @return {Vertex}
*/
Edge.prototype.getStart = function() {
return this.start_;
};
/**
* @return {Vertex}
*/
Edge.prototype.getEnd = function() {
return this.end_;
};
然后你的图表...
/**
* @param {Array.<Edge>} edges
* @constructor
*/
Graph = function(edges) {
/**
* @type {Array.<Edge>}
* @private
*/
this.edges_ = edges;
};
/**
* @param {Vertex} start
* @param {Vertex} end
* @return {number|undefined} shortest path between {@code start} and {@code end},
* with 'undefined' being returned if a path does not exist.
*/
Graph.prototype.getShortestPath = function(start, end) {
return this.getShortestPath_(start, end, 0, []);
}
/**
* @param {Vertex} start
* @param {Vertex} end
* @param {number} traveled amount traveled thus far
* @param {Array.<Vertex>} path the route taken thus far
* @return {number|undefined}
* @private
*/
Graph.prototype.getShortestPath_ = function(start, end, traveled, path) {
if (start == end) {
return traveled + 0;
}
var distances = goog.array.map(
this.getEdgesToTry_(start, path),
function (edge) {
var nextStart;
if (edge.getStart() === start) {
nextStart = edge.getEnd();
} else {
nextStart = edge.getStart();
}
return this.getShortestPath_(
newStart,
end,
edge.getDistance() + traveled,
goog.array.concat(path, start)
);
},
this
);
return goog.array.reduce(
distances,
function (shortestDistance, distance) {
if (distance !== undefined) {
if (shortestDistance === undefined) {
return distance;
} else {
return Math.min(shortestDistance, distance);
}
} else {
return shortestDistance;
}
},
undefined
);
};
/**
* @param {Vertex} vertex
* @param {Array.<Vertex>} path
* @return {Array.<Vertex>}
* @private
*/
Graph.prototype.getEdgesToTry_ = function(vertex, path) {
return goog.array.filter(
this.edges_,
function (edge) { return this.isEdgeToTry_(edge, vertex, path); },
this
);
};
/**
* @param {Edge} edge
* @param {Vertex} start
* @param {Array.<Vertex>} path
* @return {bool}
* @private
*/
Graph.prototype.isEdgeToTry_ = function(edge, start, path) {
return edge.connects(start)
&& goog.array.every(
path, function(vertex) { return !edge.connects(vertex); });
};
警告:我没有测试这个 - 这是我在一瓶美味的波多黎各朗姆酒上砍掉的东西。我可能(阅读:可能)在这里或那里搞砸了一些东西。无论如何,这个概念是成立的。这表明循环依赖不是给定语言或技术的必要(老实说,也不是可取的)要求,以回答有关图的问题。
相关文章:
- FRP 中 EventStreams 的循环依赖关系
- GraphQL代码中的Javascript循环依赖关系
- 通过JavaScript构造函数引入循环依赖关系
- RequireJS,循环依赖和导出“魔术”方法
- 使用 requireJs 的模块中的循环依赖关系
- CommonJS singelton模块中的循环依赖关系
- 如何避免 Google 闭包库/编译器中的循环依赖错误
- 如何处理 React 嵌套组件循环依赖?(使用 es6 类)
- AngularJS中的循环依赖和OOP问题
- 如何正确解决需求中的循环依赖.js
- 角度相互依赖的服务:避免循环依赖
- 在拦截器中使用“$mdToast”触发循环依赖
- 视图模型内存泄漏 - 摆脱循环依赖
- 如何在没有循环依赖的模型之间正确共享Hapi-Joi验证模式
- 如何在Backbone.js中使用嵌套视图解决循环依赖关系
- 处理循环依赖
- Javascript循环依赖
- 在单独的文件中考虑模型:如何处理循环/循环依赖关系
- 通过Node.js require和CoffeeScript中的类解决循环依赖
- Angular-使用ngNotificationsBar时发现循环依赖关系