如何避免 Google 闭包库/编译器中的循环依赖错误

How to avoid circular-dependency errors in Google Closure Library/Compiler

本文关键字:循环 依赖 错误 编译器 何避免 Google 闭包      更新时间:2023-09-26

我正在开发一个系统,该系统在内存中生成图形网络,并使用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); });
 };

警告:我没有测试这个 - 这是我在一瓶美味的波多黎各朗姆酒上砍掉的东西。我可能(阅读:可能)在这里或那里搞砸了一些东西。无论如何,这个概念是成立的。这表明循环依赖不是给定语言或技术的必要(老实说,也不是可取的)要求,以回答有关图的问题。