导读
本章节用TypeScript来实现上一章【用TypeScript实现内存型图数据库】0x02:Dagoba内存型图数据库相关的类中提到的Graph
,Edge
,Vertex
三个类。
在程序设计时,先设计好哪个功能由哪个类实现,思路会更清晰,实现出来的代码也会更具备“高内聚,低耦合”的特性。
在逆向根据代码推理设计的时候也是如此,通过代码理出类图,可以对提高对整体的理解程度,加速从代码逆向理解设计。
Vertex
和Edge
首先从这两个基本元素类开始,因为Graph
类依赖这两个类,但Vertex
和Edge
不依赖Vertex
。
类图
这两个类的类图为:
代码实现
对应的TS代码实现为:
/**
* 边
*/
class Edge {
inVertex: Vertex | undefined;
outVertex: Vertex | undefined;
}
/**
* 顶点
*/
class Vertex {
id: string;
inEdges: Edge[] = [];
outEdges: Edge[] = [];
constructor(id: string) {
this.id = id;
}
}
另外还有其对应的函数参数,在后面Graph
的方法中会用到:
// 顶点参数
type VertexParam = { id: string };
// 边参数
type EdgeParam = { in: string, out: string };
Graph
Graph
对应的类图为
但本节我们仅实现以下有关图构建的属性和方法:
代码实现
/**
* 图
*/
class Graph {
edges: Edge[] = [];
vertices: Vertex[] = [];
vertexIndex: Map<string, Vertex> = new Map();
autoId: number;
public constructor(vertices: VertexParam[], edges: EdgeParam[]) {
this.addVertices(vertices);
this.addEdges(edges);
this.autoId = 1;
}
/**
* 添加顶点
* @param verticeParams 顶点列表
*/
addVertices(verticeParams: VertexParam[]): void {
verticeParams.forEach(this.addVertex.bind(this));
}
/**
* 添加边
* @param edgeParams 边参数列表
*/
addEdges(edgeParams: EdgeParam[]): void {
edgeParams.forEach(this.addEdge.bind(this));
}
/**
* 添加顶点
* @param vertexParam 顶点参数
*/
addVertex(vertexParam: VertexParam): void {
vertexParam.id = vertexParam.id ? vertexParam.id : `${this.autoId++}`;
// 如果顶点已存在,则提示异常
if (this.vertexIndex.has(vertexParam.id)) {
error('A vertex with that ID already exists');
}
const vertex: Vertex = new Vertex(vertexParam.id);
// 添加顶点进顶点列表
this.vertices.push(vertex);
// 添加顶点到检索映射中
this.vertexIndex.set(vertex.id, vertex);
}
/**
* 添加边
* @param edgeParam 边参数
*/
addEdge(edgeParam: EdgeParam): void {
const edge: Edge = new Edge();
edge.inVertex = this.findVerticeById(edgeParam.in);
edge.outVertex = this.findVerticeById(edgeParam.out);
/* 如果任一顶点不存在则返回,并输出异常信息,但不抛出异常 */
if (!(edge.inVertex && edge.outVertex)) {
error("That edge's " + (edge.inVertex ? 'out' : 'in') + " vertex wasn't found");
return;
}
// 往出方向顶点的出边列表中添加边
edge.outVertex.outEdges.push(edge);
// 往入方向顶点的出边列表中添加边
edge.inVertex.inEdges.push(edge);
this.edges.push(edge);
}
/**
* 根据顶点ID列表找到顶点列表
* @param ids 顶点ID列表
* @returns 顶点列表
*/
findVerticesByIds(ids: string[]): (Vertex | undefined)[] {
return (ids || []).map(this.findVerticeById);
}
/**
* 根据顶点ID找到顶点
* @param id 顶点ID
* @returns 顶点
*/
findVerticeById(id: string): Vertex | undefined {
return this.vertexIndex.get(id);
}
}
export {
Graph
}
用到的工具方法:
/**
* 异常输出
* @param msg 异常消息
* @returns 否
*/
function error(msg: string): boolean {
console.error(msg);
return false;
}
运行
将上述代码放到一个名为Dagoba.ts
的文件中,然后新建另一个main.ts
作为入口文件进行试运行,以下是main.ts
的代码:
import { Graph } from './Dagoba';
const graph: Graph = new Graph(
[{ id: 'grandpa' }, { id: 'grandma' }, { id: 'father' }, { id: 'mother' }],
[{ in: 'grandma', out: 'grandpa' }, { in: 'father', out: 'grandma' }, { in: 'mother', out: 'grandma' }]);
console.log(graph);
调试输出的结果,可以看到有生成了一个有4个顶点,3条边的图。