Complex classes like AbstractNode 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. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
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 AbstractNode, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
29 | abstract class AbstractNode implements NodeInterface |
||
30 | { |
||
31 | /** |
||
32 | * name max lenght. |
||
33 | */ |
||
34 | const MAX_NAME_LENGTH = 255; |
||
35 | |||
36 | /** |
||
37 | * Unique id. |
||
38 | * |
||
39 | * @var ObjectId |
||
40 | */ |
||
41 | protected $_id; |
||
42 | |||
43 | /** |
||
44 | * Node name. |
||
45 | * |
||
46 | * @var string |
||
47 | */ |
||
48 | protected $name = ''; |
||
49 | |||
50 | /** |
||
51 | * Owner. |
||
52 | * |
||
53 | * @var ObjectId |
||
54 | */ |
||
55 | protected $owner; |
||
56 | |||
57 | /** |
||
58 | * Mime. |
||
59 | * |
||
60 | * @var string |
||
61 | */ |
||
62 | protected $mime; |
||
63 | |||
64 | /** |
||
65 | * Meta attributes. |
||
66 | * |
||
67 | * @var array |
||
68 | */ |
||
69 | protected $meta = []; |
||
70 | |||
71 | /** |
||
72 | * Parent collection. |
||
73 | * |
||
74 | * @var ObjectId |
||
75 | */ |
||
76 | protected $parent; |
||
77 | |||
78 | /** |
||
79 | * Is file deleted. |
||
80 | * |
||
81 | * @var bool|UTCDateTime |
||
82 | */ |
||
83 | protected $deleted = false; |
||
84 | |||
85 | /** |
||
86 | * Is shared? |
||
87 | * |
||
88 | * @var bool |
||
89 | */ |
||
90 | protected $shared = false; |
||
91 | |||
92 | /** |
||
93 | * Destory at a certain time. |
||
94 | * |
||
95 | * @var UTCDateTime |
||
96 | */ |
||
97 | protected $destroy; |
||
98 | |||
99 | /** |
||
100 | * Changed timestamp. |
||
101 | * |
||
102 | * @var UTCDateTime |
||
103 | */ |
||
104 | protected $changed; |
||
105 | |||
106 | /** |
||
107 | * Created timestamp. |
||
108 | * |
||
109 | * @var UTCDateTime |
||
110 | */ |
||
111 | protected $created; |
||
112 | |||
113 | /** |
||
114 | * Point to antother node (Means this node is reference to $reference). |
||
115 | * |
||
116 | * @var ObjectId |
||
117 | */ |
||
118 | protected $reference; |
||
119 | |||
120 | /** |
||
121 | * Raw attributes before any processing or modifications. |
||
122 | * |
||
123 | * @var array |
||
124 | */ |
||
125 | protected $raw_attributes; |
||
126 | |||
127 | /** |
||
128 | * Readonly flag. |
||
129 | * |
||
130 | * @var bool |
||
131 | */ |
||
132 | protected $readonly = false; |
||
133 | |||
134 | /** |
||
135 | * App attributes. |
||
136 | * |
||
137 | * @var array |
||
138 | */ |
||
139 | protected $app = []; |
||
140 | |||
141 | /** |
||
142 | * Filesystem. |
||
143 | * |
||
144 | * @var Filesystem |
||
145 | */ |
||
146 | protected $_fs; |
||
147 | |||
148 | /** |
||
149 | * Database. |
||
150 | * |
||
151 | * @var Database |
||
152 | */ |
||
153 | protected $_db; |
||
154 | |||
155 | /** |
||
156 | * User. |
||
157 | * |
||
158 | * @var User |
||
159 | */ |
||
160 | protected $_user; |
||
161 | |||
162 | /** |
||
163 | * Logger. |
||
164 | * |
||
165 | * @var LoggerInterface |
||
166 | */ |
||
167 | protected $_logger; |
||
168 | |||
169 | /** |
||
170 | * Server. |
||
171 | * |
||
172 | * @var Server |
||
173 | */ |
||
174 | protected $_server; |
||
175 | |||
176 | /** |
||
177 | * Hook. |
||
178 | * |
||
179 | * @var Hook |
||
180 | */ |
||
181 | protected $_hook; |
||
182 | |||
183 | /** |
||
184 | * Acl. |
||
185 | * |
||
186 | * @var Acl |
||
187 | */ |
||
188 | protected $_acl; |
||
189 | |||
190 | /** |
||
191 | * Mount. |
||
192 | * |
||
193 | * @var ObjectId |
||
194 | */ |
||
195 | protected $storage_reference; |
||
196 | |||
197 | /** |
||
198 | * Storage attributes. |
||
199 | * |
||
200 | * @var array |
||
201 | */ |
||
202 | protected $storage; |
||
203 | |||
204 | /** |
||
205 | * File size for files, number of children for directories. |
||
206 | * |
||
207 | * @var int |
||
208 | */ |
||
209 | protected $size = 0; |
||
210 | |||
211 | /** |
||
212 | * Acl. |
||
213 | * |
||
214 | * @var array |
||
215 | */ |
||
216 | protected $acl = []; |
||
217 | |||
218 | /** |
||
219 | * Mount. |
||
220 | * |
||
221 | * @var array |
||
222 | */ |
||
223 | protected $mount = []; |
||
224 | |||
225 | /** |
||
226 | * Lock. |
||
227 | * |
||
228 | * @var array |
||
229 | */ |
||
230 | protected $lock; |
||
231 | |||
232 | /** |
||
233 | * Parent collection. |
||
234 | * |
||
235 | * @var Collection |
||
236 | */ |
||
237 | protected $_parent; |
||
238 | |||
239 | /** |
||
240 | * Convert to filename. |
||
241 | * |
||
242 | * @return string |
||
243 | */ |
||
244 | public function __toString() |
||
248 | |||
249 | /** |
||
250 | * Get owner. |
||
251 | */ |
||
252 | public function getOwner(): ObjectId |
||
256 | |||
257 | /** |
||
258 | * Set filesystem. |
||
259 | */ |
||
260 | public function setFilesystem(Filesystem $fs): NodeInterface |
||
267 | |||
268 | /** |
||
269 | * Get filesystem. |
||
270 | */ |
||
271 | public function getFilesystem(): Filesystem |
||
275 | |||
276 | /** |
||
277 | * Check if $node is a sub node of any parent nodes of this node. |
||
278 | */ |
||
279 | public function isSubNode(NodeInterface $node): bool |
||
297 | |||
298 | /** |
||
299 | * Move node. |
||
300 | */ |
||
301 | public function setParent(Collection $parent, int $conflict = NodeInterface::CONFLICT_NOACTION): NodeInterface |
||
383 | |||
384 | /** |
||
385 | * Lock file. |
||
386 | */ |
||
387 | public function lock(string $identifier, ?int $ttl = 1800): NodeInterface |
||
400 | |||
401 | /** |
||
402 | * Get lock. |
||
403 | */ |
||
404 | public function getLock(): array |
||
412 | |||
413 | /** |
||
414 | * Is locked? |
||
415 | */ |
||
416 | public function isLocked(): bool |
||
427 | |||
428 | /** |
||
429 | * Unlock. |
||
430 | */ |
||
431 | public function unlock(?string $identifier = null): NodeInterface |
||
450 | |||
451 | /** |
||
452 | * Set node acl. |
||
453 | */ |
||
454 | public function setAcl(array $acl): NodeInterface |
||
473 | |||
474 | /** |
||
475 | * Get ACL. |
||
476 | */ |
||
477 | public function getAcl(): array |
||
487 | |||
488 | /** |
||
489 | * Get share id. |
||
490 | */ |
||
491 | public function getShareId(bool $reference = false): ?ObjectId |
||
511 | |||
512 | /** |
||
513 | * Get reference. |
||
514 | */ |
||
515 | public function getReference(): ?ObjectId |
||
519 | |||
520 | /** |
||
521 | * Get share node. |
||
522 | */ |
||
523 | public function getShareNode(): ?Collection |
||
535 | |||
536 | /** |
||
537 | * Is node marked as readonly? |
||
538 | */ |
||
539 | public function isReadonly(): bool |
||
543 | |||
544 | /** |
||
545 | * May write. |
||
546 | */ |
||
547 | public function mayWrite(): bool |
||
551 | |||
552 | /** |
||
553 | * Request is from node owner? |
||
554 | */ |
||
555 | public function isOwnerRequest(): bool |
||
559 | |||
560 | /** |
||
561 | * Check if node is kind of special. |
||
562 | */ |
||
563 | public function isSpecial(): bool |
||
577 | |||
578 | /** |
||
579 | * Check if node is a sub node of a share. |
||
580 | */ |
||
581 | public function isShareMember(): bool |
||
585 | |||
586 | /** |
||
587 | * Check if node is a sub node of an external storage mount. |
||
588 | */ |
||
589 | public function isMountMember(): bool |
||
593 | |||
594 | /** |
||
595 | * Is share. |
||
596 | */ |
||
597 | public function isShare(): bool |
||
601 | |||
602 | /** |
||
603 | * Is share (Reference or master share). |
||
604 | */ |
||
605 | public function isShared(): bool |
||
613 | |||
614 | /** |
||
615 | * Set the name. |
||
616 | */ |
||
617 | public function setName($name): bool |
||
642 | |||
643 | /** |
||
644 | * Check name. |
||
645 | */ |
||
646 | public function checkName(string $name): string |
||
661 | |||
662 | /** |
||
663 | * Get the name. |
||
664 | */ |
||
665 | public function getName(): string |
||
669 | |||
670 | /** |
||
671 | * Get mount node. |
||
672 | */ |
||
673 | public function getMount(): ?ObjectId |
||
677 | |||
678 | /** |
||
679 | * Undelete. |
||
680 | */ |
||
681 | public function undelete(int $conflict = NodeInterface::CONFLICT_NOACTION, ?string $recursion = null, bool $recursion_first = true): bool |
||
740 | |||
741 | /** |
||
742 | * Is node deleted? |
||
743 | */ |
||
744 | public function isDeleted(): bool |
||
748 | |||
749 | /** |
||
750 | * Get last modified timestamp. |
||
751 | */ |
||
752 | public function getLastModified(): int |
||
760 | |||
761 | /** |
||
762 | * Get unique id. |
||
763 | */ |
||
764 | 1 | public function getId(): ?ObjectId |
|
768 | |||
769 | /** |
||
770 | * Get parent. |
||
771 | */ |
||
772 | public function getParent(): ?Collection |
||
776 | |||
777 | /** |
||
778 | * Get parents. |
||
779 | */ |
||
780 | public function getParents(?NodeInterface $node = null, array $parents = []): array |
||
794 | |||
795 | /** |
||
796 | * Get as zip. |
||
797 | */ |
||
798 | public function getZip(): void |
||
805 | |||
806 | /** |
||
807 | * Create zip. |
||
808 | */ |
||
809 | public function zip(ZipStream $archive, bool $self = true, ?NodeInterface $parent = null, string $path = '', int $depth = 0): bool |
||
854 | |||
855 | /** |
||
856 | * Get mime type. |
||
857 | */ |
||
858 | 1 | public function getContentType(): string |
|
862 | |||
863 | /** |
||
864 | * Is reference. |
||
865 | */ |
||
866 | public function isReference(): bool |
||
870 | |||
871 | /** |
||
872 | * Set app attributes. |
||
873 | */ |
||
874 | public function setAppAttributes(string $namespace, array $attributes): NodeInterface |
||
881 | |||
882 | /** |
||
883 | * Set app attribute. |
||
884 | */ |
||
885 | public function setAppAttribute(string $namespace, string $attribute, $value): NodeInterface |
||
896 | |||
897 | /** |
||
898 | * Remove app attribute. |
||
899 | */ |
||
900 | public function unsetAppAttributes(string $namespace): NodeInterface |
||
909 | |||
910 | /** |
||
911 | * Remove app attribute. |
||
912 | */ |
||
913 | public function unsetAppAttribute(string $namespace, string $attribute): NodeInterface |
||
922 | |||
923 | /** |
||
924 | * Get app attribute. |
||
925 | */ |
||
926 | public function getAppAttribute(string $namespace, string $attribute) |
||
934 | |||
935 | /** |
||
936 | * Get app attributes. |
||
937 | */ |
||
938 | public function getAppAttributes(string $namespace): array |
||
946 | |||
947 | /** |
||
948 | * Set meta attributes. |
||
949 | */ |
||
950 | public function setMetaAttributes(array $attributes): NodeInterface |
||
965 | |||
966 | /** |
||
967 | * Get meta attributes as array. |
||
968 | */ |
||
969 | public function getMetaAttributes(array $attributes = []): array |
||
978 | |||
979 | /** |
||
980 | * Mark node as readonly. |
||
981 | */ |
||
982 | public function setReadonly(bool $readonly = true): bool |
||
989 | |||
990 | /** |
||
991 | * Mark node as self-destroyable. |
||
992 | */ |
||
993 | public function setDestroyable(?UTCDateTime $ts): bool |
||
1003 | |||
1004 | /** |
||
1005 | * Get original raw attributes before any processing. |
||
1006 | */ |
||
1007 | public function getRawAttributes(): array |
||
1011 | |||
1012 | /** |
||
1013 | * Check if node is in root. |
||
1014 | */ |
||
1015 | public function isInRoot(): bool |
||
1019 | |||
1020 | /** |
||
1021 | * Check if node is an instance of the actual root collection. |
||
1022 | */ |
||
1023 | public function isRoot(): bool |
||
1027 | |||
1028 | /** |
||
1029 | * Resolve node path. |
||
1030 | */ |
||
1031 | public function getPath(): string |
||
1042 | |||
1043 | /** |
||
1044 | * Save node attributes. |
||
1045 | * |
||
1046 | * @param array|string $attributes |
||
1047 | * @param array|string $remove |
||
1048 | * @param string $recursion |
||
1049 | */ |
||
1050 | public function save($attributes = [], $remove = [], ?string $recursion = null, bool $recursion_first = true): bool |
||
1114 | |||
1115 | /** |
||
1116 | * Duplicate name with a uniqid within name. |
||
1117 | */ |
||
1118 | public function getDuplicateName(?string $name = null, ?string $class = null): string |
||
1141 | |||
1142 | /** |
||
1143 | * Prepare lock. |
||
1144 | */ |
||
1145 | protected function prepareLock(string $identifier, int $ttl = 1800): array |
||
1154 | |||
1155 | /** |
||
1156 | * Get array value via string path. |
||
1157 | */ |
||
1158 | protected function getArrayValue(iterable $array, string $path, string $separator = '.') |
||
1175 | |||
1176 | /** |
||
1177 | * Validate meta attributes. |
||
1178 | */ |
||
1179 | protected function validateMetaAttributes(array $attributes): array |
||
1198 | |||
1199 | /** |
||
1200 | * Completly remove node. |
||
1201 | */ |
||
1202 | abstract protected function _forceDelete(): bool; |
||
1203 | } |
||
1204 |
If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.
Let’s take a look at an example:
Our function
my_function
expects aPost
object, and outputs the author of the post. The base classPost
returns a simple string and outputting a simple string will work just fine. However, the child classBlogPost
which is a sub-type ofPost
instead decided to return anobject
, and is therefore violating the SOLID principles. If aBlogPost
were passed tomy_function
, PHP would not complain, but ultimately fail when executing thestrtoupper
call in its body.