简体   繁体   English

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

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

I'm developing a system which generates a graph network in memory, and solve the shortest path problem using Dijkstra's algorithm. 我正在开发一个在内存中生成图形网络的系统,并使用Dijkstra的算法解决最短路径问题。 There are two classes that are referring each other. 有两个相互引用的类。 The first one is 第一个是

// 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_;
};

And the other is... 另一个是...

// 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_;
};

Yes, edges are referring vertices, vertices are referring edges. 是的,边是指顶点,顶点是指边。 I think this is natural. 我认为这很自然。

The problem is that these codes cannot be compiled, because including circular dependency. 问题在于这些代码无法编译,因为其中包括循环依赖性。 It is said here that system designs containing circular dependency is wrong, but I can't finally find out what's wrong with it. 据说这里包含循环依赖该系统的设计是错误的,但我无法终于找出什么地方错了。

Is it wrong to use closure-compiler to develop applications that can solve the shortest path problem with JavaScript? 使用闭包编译器开发可以解决JavaScript最短路径问题的应用程序是否错误? Are there alternative solutions? 有替代解决方案吗?

edges are referring vertices, vertices are referring edges. 边是参照顶点,顶点是参照边。 I think this is natural. 我认为这很自然。

This is a flawed assumption. 这是一个错误的假设。 It is not natural to have vertices pointing to their edges and also have edges pointing to their vertices. 顶点指向其边缘并且边缘指向其顶点是自然的。 One should reference the other, not both of them referencing each other. 一个应该引用另一个,而不是两个都互相引用。 Having duplicate referential responsibilities can cause all sorts of problems. 具有重复的引用职责可能会导致各种问题。 For example, if they ever get out of sync, you won't have a source of truth from which to rebuild your graph. 例如,如果它们不同步,则您将没有来源来重建图表。

To avoid circular dependencies, you need to have one "owner" of the relationship. 为了避免循环依赖,您需要一个关系的“所有者”。 In your particular example, edges referencing vertices strikes me as the more obvious "owner," like so: 在您的特定示例中,引用顶点的边将我视为更明显的“所有者”,如下所示:

// 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);
};

Then your edge ... 那你的优势...

// 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_;
};

Then your graph ... 然后你的图...

/**
 * @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); });
 };

Warning: I didn't test this -- it's something I hacked up over a bottle of some nice Puerto Rican rum . 警告:我没有对此进行测试-这是我为一瓶不错的波多黎各朗姆酒调制的东西 I may (read: probably) have something messed up here or there. 我可能在这里或那里弄乱了(读:可能)。 Regardless, the concept stands. 无论如何,这个概念依然存在。 This shows that circular dependencies are not a necessary (nor, to be honest, desirable) requirement for a given language or technology in order to answer questions about graphs. 这表明,对于回答关于图形的问题,对于给定的语言或技术,循环依赖性不是必需的(老实说,也是可取的)要求。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM