Total Complexity | 72 |
Total Lines | 444 |
Duplicated Lines | 0 % |
Changes | 0 |
Complex classes like GraphWalk often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use GraphWalk, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
17 | class GraphWalk extends AbstractGraphPath |
||
18 | { |
||
19 | /** |
||
20 | * The edge list making up the path |
||
21 | * |
||
22 | * @var array |
||
23 | */ |
||
24 | protected $edgeList; |
||
25 | |||
26 | /** |
||
27 | * The vertex list making up the path |
||
28 | * |
||
29 | * @var array |
||
30 | */ |
||
31 | protected $vertexList; |
||
32 | |||
33 | /** |
||
34 | * The walk unique hash |
||
35 | * |
||
36 | * @var string |
||
37 | */ |
||
38 | private $hash; |
||
39 | |||
40 | /** |
||
41 | * Construct a new graph walk |
||
42 | * |
||
43 | * @param GraphPathInterface $other - the other walk |
||
44 | * |
||
45 | * @throws InvalidArgumentException |
||
46 | */ |
||
47 | 13 | public function __construct( |
|
48 | GraphInterface $graph, |
||
49 | ?VertexInterface $startVertex = null, |
||
50 | ?VertexInterface $endVertex = null, |
||
51 | ?array $vertexList = null, |
||
52 | ?array $edgeList = null, |
||
53 | ?float $weight = 0.0 |
||
54 | ) { |
||
55 | 13 | if (is_null($vertexList) && is_null($edgeList)) { |
|
56 | 1 | throw new InvalidArgumentException("Vertex list and edge list cannot both be empty!"); |
|
57 | } |
||
58 | |||
59 | if ( |
||
60 | 12 | !is_null($startVertex) |
|
61 | 12 | && !is_null($vertexList) |
|
62 | 12 | && !is_null($edgeList) |
|
63 | 12 | && (count($edgeList) + 1 != count($vertexList)) |
|
64 | ) { |
||
65 | throw new InvalidArgumentException( |
||
66 | "VertexList and edgeList do not correspond to the same path (cardinality of vertexList +1 " . |
||
67 | "must equal the cardinality of the edgeList)" |
||
68 | ); |
||
69 | } |
||
70 | |||
71 | 12 | $this->graph = $graph; |
|
72 | 12 | $this->startVertex = $startVertex; |
|
73 | 12 | $this->endVertex = $endVertex; |
|
74 | 12 | $this->weight = $weight; |
|
75 | 12 | $this->vertexList = $vertexList; |
|
76 | 12 | $this->edgeList = $edgeList; |
|
77 | 12 | $this->hash = $this->isEmpty() ? 1 : uniqid('', true); |
|
78 | 12 | } |
|
79 | |||
80 | /** |
||
81 | * Get the edges making up the path |
||
82 | * |
||
83 | * @return array |
||
84 | */ |
||
85 | 4 | public function getEdgeList(): array |
|
88 | } |
||
89 | |||
90 | /** |
||
91 | * Get the vertices making up the path |
||
92 | * |
||
93 | * @return array |
||
94 | */ |
||
95 | 5 | public function getVertexList(): array |
|
96 | { |
||
97 | 5 | return !is_null($this->vertexList) ? $this->vertexList : parent::getVertexList(); |
|
98 | } |
||
99 | |||
100 | /** |
||
101 | * Get the length of the path |
||
102 | * |
||
103 | * @return int |
||
104 | */ |
||
105 | 1 | public function getLength(): int |
|
106 | { |
||
107 | 1 | if (!is_null($this->edgeList)) { |
|
108 | 1 | return count($this->edgeList); |
|
109 | } |
||
110 | |||
111 | if (!is_null($this->vertexList)) { |
||
112 | return count($this->vertexList) - 1; |
||
113 | } |
||
114 | |||
115 | return 0; |
||
116 | } |
||
117 | |||
118 | /** |
||
119 | * Reverse the direction of the walk |
||
120 | * |
||
121 | * @return GraphPathInterface |
||
122 | * |
||
123 | * @throws InvalidArgumentException |
||
124 | */ |
||
125 | 3 | public function reverse(): GraphPathInterface |
|
188 | } |
||
189 | |||
190 | /** |
||
191 | * Concatenate the specified GraphWalk to the end of this GraphWalk |
||
192 | * |
||
193 | * @param GraphPathInterface $extension - the graph walk |
||
194 | * |
||
195 | * @return GraphPathInterface |
||
196 | */ |
||
197 | 4 | public function concat(GraphPathInterface $extension): GraphPathInterface |
|
198 | { |
||
199 | 4 | if ($this->isEmpty()) { |
|
200 | 1 | throw new InvalidArgumentException("An empty path cannot be extended"); |
|
201 | } |
||
202 | 3 | if (!$this->endVertex->equals($extension->getStartVertex())) { |
|
203 | 1 | throw new InvalidArgumentException( |
|
204 | "This path can only be extended by another path if the end vertex of the " . |
||
205 | 1 | "orginal path and the start vertex of the extension are equal." |
|
206 | ); |
||
207 | } |
||
208 | |||
209 | 2 | $concatVertexList = null; |
|
210 | 2 | $concatEdgeList = null; |
|
211 | |||
212 | 2 | if (!is_null($this->vertexList)) { |
|
213 | 2 | $concatVertexList = array_merge($this->vertexList, array_slice($extension->getVertexList() ?? [], 1)); |
|
214 | } |
||
215 | |||
216 | 2 | if (!is_null($this->edgeList)) { |
|
217 | $concatEdgeList = array_merge($this->edgeList, array_slice($extension->getEdgeList() ?? [], 1)); |
||
218 | } |
||
219 | |||
220 | 2 | $gw = new GraphWalk( |
|
221 | 2 | $this->graph, |
|
222 | 2 | $this->startVertex, |
|
223 | 2 | $extension->getEndVertex(), |
|
224 | 2 | $concatVertexList, |
|
225 | 2 | $concatEdgeList, |
|
226 | 2 | $this->weight + $extension->getWeight() |
|
227 | ); |
||
228 | 2 | return $gw; |
|
229 | } |
||
230 | |||
231 | /** |
||
232 | * Verify that the given path is feasible |
||
233 | * |
||
234 | * @return bool |
||
235 | * |
||
236 | * @throws InvalidArgumentException |
||
237 | */ |
||
238 | 8 | public function verify(): bool |
|
351 | } |
||
352 | |||
353 | /** |
||
354 | * Create an empty walk |
||
355 | * |
||
356 | * @param GraphInterface $graph - the graph |
||
357 | * |
||
358 | * @return GraphPathInterface |
||
359 | */ |
||
360 | 2 | public static function emptyWalk(GraphInterface $graph): GraphPathInterface |
|
361 | { |
||
362 | 2 | return new GraphWalk($graph, null, null, [], [], 0.0); |
|
363 | } |
||
364 | |||
365 | /** |
||
366 | * Create a walk consisting of one vertex |
||
367 | * |
||
368 | * @param GraphInterface $graph - the graph |
||
369 | * @param VertexInterface $vertex - the vertex |
||
370 | * @param float $weight - the weight |
||
371 | * |
||
372 | * @return GraphPathInterface |
||
373 | */ |
||
374 | 3 | public static function singletonWalk( |
|
375 | GraphInterface $graph, |
||
376 | VertexInterface $vertex, |
||
377 | ?float $weight = 0.0 |
||
378 | ): GraphPathInterface { |
||
379 | 3 | return new GraphWalk( |
|
380 | 3 | $graph, |
|
381 | 3 | $vertex, |
|
382 | 3 | $vertex, |
|
383 | 3 | [$vertex], |
|
384 | 3 | [], |
|
385 | 3 | $weight |
|
386 | ); |
||
387 | } |
||
388 | |||
389 | /** |
||
390 | * Get the path string representation |
||
391 | * |
||
392 | * @return string |
||
393 | */ |
||
394 | public function __toString(): string |
||
395 | { |
||
396 | if (!empty($this->vertexList)) { |
||
397 | return implode('', array_map(function ($vertex) { |
||
398 | return (string) $vertex; |
||
399 | }, $this->vertexList)); |
||
400 | } |
||
401 | |||
402 | if (!empty($this->edgeList)) { |
||
403 | return implode('', array_map(function ($edge) { |
||
404 | return (string) $edge; |
||
405 | }, $this->edgeList)); |
||
406 | } |
||
407 | |||
408 | return ''; |
||
409 | } |
||
410 | |||
411 | /** |
||
412 | * Check if two walks are equal |
||
413 | * |
||
414 | * @param GraphPathInterface $other - the other walk |
||
415 | */ |
||
416 | 4 | public function equals(GraphPathInterface $other): bool |
|
417 | { |
||
418 | 4 | if ($this->hash == $other->getHash()) { |
|
419 | return true; |
||
420 | } |
||
421 | |||
422 | 4 | if ($this->isEmpty() && $other->isEmpty()) { |
|
423 | return true; |
||
424 | } |
||
425 | |||
426 | 4 | if ($this->isEmpty()) { |
|
427 | return false; |
||
428 | } |
||
429 | |||
430 | if ( |
||
431 | 4 | !$this->startVertex->equals($other->getStartVertex()) |
|
432 | 4 | || !$this->endVertex->equals($other->getEndVertex()) |
|
433 | ) { |
||
434 | return false; |
||
435 | } |
||
436 | |||
437 | 4 | if (empty($this->edgeList) && !$other->getGraph()->getType()->isAllowingMultipleEdges()) { |
|
438 | 2 | return $this->vertexList == $other->getVertexList(); |
|
439 | } |
||
440 | 2 | return $this->edgeList == $other->getEdgeList(); |
|
441 | } |
||
442 | |||
443 | /** |
||
444 | * Get the walk hash |
||
445 | * |
||
446 | * @return string |
||
447 | */ |
||
448 | 4 | public function getHash(): string |
|
449 | { |
||
450 | 4 | return $this->hash; |
|
451 | } |
||
452 | |||
453 | /** |
||
454 | * Check if the path is empty |
||
455 | * |
||
456 | * @return bool |
||
457 | */ |
||
458 | 12 | public function isEmpty(): bool |
|
461 | } |
||
462 | } |
||
463 |